]> git.cworth.org Git - tar/blob - lib/fchdir.c
Imported Upstream version 1.21
[tar] / lib / fchdir.c
1 /* fchdir replacement.
2    Copyright (C) 2006-2008 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 #include <config.h>
18
19 /* Specification.  */
20 #include <unistd.h>
21
22 #include <dirent.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30
31 #include "canonicalize.h"
32
33 /* This replacement assumes that a directory is not renamed while opened
34    through a file descriptor.  */
35
36 /* Array of file descriptors opened.  If it points to a directory, it stores
37    info about this directory; otherwise it stores an errno value of ENOTDIR.  */
38 typedef struct
39 {
40   char *name;       /* Absolute name of the directory, or NULL.  */
41   int saved_errno;  /* If name == NULL: The error code describing the failure
42                        reason.  */
43 } dir_info_t;
44 static dir_info_t *dirs;
45 static size_t dirs_allocated;
46
47 /* Try to ensure dirs has enough room for a slot at index fd.  */
48 static void
49 ensure_dirs_slot (size_t fd)
50 {
51   if (fd >= dirs_allocated)
52     {
53       size_t new_allocated;
54       dir_info_t *new_dirs;
55       size_t i;
56
57       new_allocated = 2 * dirs_allocated + 1;
58       if (new_allocated <= fd)
59         new_allocated = fd + 1;
60       new_dirs =
61         (dirs != NULL
62          ? (dir_info_t *) realloc (dirs, new_allocated * sizeof (dir_info_t))
63          : (dir_info_t *) malloc (new_allocated * sizeof (dir_info_t)));
64       if (new_dirs != NULL)
65         {
66           for (i = dirs_allocated; i < new_allocated; i++)
67             {
68               new_dirs[i].name = NULL;
69               new_dirs[i].saved_errno = ENOTDIR;
70             }
71           dirs = new_dirs;
72           dirs_allocated = new_allocated;
73         }
74     }
75 }
76
77 /* Hook into the gnulib replacements for open() and close() to keep track
78    of the open file descriptors.  */
79
80 void
81 _gl_unregister_fd (int fd)
82 {
83   if (fd >= 0 && fd < dirs_allocated)
84     {
85       if (dirs[fd].name != NULL)
86         free (dirs[fd].name);
87       dirs[fd].name = NULL;
88       dirs[fd].saved_errno = ENOTDIR;
89     }
90 }
91
92 void
93 _gl_register_fd (int fd, const char *filename)
94 {
95   struct stat statbuf;
96
97   ensure_dirs_slot (fd);
98   if (fd < dirs_allocated
99       && fstat (fd, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
100     {
101       dirs[fd].name = canonicalize_file_name (filename);
102       if (dirs[fd].name == NULL)
103         dirs[fd].saved_errno = errno;
104     }
105 }
106
107 /* Override opendir() and closedir(), to keep track of the open file
108    descriptors.  Needed because there is a function dirfd().  */
109
110 int
111 rpl_closedir (DIR *dp)
112 #undef closedir
113 {
114   int fd = dirfd (dp);
115   int retval = closedir (dp);
116
117   if (retval >= 0)
118     _gl_unregister_fd (fd);
119   return retval;
120 }
121
122 DIR *
123 rpl_opendir (const char *filename)
124 #undef opendir
125 {
126   DIR *dp;
127
128   dp = opendir (filename);
129   if (dp != NULL)
130     {
131       int fd = dirfd (dp);
132       if (fd >= 0)
133         _gl_register_fd (fd, filename);
134     }
135   return dp;
136 }
137
138 /* Override dup() and dup2(), to keep track of open file descriptors.  */
139
140 int
141 rpl_dup (int oldfd)
142 #undef dup
143 {
144   int newfd = dup (oldfd);
145
146   if (oldfd >= 0 && newfd >= 0)
147     {
148       ensure_dirs_slot (newfd);
149       if (newfd < dirs_allocated)
150         {
151           if (oldfd < dirs_allocated)
152             {
153               if (dirs[oldfd].name != NULL)
154                 {
155                   dirs[newfd].name = strdup (dirs[oldfd].name);
156                   if (dirs[newfd].name == NULL)
157                     dirs[newfd].saved_errno = ENOMEM;
158                 }
159               else
160                 {
161                   dirs[newfd].name = NULL;
162                   dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
163                 }
164             }
165           else
166             {
167               dirs[newfd].name = NULL;
168               dirs[newfd].saved_errno = ENOMEM;
169             }
170         }
171     }
172   return newfd;
173 }
174
175 int
176 rpl_dup2 (int oldfd, int newfd)
177 #undef dup2
178 {
179   int retval = dup2 (oldfd, newfd);
180
181   if (retval >= 0 && oldfd >= 0 && newfd >= 0 && newfd != oldfd)
182     {
183       ensure_dirs_slot (newfd);
184       if (newfd < dirs_allocated)
185         {
186           if (oldfd < dirs_allocated)
187             {
188               if (dirs[oldfd].name != NULL)
189                 {
190                   dirs[newfd].name = strdup (dirs[oldfd].name);
191                   if (dirs[newfd].name == NULL)
192                     dirs[newfd].saved_errno = ENOMEM;
193                 }
194               else
195                 {
196                   dirs[newfd].name = NULL;
197                   dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
198                 }
199             }
200           else
201             {
202               dirs[newfd].name = NULL;
203               dirs[newfd].saved_errno = ENOMEM;
204             }
205         }
206     }
207   return retval;
208 }
209
210 /* Implement fchdir() in terms of chdir().  */
211
212 int
213 fchdir (int fd)
214 {
215   if (fd >= 0)
216     {
217       if (fd < dirs_allocated)
218         {
219           if (dirs[fd].name != NULL)
220             return chdir (dirs[fd].name);
221           else
222             {
223               errno = dirs[fd].saved_errno;
224               return -1;
225             }
226         }
227       else
228         {
229           errno = ENOMEM;
230           return -1;
231         }
232     }
233   else
234     {
235       errno = EBADF;
236       return -1;
237     }
238 }