Mercurial > hg > octave-lojdl > gnulib-hg
diff lib/link.c @ 11981:7e5f9267c62e
link: fix platform bugs
* m4/link.m4 (gl_FUNC_LINK): Detect Solaris and Cygwin bugs.
* lib/link.c (link): Work around them. Fix related mingw bug.
* m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Add REPLACE_LINK.
* modules/unistd (Makefile.am): Substitute it.
* lib/unistd.in.h (link): Declare replacement.
* doc/posix-functions/link.texi (link): Document this.
* modules/link (Depends-on): Add strdup-posix, sys_stat.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Wed, 09 Sep 2009 15:25:26 -0600 |
parents | 2921cdaa1164 |
children | 00e8f52bdef4 |
line wrap: on
line diff
--- a/lib/link.c +++ b/lib/link.c @@ -18,13 +18,18 @@ #include <config.h> -#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ - -#define WIN32_LEAN_AND_MEAN #include <unistd.h> -#include <windows.h> #include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#if !HAVE_LINK +# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + +# define WIN32_LEAN_AND_MEAN +# include <windows.h> /* CreateHardLink was introduced only in Windows 2000. */ typedef BOOL (WINAPI * CreateHardLinkFuncType) (LPCTSTR lpFileName, @@ -46,8 +51,11 @@ } 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,7 +64,39 @@ 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 @@ -102,8 +142,60 @@ 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 -#error "This platform lacks a link function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib." - -#endif /* !Windows */ +/* Create a hard link from FILE1 to FILE2, working around platform bugs. */ +int +rpl_link (char const *file1, char const *file2) +{ + /* Reject trailing slashes on non-directories. */ + size_t len1 = strlen (file1); + size_t len2 = strlen (file2); + if ((len1 && file1[len1 - 1] == '/') + || (len2 && file2[len2 - 1] == '/')) + { + /* Let link() decide whether hard-linking directories is legal. + If stat() fails, link() will probably fail for the same + reason; so we only have to worry about successful stat() and + non-directory. */ + struct stat st; + if (stat (file1, &st) == 0 && !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); + struct stat st; + 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 */