linkat: new module
[gnulib.git] / 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 <sys/stat.h>
27
28 #include "areadlink.h"
29 #include "dirname.h"
30 #include "filenamecat.h"
31 #include "openat-priv.h"
32
33 #if HAVE_SYS_PARAM_H
34 # include <sys/param.h>
35 #endif
36 #ifndef MAXSYMLINKS
37 # ifdef SYMLOOP_MAX
38 #  define MAXSYMLINKS SYMLOOP_MAX
39 # else
40 #  define MAXSYMLINKS 20
41 # endif
42 #endif
43
44 /* Create a link.  If FILE1 is a symlink, either create a hardlink to
45    that symlink, or fake it by creating an identical symlink.  */
46 #if LINK_FOLLOWS_SYMLINKS == 0
47 # define link_immediate link
48 #else
49 static int
50 link_immediate (char const *file1, char const *file2)
51 {
52   char *target = areadlink (file1);
53   if (target)
54     {
55       /* A symlink cannot be modified in-place.  Therefore, creating
56          an identical symlink behaves like a hard link to a symlink,
57          except for incorrect st_ino and st_nlink.  However, we must
58          be careful of EXDEV.  */
59       struct stat st1;
60       struct stat st2;
61       char *dir = mdir_name (file2);
62       if (!dir)
63         {
64           free (target);
65           errno = ENOMEM;
66           return -1;
67         }
68       if (lstat (file1, &st1) == 0 && stat (dir, &st2) == 0)
69         {
70           if (st1.st_dev == st2.st_dev)
71             {
72               int result = symlink (target, file2);
73               int saved_errno = errno;
74               free (target);
75               free (dir);
76               errno = saved_errno;
77               return result;
78             }
79           free (target);
80           free (dir);
81           errno = EXDEV;
82           return -1;
83         }
84       free (target);
85       free (dir);
86     }
87   if (errno == ENOMEM)
88     return -1;
89   return link (file1, file2);
90 }
91 #endif
92
93 /* Create a link.  If FILE1 is a symlink, create a hardlink to the
94    canonicalized file.  */
95 #if 0 < LINK_FOLLOWS_SYMLINKS
96 # define link_follow link
97 #else
98 static int
99 link_follow (char const *file1, char const *file2)
100 {
101   char *name = (char *) file1;
102   char *target;
103   int result;
104   int i = MAXSYMLINKS;
105
106   /* Using realpath or canonicalize_file_name is too heavy-handed: we
107      don't need an absolute name, and we don't need to resolve
108      intermediate symlinks, just the basename of each iteration.  */
109   while (i-- && (target = areadlink (name)))
110     {
111       if (IS_ABSOLUTE_FILE_NAME (target))
112         {
113           if (name != file1)
114             free (name);
115           name = target;
116         }
117       else
118         {
119           char *dir = mdir_name (name);
120           if (name != file1)
121             free (name);
122           if (!dir)
123             {
124               free (target);
125               errno = ENOMEM;
126               return -1;
127             }
128           name = mfile_name_concat (dir, target, NULL);
129           free (dir);
130           free (target);
131           if (!name)
132             {
133               errno = ENOMEM;
134               return -1;
135             }
136         }
137     }
138   if (i < 0)
139     {
140       target = NULL;
141       errno = ELOOP;
142     }
143   if (!target && errno != EINVAL)
144     {
145       if (name != file1)
146         {
147           int saved_errno = errno;
148           free (name);
149           errno = saved_errno;
150         }
151       return -1;
152     }
153   result = link (name, file2);
154   if (name != file1)
155     {
156       int saved_errno = errno;
157       free (name);
158       errno = saved_errno;
159     }
160   return result;
161 }
162 #endif
163
164 /* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2,
165    in the directory open on descriptor FD2.  If FILE1 is a symlink, FLAG
166    controls whether to dereference FILE1 first.  If possible, do it without
167    changing the working directory.  Otherwise, resort to using
168    save_cwd/fchdir, then rename/restore_cwd.  If either the save_cwd or
169    the restore_cwd fails, then give a diagnostic and exit nonzero.  */
170
171 int
172 linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
173 {
174   if (flag & ~AT_SYMLINK_FOLLOW)
175     {
176       errno = EINVAL;
177       return -1;
178     }
179   return at_func2 (fd1, file1, fd2, file2,
180                    flag ? link_follow : link_immediate);
181 }