]> git.cworth.org Git - tar/blob - gnu/linkat.c
Imported Upstream version 1.24
[tar] / gnu / linkat.c
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* Create a hard link relative to open directories.
4    Copyright (C) 2009-2010 Free Software Foundation, Inc.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 /* written by Eric Blake */
20
21 #include <config.h>
22
23 #include <unistd.h>
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32
33 #include "areadlink.h"
34 #include "dirname.h"
35 #include "filenamecat.h"
36 #include "openat-priv.h"
37
38 #if HAVE_SYS_PARAM_H
39 # include <sys/param.h>
40 #endif
41 #ifndef MAXSYMLINKS
42 # ifdef SYMLOOP_MAX
43 #  define MAXSYMLINKS SYMLOOP_MAX
44 # else
45 #  define MAXSYMLINKS 20
46 # endif
47 #endif
48
49 #if !HAVE_LINKAT
50
51 /* Create a link.  If FILE1 is a symlink, either create a hardlink to
52    that symlink, or fake it by creating an identical symlink.  */
53 # if LINK_FOLLOWS_SYMLINKS == 0
54 #  define link_immediate link
55 # else
56 static int
57 link_immediate (char const *file1, char const *file2)
58 {
59   char *target = areadlink (file1);
60   if (target)
61     {
62       /* A symlink cannot be modified in-place.  Therefore, creating
63          an identical symlink behaves like a hard link to a symlink,
64          except for incorrect st_ino and st_nlink.  However, we must
65          be careful of EXDEV.  */
66       struct stat st1;
67       struct stat st2;
68       char *dir = mdir_name (file2);
69       if (!dir)
70         {
71           free (target);
72           errno = ENOMEM;
73           return -1;
74         }
75       if (lstat (file1, &st1) == 0 && stat (dir, &st2) == 0)
76         {
77           if (st1.st_dev == st2.st_dev)
78             {
79               int result = symlink (target, file2);
80               int saved_errno = errno;
81               free (target);
82               free (dir);
83               errno = saved_errno;
84               return result;
85             }
86           free (target);
87           free (dir);
88           errno = EXDEV;
89           return -1;
90         }
91       free (target);
92       free (dir);
93     }
94   if (errno == ENOMEM)
95     return -1;
96   return link (file1, file2);
97 }
98 # endif /* LINK_FOLLOWS_SYMLINKS == 0 */
99
100 /* Create a link.  If FILE1 is a symlink, create a hardlink to the
101    canonicalized file.  */
102 # if 0 < LINK_FOLLOWS_SYMLINKS
103 #  define link_follow link
104 # else
105 static int
106 link_follow (char const *file1, char const *file2)
107 {
108   char *name = (char *) file1;
109   char *target;
110   int result;
111   int i = MAXSYMLINKS;
112
113   /* Using realpath or canonicalize_file_name is too heavy-handed: we
114      don't need an absolute name, and we don't need to resolve
115      intermediate symlinks, just the basename of each iteration.  */
116   while (i-- && (target = areadlink (name)))
117     {
118       if (IS_ABSOLUTE_FILE_NAME (target))
119         {
120           if (name != file1)
121             free (name);
122           name = target;
123         }
124       else
125         {
126           char *dir = mdir_name (name);
127           if (name != file1)
128             free (name);
129           if (!dir)
130             {
131               free (target);
132               errno = ENOMEM;
133               return -1;
134             }
135           name = mfile_name_concat (dir, target, NULL);
136           free (dir);
137           free (target);
138           if (!name)
139             {
140               errno = ENOMEM;
141               return -1;
142             }
143         }
144     }
145   if (i < 0)
146     {
147       target = NULL;
148       errno = ELOOP;
149     }
150   if (!target && errno != EINVAL)
151     {
152       if (name != file1)
153         {
154           int saved_errno = errno;
155           free (name);
156           errno = saved_errno;
157         }
158       return -1;
159     }
160   result = link (name, file2);
161   if (name != file1)
162     {
163       int saved_errno = errno;
164       free (name);
165       errno = saved_errno;
166     }
167   return result;
168 }
169 # endif /* 0 < LINK_FOLLOWS_SYMLINKS */
170
171 /* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2,
172    in the directory open on descriptor FD2.  If FILE1 is a symlink, FLAG
173    controls whether to dereference FILE1 first.  If possible, do it without
174    changing the working directory.  Otherwise, resort to using
175    save_cwd/fchdir, then rename/restore_cwd.  If either the save_cwd or
176    the restore_cwd fails, then give a diagnostic and exit nonzero.  */
177
178 int
179 linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
180 {
181   if (flag & ~AT_SYMLINK_FOLLOW)
182     {
183       errno = EINVAL;
184       return -1;
185     }
186   return at_func2 (fd1, file1, fd2, file2,
187                    flag ? link_follow : link_immediate);
188 }
189
190 #else /* HAVE_LINKAT */
191
192 # undef linkat
193
194 /* Create a link.  If FILE1 is a symlink, create a hardlink to the
195    canonicalized file.  */
196
197 static int
198 linkat_follow (int fd1, char const *file1, int fd2, char const *file2)
199 {
200   char *name = (char *) file1;
201   char *target;
202   int result;
203   int i = MAXSYMLINKS;
204
205   /* There is no realpathat.  */
206   while (i-- && (target = areadlinkat (fd1, name)))
207     {
208       if (IS_ABSOLUTE_FILE_NAME (target))
209         {
210           if (name != file1)
211             free (name);
212           name = target;
213         }
214       else
215         {
216           char *dir = mdir_name (name);
217           if (name != file1)
218             free (name);
219           if (!dir)
220             {
221               free (target);
222               errno = ENOMEM;
223               return -1;
224             }
225           name = mfile_name_concat (dir, target, NULL);
226           free (dir);
227           free (target);
228           if (!name)
229             {
230               errno = ENOMEM;
231               return -1;
232             }
233         }
234     }
235   if (i < 0)
236     {
237       target = NULL;
238       errno = ELOOP;
239     }
240   if (!target && errno != EINVAL)
241     {
242       if (name != file1)
243         {
244           int saved_errno = errno;
245           free (name);
246           errno = saved_errno;
247         }
248       return -1;
249     }
250   result = linkat (fd1, name, fd2, file2, 0);
251   if (name != file1)
252     {
253       int saved_errno = errno;
254       free (name);
255       errno = saved_errno;
256     }
257   return result;
258 }
259
260
261 /* Like linkat, but guarantee that AT_SYMLINK_FOLLOW works even on
262    older Linux kernels.  */
263
264 int
265 rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
266 {
267   if (flag & ~AT_SYMLINK_FOLLOW)
268     {
269       errno = EINVAL;
270       return -1;
271     }
272
273 #if LINKAT_TRAILING_SLASH_BUG
274   /* Reject trailing slashes on non-directories.  */
275   {
276     size_t len1 = strlen (file1);
277     size_t len2 = strlen (file2);
278     if ((len1 && file1[len1 - 1] == '/')
279         || (len2 && file2[len2 - 1] == '/'))
280       {
281         /* Let linkat() decide whether hard-linking directories is legal.
282            If fstatat() fails, then linkat() should fail for the same reason;
283            if fstatat() succeeds, require a directory.  */
284         struct stat st;
285         if (fstatat (fd1, file1, &st, flag ? 0 : AT_SYMLINK_NOFOLLOW))
286           return -1;
287         if (!S_ISDIR (st.st_mode))
288           {
289             errno = ENOTDIR;
290             return -1;
291           }
292       }
293   }
294 #endif
295
296   if (!flag)
297     return linkat (fd1, file1, fd2, file2, flag);
298
299   /* Cache the information on whether the system call really works.  */
300   {
301     static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */
302     if (0 <= have_follow_really)
303     {
304       int result = linkat (fd1, file1, fd2, file2, flag);
305       if (!(result == -1 && errno == EINVAL))
306         {
307           have_follow_really = 1;
308           return result;
309         }
310       have_follow_really = -1;
311     }
312   }
313   return linkat_follow (fd1, file1, fd2, file2);
314 }
315
316 #endif /* HAVE_LINKAT */