1/* Create a hard link relative to open directories.
2   Copyright (C) 2009-2010 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
54static int
55link_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
103static int
104link_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
176int
177linkat (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/* Create a link.  If FILE1 is a symlink, create a hardlink to the
193   canonicalized file.  */
194
195static int
196linkat_follow (int fd1, char const *file1, int fd2, char const *file2)
197{
198  char *name = (char *) file1;
199  char *target;
200  int result;
201  int i = MAXSYMLINKS;
202
203  /* There is no realpathat.  */
204  while (i-- && (target = areadlinkat (fd1, name)))
205    {
206      if (IS_ABSOLUTE_FILE_NAME (target))
207        {
208          if (name != file1)
209            free (name);
210          name = target;
211        }
212      else
213        {
214          char *dir = mdir_name (name);
215          if (name != file1)
216            free (name);
217          if (!dir)
218            {
219              free (target);
220              errno = ENOMEM;
221              return -1;
222            }
223          name = mfile_name_concat (dir, target, NULL);
224          free (dir);
225          free (target);
226          if (!name)
227            {
228              errno = ENOMEM;
229              return -1;
230            }
231        }
232    }
233  if (i < 0)
234    {
235      target = NULL;
236      errno = ELOOP;
237    }
238  if (!target && errno != EINVAL)
239    {
240      if (name != file1)
241        {
242          int saved_errno = errno;
243          free (name);
244          errno = saved_errno;
245        }
246      return -1;
247    }
248  result = linkat (fd1, name, fd2, file2, 0);
249  if (name != file1)
250    {
251      int saved_errno = errno;
252      free (name);
253      errno = saved_errno;
254    }
255  return result;
256}
257
258
259/* Like linkat, but guarantee that AT_SYMLINK_FOLLOW works even on
260   older Linux kernels.  */
261
262int
263rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
264{
265  if (!flag)
266    return linkat (fd1, file1, fd2, file2, flag);
267  if (flag & ~AT_SYMLINK_FOLLOW)
268    {
269      errno = EINVAL;
270      return -1;
271    }
272
273  /* Cache the information on whether the system call really works.  */
274  {
275    static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */
276    if (0 <= have_follow_really)
277    {
278      int result = linkat (fd1, file1, fd2, file2, flag);
279      if (!(result == -1 && errno == EINVAL))
280        {
281          have_follow_really = 1;
282          return result;
283        }
284      have_follow_really = -1;
285    }
286  }
287  return linkat_follow (fd1, file1, fd2, file2);
288}
289
290#endif /* HAVE_LINKAT */
291