X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Flink.c;h=94a6dda11c27414616b5da5c24fc9ac78633d5a4;hb=fd27e50d9f01023121aa5d01fb24d8e7dd39392a;hp=2b5a97f6fb5f33eeec43b9e4891eee23cb787683;hpb=beb302486c90af698101118d7f55162e71358d5a;p=gnulib.git diff --git a/lib/link.c b/lib/link.c index 2b5a97f6f..94a6dda11 100644 --- a/lib/link.c +++ b/lib/link.c @@ -1,6 +1,6 @@ /* Emulate link on platforms that lack it, namely native Windows platforms. - Copyright (C) 2009 Free Software Foundation, Inc. + Copyright (C) 2009-2013 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,41 +13,48 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program; if not, see . */ #include -#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ - -#define WIN32_LEAN_AND_MEAN #include -#include #include +#include +#include +#include + +#if !HAVE_LINK +# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + +# define WIN32_LEAN_AND_MEAN +# include /* CreateHardLink was introduced only in Windows 2000. */ typedef BOOL (WINAPI * CreateHardLinkFuncType) (LPCTSTR lpFileName, - LPCTSTR lpExistingFileName, - LPSECURITY_ATTRIBUTES lpSecurityAttributes); + LPCTSTR lpExistingFileName, + LPSECURITY_ATTRIBUTES lpSecurityAttributes); static CreateHardLinkFuncType CreateHardLinkFunc = NULL; static BOOL initialized = FALSE; static void initialize (void) { - HMODULE kernel32 = LoadLibrary ("kernel32.dll"); + HMODULE kernel32 = GetModuleHandle ("kernel32.dll"); if (kernel32 != NULL) { CreateHardLinkFunc = - (CreateHardLinkFuncType) GetProcAddress (kernel32, "CreateHardLinkA"); + (CreateHardLinkFuncType) GetProcAddress (kernel32, "CreateHardLinkA"); } initialized = TRUE; } int -link (const char *path1, const char *path2) +link (const char *file1, const char *file2) { + char *dir; + size_t len1 = strlen (file1); + size_t len2 = strlen (file2); if (!initialized) initialize (); if (CreateHardLinkFunc == NULL) @@ -56,54 +63,149 @@ link (const char *path1, const char *path2) errno = EPERM; return -1; } - if (CreateHardLinkFunc (path2, path1, NULL) == 0) + /* Reject trailing slashes on non-directories; mingw does not + support hard-linking directories. */ + if ((len1 && (file1[len1 - 1] == '/' || file1[len1 - 1] == '\\')) + || (len2 && (file2[len2 - 1] == '/' || file2[len2 - 1] == '\\'))) + { + struct stat st; + if (stat (file1, &st) == 0 && S_ISDIR (st.st_mode)) + errno = EPERM; + else + errno = ENOTDIR; + return -1; + } + /* CreateHardLink("b/.","a",NULL) creates file "b", so we must check + that dirname(file2) exists. */ + dir = strdup (file2); + if (!dir) + return -1; + { + struct stat st; + char *p = strchr (dir, '\0'); + while (dir < p && (*--p != '/' && *p != '\\')); + *p = '\0'; + if (p != dir && stat (dir, &st) == -1) + { + int saved_errno = errno; + free (dir); + errno = saved_errno; + return -1; + } + free (dir); + } + /* Now create the link. */ + if (CreateHardLinkFunc (file2, file1, NULL) == 0) { /* It is not documented which errors CreateHardLink() can produce. * The following conversions are based on tests on a Windows XP SP2 * system. */ DWORD err = GetLastError (); switch (err) - { - case ERROR_ACCESS_DENIED: - errno = EACCES; - break; - - case ERROR_INVALID_FUNCTION: /* fs does not support hard links */ - errno = EPERM; - break; - - case ERROR_NOT_SAME_DEVICE: - errno = EXDEV; - break; - - case ERROR_PATH_NOT_FOUND: - case ERROR_FILE_NOT_FOUND: - errno = ENOENT; - break; - - case ERROR_INVALID_PARAMETER: - errno = ENAMETOOLONG; - break; - - case ERROR_TOO_MANY_LINKS: - errno = EMLINK; - break; - - case ERROR_ALREADY_EXISTS: - errno = EEXIST; - break; - - default: - errno = EIO; - } + { + case ERROR_ACCESS_DENIED: + errno = EACCES; + break; + + case ERROR_INVALID_FUNCTION: /* fs does not support hard links */ + errno = EPERM; + break; + + case ERROR_NOT_SAME_DEVICE: + errno = EXDEV; + break; + + case ERROR_PATH_NOT_FOUND: + case ERROR_FILE_NOT_FOUND: + errno = ENOENT; + break; + + case ERROR_INVALID_PARAMETER: + errno = ENAMETOOLONG; + break; + + case ERROR_TOO_MANY_LINKS: + errno = EMLINK; + break; + + case ERROR_ALREADY_EXISTS: + errno = EEXIST; + break; + + default: + errno = EIO; + } return -1; } return 0; } -#else /* !Windows */ +# else /* !Windows */ + +# error "This platform lacks a link function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib." + +# endif /* !Windows */ +#else /* HAVE_LINK */ + +# undef link + +/* Create a hard link from FILE1 to FILE2, working around platform bugs. */ +int +rpl_link (char const *file1, char const *file2) +{ + size_t len1; + size_t len2; + struct stat st; -#error "This platform lacks a link function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib." + /* Don't allow IRIX to dereference dangling file2 symlink. */ + if (!lstat (file2, &st)) + { + errno = EEXIST; + return -1; + } -#endif /* !Windows */ + /* Reject trailing slashes on non-directories. */ + len1 = strlen (file1); + len2 = strlen (file2); + if ((len1 && file1[len1 - 1] == '/') + || (len2 && file2[len2 - 1] == '/')) + { + /* Let link() decide whether hard-linking directories is legal. + If stat() fails, then link() should fail for the same reason + (although on Solaris 9, link("file/","oops") mistakenly + succeeds); if stat() succeeds, require a directory. */ + if (stat (file1, &st)) + return -1; + if (!S_ISDIR (st.st_mode)) + { + errno = ENOTDIR; + return -1; + } + } + else + { + /* Fix Cygwin 1.5.x bug where link("a","b/.") creates file "b". */ + char *dir = strdup (file2); + char *p; + if (!dir) + return -1; + /* We already know file2 does not end in slash. Strip off the + basename, then check that the dirname exists. */ + p = strrchr (dir, '/'); + if (p) + { + *p = '\0'; + if (stat (dir, &st) == -1) + { + int saved_errno = errno; + free (dir); + errno = saved_errno; + return -1; + } + } + free (dir); + } + return link (file1, file2); +} +#endif /* HAVE_LINK */