2 Copyright (C) 2006, 2007 Free Software Foundation, Inc.
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 2, or (at your option)
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.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
28 #include <sys/types.h>
32 #include "canonicalize.h"
35 /* This replacement assumes that a directory is not renamed while opened
36 through a file descriptor. */
38 /* Array of file descriptors opened. If it points to a directory, it stores
39 info about this directory; otherwise it stores an errno value of ENOTDIR. */
42 char *name; /* Absolute name of the directory, or NULL. */
43 int saved_errno; /* If name == NULL: The error code describing the failure
46 static dir_info_t *dirs;
47 static size_t dirs_allocated;
49 /* Try to ensure dirs has enough room for a slot at index fd. */
51 ensure_dirs_slot (size_t fd)
53 if (fd >= dirs_allocated)
59 new_allocated = 2 * dirs_allocated + 1;
60 if (new_allocated <= fd)
61 new_allocated = fd + 1;
64 ? (dir_info_t *) realloc (dirs, new_allocated * sizeof (dir_info_t))
65 : (dir_info_t *) malloc (new_allocated * sizeof (dir_info_t)));
68 for (i = dirs_allocated; i < new_allocated; i++)
70 new_dirs[i].name = NULL;
71 new_dirs[i].saved_errno = ENOTDIR;
74 dirs_allocated = new_allocated;
79 /* Override open() and close(), to keep track of the open file descriptors. */
85 int retval = close (fd);
87 if (retval >= 0 && fd >= 0 && fd < dirs_allocated)
89 if (dirs[fd].name != NULL)
92 dirs[fd].saved_errno = ENOTDIR;
98 open (const char *filename, int flags, ...)
109 va_start (arg, flags);
111 /* If mode_t is narrower than int, use the promoted type (int),
112 not mode_t. Use sizeof to guess whether mode_t is narrower;
113 we don't know of any practical counterexamples. */
114 mode = (sizeof (mode_t) < sizeof (int)
116 : va_arg (arg, mode_t));
120 fd = open (filename, flags, mode);
123 ensure_dirs_slot (fd);
124 if (fd < dirs_allocated
125 && fstat (fd, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
127 dirs[fd].name = canonicalize_file_name (filename);
128 if (dirs[fd].name == NULL)
129 dirs[fd].saved_errno = errno;
135 /* Override opendir() and closedir(), to keep track of the open file
136 descriptors. Needed because there is a function dirfd(). */
143 int retval = closedir (dp);
145 if (retval >= 0 && fd >= 0 && fd < dirs_allocated)
147 if (dirs[fd].name != NULL)
148 free (dirs[fd].name);
149 dirs[fd].name = NULL;
150 dirs[fd].saved_errno = ENOTDIR;
156 opendir (const char *filename)
161 dp = opendir (filename);
167 ensure_dirs_slot (fd);
168 if (fd < dirs_allocated)
170 dirs[fd].name = canonicalize_file_name (filename);
171 if (dirs[fd].name == NULL)
172 dirs[fd].saved_errno = errno;
179 /* Override dup() and dup2(), to keep track of open file descriptors. */
185 int newfd = dup (oldfd);
187 if (oldfd >= 0 && newfd >= 0)
189 ensure_dirs_slot (newfd);
190 if (newfd < dirs_allocated)
192 if (oldfd < dirs_allocated)
194 if (dirs[oldfd].name != NULL)
196 dirs[newfd].name = strdup (dirs[oldfd].name);
197 if (dirs[newfd].name == NULL)
198 dirs[newfd].saved_errno = ENOMEM;
202 dirs[newfd].name = NULL;
203 dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
208 dirs[newfd].name = NULL;
209 dirs[newfd].saved_errno = ENOMEM;
217 dup2 (int oldfd, int newfd)
220 int retval = dup2 (oldfd, newfd);
222 if (retval >= 0 && oldfd >= 0 && newfd >= 0 && newfd != oldfd)
224 ensure_dirs_slot (newfd);
225 if (newfd < dirs_allocated)
227 if (oldfd < dirs_allocated)
229 if (dirs[oldfd].name != NULL)
231 dirs[newfd].name = strdup (dirs[oldfd].name);
232 if (dirs[newfd].name == NULL)
233 dirs[newfd].saved_errno = ENOMEM;
237 dirs[newfd].name = NULL;
238 dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
243 dirs[newfd].name = NULL;
244 dirs[newfd].saved_errno = ENOMEM;
251 /* Implement fchdir() in terms of chdir(). */
258 if (fd < dirs_allocated)
260 if (dirs[fd].name != NULL)
261 return chdir (dirs[fd].name);
264 errno = dirs[fd].saved_errno;