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 #if defined GNULIB_OPEN && ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
121 if (strcmp (filename, "/dev/null") == 0)
124 fd = open (filename, flags, mode);
127 ensure_dirs_slot (fd);
128 if (fd < dirs_allocated
129 && fstat (fd, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
131 dirs[fd].name = canonicalize_file_name (filename);
132 if (dirs[fd].name == NULL)
133 dirs[fd].saved_errno = errno;
139 /* Override opendir() and closedir(), to keep track of the open file
140 descriptors. Needed because there is a function dirfd(). */
147 int retval = closedir (dp);
149 if (retval >= 0 && fd >= 0 && fd < dirs_allocated)
151 if (dirs[fd].name != NULL)
152 free (dirs[fd].name);
153 dirs[fd].name = NULL;
154 dirs[fd].saved_errno = ENOTDIR;
160 opendir (const char *filename)
165 dp = opendir (filename);
171 ensure_dirs_slot (fd);
172 if (fd < dirs_allocated)
174 dirs[fd].name = canonicalize_file_name (filename);
175 if (dirs[fd].name == NULL)
176 dirs[fd].saved_errno = errno;
183 /* Override dup() and dup2(), to keep track of open file descriptors. */
189 int newfd = dup (oldfd);
191 if (oldfd >= 0 && newfd >= 0)
193 ensure_dirs_slot (newfd);
194 if (newfd < dirs_allocated)
196 if (oldfd < dirs_allocated)
198 if (dirs[oldfd].name != NULL)
200 dirs[newfd].name = strdup (dirs[oldfd].name);
201 if (dirs[newfd].name == NULL)
202 dirs[newfd].saved_errno = ENOMEM;
206 dirs[newfd].name = NULL;
207 dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
212 dirs[newfd].name = NULL;
213 dirs[newfd].saved_errno = ENOMEM;
221 dup2 (int oldfd, int newfd)
224 int retval = dup2 (oldfd, newfd);
226 if (retval >= 0 && oldfd >= 0 && newfd >= 0 && newfd != oldfd)
228 ensure_dirs_slot (newfd);
229 if (newfd < dirs_allocated)
231 if (oldfd < dirs_allocated)
233 if (dirs[oldfd].name != NULL)
235 dirs[newfd].name = strdup (dirs[oldfd].name);
236 if (dirs[newfd].name == NULL)
237 dirs[newfd].saved_errno = ENOMEM;
241 dirs[newfd].name = NULL;
242 dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
247 dirs[newfd].name = NULL;
248 dirs[newfd].saved_errno = ENOMEM;
255 /* Implement fchdir() in terms of chdir(). */
262 if (fd < dirs_allocated)
264 if (dirs[fd].name != NULL)
265 return chdir (dirs[fd].name);
268 errno = dirs[fd].saved_errno;