]> git.cworth.org Git - tar/blob - gnu/link.c
Imported Upstream version 1.24
[tar] / gnu / link.c
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* Emulate link on platforms that lack it, namely native Windows platforms.
4
5    Copyright (C) 2009, 2010 Free Software Foundation, Inc.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3, or (at your option)
10    any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software Foundation,
19    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
20
21 #include <config.h>
22
23 #include <unistd.h>
24
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29
30 #if !HAVE_LINK
31 # if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
32
33 #  define WIN32_LEAN_AND_MEAN
34 #  include <windows.h>
35
36 /* CreateHardLink was introduced only in Windows 2000.  */
37 typedef BOOL (WINAPI * CreateHardLinkFuncType) (LPCTSTR lpFileName,
38                                                 LPCTSTR lpExistingFileName,
39                                                 LPSECURITY_ATTRIBUTES lpSecurityAttributes);
40 static CreateHardLinkFuncType CreateHardLinkFunc = NULL;
41 static BOOL initialized = FALSE;
42
43 static void
44 initialize (void)
45 {
46   HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
47   if (kernel32 != NULL)
48     {
49       CreateHardLinkFunc =
50         (CreateHardLinkFuncType) GetProcAddress (kernel32, "CreateHardLinkA");
51     }
52   initialized = TRUE;
53 }
54
55 int
56 link (const char *file1, const char *file2)
57 {
58   char *dir;
59   size_t len1 = strlen (file1);
60   size_t len2 = strlen (file2);
61   if (!initialized)
62     initialize ();
63   if (CreateHardLinkFunc == NULL)
64     {
65       /* System does not support hard links.  */
66       errno = EPERM;
67       return -1;
68     }
69   /* Reject trailing slashes on non-directories; mingw does not
70      support hard-linking directories.  */
71   if ((len1 && (file1[len1 - 1] == '/' || file1[len1 - 1] == '\\'))
72       || (len2 && (file2[len2 - 1] == '/' || file2[len2 - 1] == '\\')))
73     {
74       struct stat st;
75       if (stat (file1, &st) == 0 && S_ISDIR (st.st_mode))
76         errno = EPERM;
77       else
78         errno = ENOTDIR;
79       return -1;
80     }
81   /* CreateHardLink("b/.","a",NULL) creates file "b", so we must check
82      that dirname(file2) exists.  */
83   dir = strdup (file2);
84   if (!dir)
85     return -1;
86   {
87     struct stat st;
88     char *p = strchr (dir, '\0');
89     while (dir < p && (*--p != '/' && *p != '\\'));
90     *p = '\0';
91     if (p != dir && stat (dir, &st) == -1)
92       {
93         int saved_errno = errno;
94         free (dir);
95         errno = saved_errno;
96         return -1;
97       }
98     free (dir);
99   }
100   /* Now create the link.  */
101   if (CreateHardLinkFunc (file2, file1, NULL) == 0)
102     {
103       /* It is not documented which errors CreateHardLink() can produce.
104        * The following conversions are based on tests on a Windows XP SP2
105        * system. */
106       DWORD err = GetLastError ();
107       switch (err)
108         {
109         case ERROR_ACCESS_DENIED:
110           errno = EACCES;
111           break;
112
113         case ERROR_INVALID_FUNCTION:    /* fs does not support hard links */
114           errno = EPERM;
115           break;
116
117         case ERROR_NOT_SAME_DEVICE:
118           errno = EXDEV;
119           break;
120
121         case ERROR_PATH_NOT_FOUND:
122         case ERROR_FILE_NOT_FOUND:
123           errno = ENOENT;
124           break;
125
126         case ERROR_INVALID_PARAMETER:
127           errno = ENAMETOOLONG;
128           break;
129
130         case ERROR_TOO_MANY_LINKS:
131           errno = EMLINK;
132           break;
133
134         case ERROR_ALREADY_EXISTS:
135           errno = EEXIST;
136           break;
137
138         default:
139           errno = EIO;
140         }
141       return -1;
142     }
143
144   return 0;
145 }
146
147 # else /* !Windows */
148
149 #  error "This platform lacks a link function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
150
151 # endif /* !Windows */
152 #else /* HAVE_LINK */
153
154 # undef link
155
156 /* Create a hard link from FILE1 to FILE2, working around platform bugs.  */
157 int
158 rpl_link (char const *file1, char const *file2)
159 {
160   /* Reject trailing slashes on non-directories.  */
161   size_t len1 = strlen (file1);
162   size_t len2 = strlen (file2);
163   if ((len1 && file1[len1 - 1] == '/')
164       || (len2 && file2[len2 - 1] == '/'))
165     {
166       /* Let link() decide whether hard-linking directories is legal.
167          If stat() fails, then link() should fail for the same reason
168          (although on Solaris 9, link("file/","oops") mistakenly
169          succeeds); if stat() succeeds, require a directory.  */
170       struct stat st;
171       if (stat (file1, &st))
172         return -1;
173       if (!S_ISDIR (st.st_mode))
174         {
175           errno = ENOTDIR;
176           return -1;
177         }
178     }
179   else
180     {
181       /* Fix Cygwin 1.5.x bug where link("a","b/.") creates file "b".  */
182       char *dir = strdup (file2);
183       struct stat st;
184       char *p;
185       if (!dir)
186         return -1;
187       /* We already know file2 does not end in slash.  Strip off the
188          basename, then check that the dirname exists.  */
189       p = strrchr (dir, '/');
190       if (p)
191         {
192           *p = '\0';
193           if (stat (dir, &st) == -1)
194             {
195               int saved_errno = errno;
196               free (dir);
197               errno = saved_errno;
198               return -1;
199             }
200         }
201       free (dir);
202     }
203   return link (file1, file2);
204 }
205 #endif /* HAVE_LINK */