]> git.cworth.org Git - tar/blob - gnu/at-func2.c
Imported Upstream version 1.24
[tar] / gnu / at-func2.c
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* Define an at-style functions like linkat or renameat.
4    Copyright (C) 2006, 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 Jim Meyering and Eric Blake */
20
21 #include <config.h>
22
23 #include "openat-priv.h"
24
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
31 #include "filenamecat.h"
32 #include "openat.h"
33 #include "same-inode.h"
34 #include "save-cwd.h"
35
36 /* Call FUNC to operate on a pair of files, where FILE1 is relative to FD1,
37    and FILE2 is relative to FD2.  If possible, do it without changing the
38    working directory.  Otherwise, resort to using save_cwd/fchdir,
39    FUNC, restore_cwd (up to two times).  If either the save_cwd or the
40    restore_cwd fails, then give a diagnostic and exit nonzero.  */
41 int
42 at_func2 (int fd1, char const *file1,
43           int fd2, char const *file2,
44           int (*func) (char const *file1, char const *file2))
45 {
46   struct saved_cwd saved_cwd;
47   int saved_errno;
48   int err;
49   char *file1_alt;
50   char *file2_alt;
51   struct stat st1;
52   struct stat st2;
53
54   /* There are 16 possible scenarios, based on whether an fd is
55      AT_FDCWD or real, and whether a file is absolute or relative:
56
57          fd1  file1 fd2  file2  action
58      0   cwd  abs   cwd  abs    direct call
59      1   cwd  abs   cwd  rel    direct call
60      2   cwd  abs   fd   abs    direct call
61      3   cwd  abs   fd   rel    chdir to fd2
62      4   cwd  rel   cwd  abs    direct call
63      5   cwd  rel   cwd  rel    direct call
64      6   cwd  rel   fd   abs    direct call
65      7   cwd  rel   fd   rel    convert file1 to abs, then case 3
66      8   fd   abs   cwd  abs    direct call
67      9   fd   abs   cwd  rel    direct call
68      10  fd   abs   fd   abs    direct call
69      11  fd   abs   fd   rel    chdir to fd2
70      12  fd   rel   cwd  abs    chdir to fd1
71      13  fd   rel   cwd  rel    convert file2 to abs, then case 12
72      14  fd   rel   fd   abs    chdir to fd1
73      15a fd1  rel   fd1  rel    chdir to fd1
74      15b fd1  rel   fd2  rel    chdir to fd1, then case 7
75
76      Try some optimizations to reduce fd to AT_FDCWD, or to at least
77      avoid converting an absolute name or doing a double chdir.  */
78
79   if ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1))
80       && (fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2)))
81     return func (file1, file2); /* Case 0-2, 4-6, 8-10.  */
82
83   /* If /proc/self/fd works, we don't need any stat or chdir.  */
84   {
85     char proc_buf1[OPENAT_BUFFER_SIZE];
86     char *proc_file1 = ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1))
87                         ? (char *) file1
88                         : openat_proc_name (proc_buf1, fd1, file1));
89     if (proc_file1)
90       {
91         char proc_buf2[OPENAT_BUFFER_SIZE];
92         char *proc_file2 = ((fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2))
93                             ? (char *) file2
94                             : openat_proc_name (proc_buf2, fd2, file2));
95         if (proc_file2)
96           {
97             int proc_result = func (proc_file1, proc_file2);
98             int proc_errno = errno;
99             if (proc_file1 != proc_buf1 && proc_file1 != file1)
100               free (proc_file1);
101             if (proc_file2 != proc_buf2 && proc_file2 != file2)
102               free (proc_file2);
103             /* If the syscall succeeds, or if it fails with an unexpected
104                errno value, then return right away.  Otherwise, fall through
105                and resort to using save_cwd/restore_cwd.  */
106             if (0 <= proc_result)
107               return proc_result;
108             if (! EXPECTED_ERRNO (proc_errno))
109               {
110                 errno = proc_errno;
111                 return proc_result;
112               }
113           }
114         else if (proc_file1 != proc_buf1 && proc_file1 != file1)
115           free (proc_file1);
116       }
117   }
118
119   /* Cases 3, 7, 11-15 remain.  Time to normalize directory fds, if
120      possible.  */
121   if (IS_ABSOLUTE_FILE_NAME (file1))
122     fd1 = AT_FDCWD; /* Case 11 reduced to 3.  */
123   else if (IS_ABSOLUTE_FILE_NAME (file2))
124     fd2 = AT_FDCWD; /* Case 14 reduced to 12.  */
125
126   /* Cases 3, 7, 12, 13, 15 remain.  */
127
128   if (fd1 == AT_FDCWD) /* Cases 3, 7.  */
129     {
130       if (stat (".", &st1) == -1 || fstat (fd2, &st2) == -1)
131         return -1;
132       if (!S_ISDIR (st2.st_mode))
133         {
134           errno = ENOTDIR;
135           return -1;
136         }
137       if (SAME_INODE (st1, st2)) /* Reduced to cases 1, 5.  */
138         return func (file1, file2);
139     }
140   else if (fd2 == AT_FDCWD) /* Cases 12, 13.  */
141     {
142       if (stat (".", &st2) == -1 || fstat (fd1, &st1) == -1)
143         return -1;
144       if (!S_ISDIR (st1.st_mode))
145         {
146           errno = ENOTDIR;
147           return -1;
148         }
149       if (SAME_INODE (st1, st2)) /* Reduced to cases 4, 5.  */
150         return func (file1, file2);
151     }
152   else if (fd1 != fd2) /* Case 15b.  */
153     {
154       if (fstat (fd1, &st1) == -1 || fstat (fd2, &st2) == -1)
155         return -1;
156       if (!S_ISDIR (st1.st_mode) || !S_ISDIR (st2.st_mode))
157         {
158           errno = ENOTDIR;
159           return -1;
160         }
161       if (SAME_INODE (st1, st2)) /* Reduced to case 15a.  */
162         {
163           fd2 = fd1;
164           if (stat (".", &st1) == 0 && SAME_INODE (st1, st2))
165             return func (file1, file2); /* Further reduced to case 5.  */
166         }
167     }
168   else /* Case 15a.  */
169     {
170       if (fstat (fd1, &st1) == -1)
171         return -1;
172       if (!S_ISDIR (st1.st_mode))
173         {
174           errno = ENOTDIR;
175           return -1;
176         }
177       if (stat (".", &st2) == 0 && SAME_INODE (st1, st2))
178         return func (file1, file2); /* Reduced to case 5.  */
179     }
180
181   /* Cases 3, 7, 12, 13, 15a, 15b remain.  With all reductions in
182      place, it is time to start changing directories.  */
183
184   if (save_cwd (&saved_cwd) != 0)
185     openat_save_fail (errno);
186
187   if (fd1 != AT_FDCWD && fd2 != AT_FDCWD && fd1 != fd2) /* Case 15b.  */
188     {
189       if (fchdir (fd1) != 0)
190         {
191           saved_errno = errno;
192           free_cwd (&saved_cwd);
193           errno = saved_errno;
194           return -1;
195         }
196       fd1 = AT_FDCWD; /* Reduced to case 7.  */
197     }
198
199   /* Cases 3, 7, 12, 13, 15a remain.  Convert one relative name to
200      absolute, if necessary.  */
201
202   file1_alt = (char *) file1;
203   file2_alt = (char *) file2;
204
205   if (fd1 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file1)) /* Case 7.  */
206     {
207       /* It would be nicer to use:
208          file1_alt = file_name_concat (xgetcwd (), file1, NULL);
209          but libraries should not call xalloc_die.  */
210       char *cwd = getcwd (NULL, 0);
211       if (!cwd)
212         {
213           saved_errno = errno;
214           free_cwd (&saved_cwd);
215           errno = saved_errno;
216           return -1;
217         }
218       file1_alt = mfile_name_concat (cwd, file1, NULL);
219       if (!file1_alt)
220         {
221           saved_errno = errno;
222           free (cwd);
223           free_cwd (&saved_cwd);
224           errno = saved_errno;
225           return -1;
226         }
227       free (cwd); /* Reduced to case 3.  */
228     }
229   else if (fd2 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file2)) /* Case 13.  */
230     {
231       char *cwd = getcwd (NULL, 0);
232       if (!cwd)
233         {
234           saved_errno = errno;
235           free_cwd (&saved_cwd);
236           errno = saved_errno;
237           return -1;
238         }
239       file2_alt = mfile_name_concat (cwd, file2, NULL);
240       if (!file2_alt)
241         {
242           saved_errno = errno;
243           free (cwd);
244           free_cwd (&saved_cwd);
245           errno = saved_errno;
246           return -1;
247         }
248       free (cwd); /* Reduced to case 12.  */
249     }
250
251   /* Cases 3, 12, 15a remain.  Change to the correct directory.  */
252   if (fchdir (fd1 == AT_FDCWD ? fd2 : fd1) != 0)
253     {
254       saved_errno = errno;
255       free_cwd (&saved_cwd);
256       if (file1 != file1_alt)
257         free (file1_alt);
258       else if (file2 != file2_alt)
259         free (file2_alt);
260       errno = saved_errno;
261       return -1;
262     }
263
264   /* Finally safe to perform the user's function, then clean up.  */
265
266   err = func (file1_alt, file2_alt);
267   saved_errno = (err < 0 ? errno : 0);
268
269   if (file1 != file1_alt)
270     free (file1_alt);
271   else if (file2 != file2_alt)
272     free (file2_alt);
273
274   if (restore_cwd (&saved_cwd) != 0)
275     openat_restore_fail (errno);
276
277   free_cwd (&saved_cwd);
278
279   if (saved_errno)
280     errno = saved_errno;
281   return err;
282 }
283 #undef CALL_FUNC
284 #undef FUNC_RESULT