f715846be0e63a459b03d10b2a9d75dde3365def
[debian/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-2013 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, see <http://www.gnu.org/licenses/>.  */
19
20 #include <config.h>
21
22 #include <unistd.h>
23
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28
29 #if !HAVE_LINK
30 # if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
31
32 #  define WIN32_LEAN_AND_MEAN
33 #  include <windows.h>
34
35 /* CreateHardLink was introduced only in Windows 2000.  */
36 typedef BOOL (WINAPI * CreateHardLinkFuncType) (LPCTSTR lpFileName,
37                                                 LPCTSTR lpExistingFileName,
38                                                 LPSECURITY_ATTRIBUTES lpSecurityAttributes);
39 static CreateHardLinkFuncType CreateHardLinkFunc = NULL;
40 static BOOL initialized = FALSE;
41
42 static void
43 initialize (void)
44 {
45   HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
46   if (kernel32 != NULL)
47     {
48       CreateHardLinkFunc =
49         (CreateHardLinkFuncType) GetProcAddress (kernel32, "CreateHardLinkA");
50     }
51   initialized = TRUE;
52 }
53
54 int
55 link (const char *file1, const char *file2)
56 {
57   char *dir;
58   size_t len1 = strlen (file1);
59   size_t len2 = strlen (file2);
60   if (!initialized)
61     initialize ();
62   if (CreateHardLinkFunc == NULL)
63     {
64       /* System does not support hard links.  */
65       errno = EPERM;
66       return -1;
67     }
68   /* Reject trailing slashes on non-directories; mingw does not
69      support hard-linking directories.  */
70   if ((len1 && (file1[len1 - 1] == '/' || file1[len1 - 1] == '\\'))
71       || (len2 && (file2[len2 - 1] == '/' || file2[len2 - 1] == '\\')))
72     {
73       struct stat st;
74       if (stat (file1, &st) == 0 && S_ISDIR (st.st_mode))
75         errno = EPERM;
76       else
77         errno = ENOTDIR;
78       return -1;
79     }
80   /* CreateHardLink("b/.","a",NULL) creates file "b", so we must check
81      that dirname(file2) exists.  */
82   dir = strdup (file2);
83   if (!dir)
84     return -1;
85   {
86     struct stat st;
87     char *p = strchr (dir, '\0');
88     while (dir < p && (*--p != '/' && *p != '\\'));
89     *p = '\0';
90     if (p != dir && stat (dir, &st) == -1)
91       {
92         int saved_errno = errno;
93         free (dir);
94         errno = saved_errno;
95         return -1;
96       }
97     free (dir);
98   }
99   /* Now create the link.  */
100   if (CreateHardLinkFunc (file2, file1, NULL) == 0)
101     {
102       /* It is not documented which errors CreateHardLink() can produce.
103        * The following conversions are based on tests on a Windows XP SP2
104        * system. */
105       DWORD err = GetLastError ();
106       switch (err)
107         {
108         case ERROR_ACCESS_DENIED:
109           errno = EACCES;
110           break;
111
112         case ERROR_INVALID_FUNCTION:    /* fs does not support hard links */
113           errno = EPERM;
114           break;
115
116         case ERROR_NOT_SAME_DEVICE:
117           errno = EXDEV;
118           break;
119
120         case ERROR_PATH_NOT_FOUND:
121         case ERROR_FILE_NOT_FOUND:
122           errno = ENOENT;
123           break;
124
125         case ERROR_INVALID_PARAMETER:
126           errno = ENAMETOOLONG;
127           break;
128
129         case ERROR_TOO_MANY_LINKS:
130           errno = EMLINK;
131           break;
132
133         case ERROR_ALREADY_EXISTS:
134           errno = EEXIST;
135           break;
136
137         default:
138           errno = EIO;
139         }
140       return -1;
141     }
142
143   return 0;
144 }
145
146 # else /* !Windows */
147
148 #  error "This platform lacks a link function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
149
150 # endif /* !Windows */
151 #else /* HAVE_LINK */
152
153 # undef link
154
155 /* Create a hard link from FILE1 to FILE2, working around platform bugs.  */
156 int
157 rpl_link (char const *file1, char const *file2)
158 {
159   size_t len1;
160   size_t len2;
161   struct stat st;
162
163   /* Don't allow IRIX to dereference dangling file2 symlink.  */
164   if (!lstat (file2, &st))
165     {
166       errno = EEXIST;
167       return -1;
168     }
169
170   /* Reject trailing slashes on non-directories.  */
171   len1 = strlen (file1);
172   len2 = strlen (file2);
173   if ((len1 && file1[len1 - 1] == '/')
174       || (len2 && file2[len2 - 1] == '/'))
175     {
176       /* Let link() decide whether hard-linking directories is legal.
177          If stat() fails, then link() should fail for the same reason
178          (although on Solaris 9, link("file/","oops") mistakenly
179          succeeds); if stat() succeeds, require a directory.  */
180       if (stat (file1, &st))
181         return -1;
182       if (!S_ISDIR (st.st_mode))
183         {
184           errno = ENOTDIR;
185           return -1;
186         }
187     }
188   else
189     {
190       /* Fix Cygwin 1.5.x bug where link("a","b/.") creates file "b".  */
191       char *dir = strdup (file2);
192       char *p;
193       if (!dir)
194         return -1;
195       /* We already know file2 does not end in slash.  Strip off the
196          basename, then check that the dirname exists.  */
197       p = strrchr (dir, '/');
198       if (p)
199         {
200           *p = '\0';
201           if (stat (dir, &st) == -1)
202             {
203               int saved_errno = errno;
204               free (dir);
205               errno = saved_errno;
206               return -1;
207             }
208         }
209       free (dir);
210     }
211   return link (file1, file2);
212 }
213 #endif /* HAVE_LINK */