linkat: avoid compilation failure
[pspp] / lib / linkat.c
1 /* Create a hard link relative to open directories.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* written by Eric Blake */
18
19 #include <config.h>
20
21 #include <unistd.h>
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30
31 #include "areadlink.h"
32 #include "dirname.h"
33 #include "filenamecat.h"
34 #include "openat-priv.h"
35
36 #if HAVE_SYS_PARAM_H
37 # include <sys/param.h>
38 #endif
39 #ifndef MAXSYMLINKS
40 # ifdef SYMLOOP_MAX
41 #  define MAXSYMLINKS SYMLOOP_MAX
42 # else
43 #  define MAXSYMLINKS 20
44 # endif
45 #endif
46
47 #if !HAVE_LINKAT
48
49 /* Create a link.  If FILE1 is a symlink, either create a hardlink to
50    that symlink, or fake it by creating an identical symlink.  */
51 # if LINK_FOLLOWS_SYMLINKS == 0
52 #  define link_immediate link
53 # else
54 static int
55 link_immediate (char const *file1, char const *file2)
56 {
57   char *target = areadlink (file1);
58   if (target)
59     {
60       /* A symlink cannot be modified in-place.  Therefore, creating
61          an identical symlink behaves like a hard link to a symlink,
62          except for incorrect st_ino and st_nlink.  However, we must
63          be careful of EXDEV.  */
64       struct stat st1;
65       struct stat st2;
66       char *dir = mdir_name (file2);
67       if (!dir)
68         {
69           free (target);
70           errno = ENOMEM;
71           return -1;
72         }
73       if (lstat (file1, &st1) == 0 && stat (dir, &st2) == 0)
74         {
75           if (st1.st_dev == st2.st_dev)
76             {
77               int result = symlink (target, file2);
78               int saved_errno = errno;
79               free (target);
80               free (dir);
81               errno = saved_errno;
82               return result;
83             }
84           free (target);
85           free (dir);
86           errno = EXDEV;
87           return -1;
88         }
89       free (target);
90       free (dir);
91     }
92   if (errno == ENOMEM)
93     return -1;
94   return link (file1, file2);
95 }
96 # endif /* LINK_FOLLOWS_SYMLINKS == 0 */
97
98 /* Create a link.  If FILE1 is a symlink, create a hardlink to the
99    canonicalized file.  */
100 # if 0 < LINK_FOLLOWS_SYMLINKS
101 #  define link_follow link
102 # else
103 static int
104 link_follow (char const *file1, char const *file2)
105 {
106   char *name = (char *) file1;
107   char *target;
108   int result;
109   int i = MAXSYMLINKS;
110
111   /* Using realpath or canonicalize_file_name is too heavy-handed: we
112      don't need an absolute name, and we don't need to resolve
113      intermediate symlinks, just the basename of each iteration.  */
114   while (i-- && (target = areadlink (name)))
115     {
116       if (IS_ABSOLUTE_FILE_NAME (target))
117         {
118           if (name != file1)
119             free (name);
120           name = target;
121         }
122       else
123         {
124           char *dir = mdir_name (name);
125           if (name != file1)
126             free (name);
127           if (!dir)
128             {
129               free (target);
130               errno = ENOMEM;
131               return -1;
132             }
133           name = mfile_name_concat (dir, target, NULL);
134           free (dir);
135           free (target);
136           if (!name)
137             {
138               errno = ENOMEM;
139               return -1;
140             }
141         }
142     }
143   if (i < 0)
144     {
145       target = NULL;
146       errno = ELOOP;
147     }
148   if (!target && errno != EINVAL)
149     {
150       if (name != file1)
151         {
152           int saved_errno = errno;
153           free (name);
154           errno = saved_errno;
155         }
156       return -1;
157     }
158   result = link (name, file2);
159   if (name != file1)
160     {
161       int saved_errno = errno;
162       free (name);
163       errno = saved_errno;
164     }
165   return result;
166 }
167 # endif /* 0 < LINK_FOLLOWS_SYMLINKS */
168
169 /* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2,
170    in the directory open on descriptor FD2.  If FILE1 is a symlink, FLAG
171    controls whether to dereference FILE1 first.  If possible, do it without
172    changing the working directory.  Otherwise, resort to using
173    save_cwd/fchdir, then rename/restore_cwd.  If either the save_cwd or
174    the restore_cwd fails, then give a diagnostic and exit nonzero.  */
175
176 int
177 linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
178 {
179   if (flag & ~AT_SYMLINK_FOLLOW)
180     {
181       errno = EINVAL;
182       return -1;
183     }
184   return at_func2 (fd1, file1, fd2, file2,
185                    flag ? link_follow : link_immediate);
186 }
187
188 #else /* HAVE_LINKAT */
189
190 # undef linkat
191
192 /* Read a symlink, like areadlink, but relative to FD.  */
193
194 static char *
195 areadlinkat (int fd, char const *filename)
196 {
197   /* The initial buffer size for the link value.  A power of 2
198      detects arithmetic overflow earlier, but is not required.  */
199 # define INITIAL_BUF_SIZE 1024
200
201   /* Allocate the initial buffer on the stack.  This way, in the common
202      case of a symlink of small size, we get away with a single small malloc()
203      instead of a big malloc() followed by a shrinking realloc().  */
204   char initial_buf[INITIAL_BUF_SIZE];
205
206   char *buffer = initial_buf;
207   size_t buf_size = sizeof (initial_buf);
208
209   while (1)
210     {
211       /* Attempt to read the link into the current buffer.  */
212       ssize_t link_length = readlinkat (fd, filename, buffer, buf_size);
213
214       /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1
215          with errno == ERANGE if the buffer is too small.  */
216       if (link_length < 0 && errno != ERANGE)
217         {
218           if (buffer != initial_buf)
219             {
220               int saved_errno = errno;
221               free (buffer);
222               errno = saved_errno;
223             }
224           return NULL;
225         }
226
227       if ((size_t) link_length < buf_size)
228         {
229           buffer[link_length++] = '\0';
230
231           /* Return it in a chunk of memory as small as possible.  */
232           if (buffer == initial_buf)
233             {
234               buffer = (char *) malloc (link_length);
235               if (buffer == NULL)
236                 /* errno is ENOMEM.  */
237                 return NULL;
238               memcpy (buffer, initial_buf, link_length);
239             }
240           else
241             {
242               /* Shrink buffer before returning it.  */
243               if ((size_t) link_length < buf_size)
244                 {
245                   char *smaller_buffer = (char *) realloc (buffer, link_length);
246
247                   if (smaller_buffer != NULL)
248                     buffer = smaller_buffer;
249                 }
250             }
251           return buffer;
252         }
253
254       if (buffer != initial_buf)
255         free (buffer);
256       buf_size *= 2;
257       if (SSIZE_MAX < buf_size || (SIZE_MAX / 2 < SSIZE_MAX && buf_size == 0))
258         {
259           errno = ENOMEM;
260           return NULL;
261         }
262       buffer = (char *) malloc (buf_size);
263       if (buffer == NULL)
264         /* errno is ENOMEM.  */
265         return NULL;
266     }
267 }
268
269 /* Create a link.  If FILE1 is a symlink, create a hardlink to the
270    canonicalized file.  */
271
272 static int
273 linkat_follow (int fd1, char const *file1, int fd2, char const *file2)
274 {
275   char *name = (char *) file1;
276   char *target;
277   int result;
278   int i = MAXSYMLINKS;
279
280   /* There is no realpathat.  */
281   while (i-- && (target = areadlinkat (fd1, name)))
282     {
283       if (IS_ABSOLUTE_FILE_NAME (target))
284         {
285           if (name != file1)
286             free (name);
287           name = target;
288         }
289       else
290         {
291           char *dir = mdir_name (name);
292           if (name != file1)
293             free (name);
294           if (!dir)
295             {
296               free (target);
297               errno = ENOMEM;
298               return -1;
299             }
300           name = mfile_name_concat (dir, target, NULL);
301           free (dir);
302           free (target);
303           if (!name)
304             {
305               errno = ENOMEM;
306               return -1;
307             }
308         }
309     }
310   if (i < 0)
311     {
312       target = NULL;
313       errno = ELOOP;
314     }
315   if (!target && errno != EINVAL)
316     {
317       if (name != file1)
318         {
319           int saved_errno = errno;
320           free (name);
321           errno = saved_errno;
322         }
323       return -1;
324     }
325   result = linkat (fd1, name, fd2, file2, 0);
326   if (name != file1)
327     {
328       int saved_errno = errno;
329       free (name);
330       errno = saved_errno;
331     }
332   return result;
333 }
334
335
336 /* Like linkat, but guarantee that AT_SYMLINK_FOLLOW works even on
337    older Linux kernels.  */
338
339 int
340 rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
341 {
342   if (!flag)
343     return linkat (fd1, file1, fd2, file2, flag);
344   if (flag & ~AT_SYMLINK_FOLLOW)
345     {
346       errno = EINVAL;
347       return -1;
348     }
349
350   /* Cache the information on whether the system call really works.  */
351   {
352     static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */
353     if (0 <= have_follow_really)
354     {
355       int result = linkat (fd1, file1, fd2, file2, flag);
356       if (!(result == -1 && errno == EINVAL))
357         {
358           have_follow_really = 1;
359           return result;
360         }
361       have_follow_really = -1;
362     }
363   }
364   return linkat_follow (fd1, file1, fd2, file2);
365 }
366
367 #endif /* HAVE_LINKAT */