2 Copyright (C) 2006-2009 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 3 of the License, or
7 (at your option) any later version.
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, see <http://www.gnu.org/licenses/>. */
29 #include <sys/types.h>
32 #ifndef REPLACE_OPEN_DIRECTORY
33 # define REPLACE_OPEN_DIRECTORY 0
36 #ifndef HAVE_CANONICALIZE_FILE_NAME
37 # if GNULIB_CANONICALIZE || GNULIB_CANONICALIZE_LGPL
38 # define HAVE_CANONICALIZE_FILE_NAME 1
40 # define HAVE_CANONICALIZE_FILE_NAME 0
41 # define canonicalize_file_name(name) NULL
45 /* This replacement assumes that a directory is not renamed while opened
46 through a file descriptor.
48 FIXME: On mingw, this would be possible to enforce if we were to
49 also open a HANDLE to each directory currently visited by a file
50 descriptor, since mingw refuses to rename any in-use file system
53 /* Array of file descriptors opened. If REPLACE_OPEN_DIRECTORY or if it points
54 to a directory, it stores info about this directory. */
57 char *name; /* Absolute name of the directory, or NULL. */
58 /* FIXME - add a DIR* member to make dirfd possible on mingw? */
60 static dir_info_t *dirs;
61 static size_t dirs_allocated;
63 /* Try to ensure dirs has enough room for a slot at index fd; free any
64 contents already in that slot. Return false and set errno to
65 ENOMEM on allocation failure. */
67 ensure_dirs_slot (size_t fd)
69 if (fd < dirs_allocated)
76 new_allocated = 2 * dirs_allocated + 1;
77 if (new_allocated <= fd)
78 new_allocated = fd + 1;
81 ? (dir_info_t *) realloc (dirs, new_allocated * sizeof *dirs)
82 : (dir_info_t *) malloc (new_allocated * sizeof *dirs));
85 memset (new_dirs + dirs_allocated, 0,
86 (new_allocated - dirs_allocated) * sizeof *dirs);
88 dirs_allocated = new_allocated;
93 /* Return the canonical name of DIR in malloc'd storage. */
95 get_name (char const *dir)
98 if (REPLACE_OPEN_DIRECTORY || !HAVE_CANONICALIZE_FILE_NAME)
100 /* The function canonicalize_file_name has not yet been ported
101 to mingw, with all its drive letter and backslash quirks.
102 Fortunately, getcwd is reliable in this case, but we ensure
103 we can get back to where we started before using it. Treat
104 "." as a special case, as it is frequently encountered. */
105 char *cwd = getcwd (NULL, 0);
107 if (dir[0] == '.' && dir[1] == '\0')
111 result = chdir (dir) ? NULL : getcwd (NULL, 0);
120 /* Avoid changing the directory. */
121 result = canonicalize_file_name (dir);
126 /* Hook into the gnulib replacements for open() and close() to keep track
127 of the open file descriptors. */
129 /* Close FD, cleaning up any fd to name mapping if fd was visiting a
132 _gl_unregister_fd (int fd)
134 if (fd >= 0 && fd < dirs_allocated)
136 free (dirs[fd].name);
137 dirs[fd].name = NULL;
141 /* Mark FD as visiting FILENAME. FD must be non-negative, and refer
142 to an open file descriptor. If REPLACE_OPEN_DIRECTORY is non-zero,
143 this should only be called if FD is visiting a directory. Close FD
144 and return -1 if there is insufficient memory to track the
145 directory name; otherwise return FD. */
147 _gl_register_fd (int fd, const char *filename)
152 if (REPLACE_OPEN_DIRECTORY
153 || (fstat (fd, &statbuf) == 0 && S_ISDIR (statbuf.st_mode)))
155 if (!ensure_dirs_slot (fd)
156 || (dirs[fd].name = get_name (filename)) == NULL)
158 int saved_errno = errno;
167 /* Mark NEWFD as a duplicate of OLDFD; useful from dup, dup2, dup3,
168 and fcntl. Both arguments must be valid and distinct file
169 descriptors. Close NEWFD and return -1 if OLDFD is tracking a
170 directory, but there is insufficient memory to track the same
171 directory in NEWFD; otherwise return NEWFD.
173 FIXME: Need to implement rpl_fcntl in gnulib, and have it call
176 _gl_register_dup (int oldfd, int newfd)
178 assert (0 <= oldfd && 0 <= newfd && oldfd != newfd);
179 if (oldfd < dirs_allocated && dirs[oldfd].name)
181 /* Duplicated a directory; must ensure newfd is allocated. */
182 if (!ensure_dirs_slot (newfd)
183 || (dirs[newfd].name = strdup (dirs[oldfd].name)) == NULL)
185 int saved_errno = errno;
191 else if (newfd < dirs_allocated)
193 /* Duplicated a non-directory; ensure newfd is cleared. */
194 free (dirs[newfd].name);
195 dirs[newfd].name = NULL;
200 /* If FD is currently visiting a directory, then return the name of
201 that directory. Otherwise, return NULL and set errno. */
203 _gl_directory_name (int fd)
205 if (0 <= fd && fd < dirs_allocated && dirs[fd].name != NULL)
206 return dirs[fd].name;
207 /* At this point, fd is either invalid, or open but not a directory.
208 If dup2 fails, errno is correctly EBADF. */
211 if (dup2 (fd, fd) == fd)
219 #if REPLACE_OPEN_DIRECTORY
220 /* Return stat information about FD in STATBUF. Needed when
221 rpl_open() used a dummy file to work around an open() that can't
222 normally visit directories. */
225 rpl_fstat (int fd, struct stat *statbuf)
227 if (0 <= fd && fd < dirs_allocated && dirs[fd].name != NULL)
228 return stat (dirs[fd].name, statbuf);
229 return fstat (fd, statbuf);
233 /* Override opendir() and closedir(), to keep track of the open file
234 descriptors. Needed because there is a function dirfd(). */
237 rpl_closedir (DIR *dp)
241 int retval = closedir (dp);
244 _gl_unregister_fd (fd);
249 rpl_opendir (const char *filename)
254 dp = opendir (filename);
258 if (0 <= fd && _gl_register_fd (fd, filename) != fd)
260 int saved_errno = errno;
269 /* Override dup(), to keep track of open file descriptors. */
275 int newfd = dup (oldfd);
278 newfd = _gl_register_dup (oldfd, newfd);
283 /* Implement fchdir() in terms of chdir(). */
288 const char *name = _gl_directory_name (fd);
289 return name ? chdir (name) : -1;