X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=lib%2Ffchdir.c;h=015e5f382f35222c93c4733692e21c1ee96cee33;hb=b187ff0528e2a081392a834e684d1d0c161708a7;hp=1f7cf994c23db9958a3668216620e80c053368f8;hpb=5e6b92bb993a0e913877c495687556df918d7d86;p=gnulib.git
diff --git a/lib/fchdir.c b/lib/fchdir.c
index 1f7cf994c..015e5f382 100644
--- a/lib/fchdir.c
+++ b/lib/fchdir.c
@@ -1,10 +1,10 @@
/* fchdir replacement.
- Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2006-2010 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -12,152 +12,238 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see . */
#include
/* Specification. */
#include
+#include
+#include
#include
#include
-#include
+#include
#include
#include
#include
#include
-#include
-#include "canonicalize.h"
-#include "dirfd.h"
+#ifndef REPLACE_OPEN_DIRECTORY
+# define REPLACE_OPEN_DIRECTORY 0
+#endif
+
+#ifndef HAVE_CANONICALIZE_FILE_NAME
+# if GNULIB_CANONICALIZE || GNULIB_CANONICALIZE_LGPL
+# define HAVE_CANONICALIZE_FILE_NAME 1
+# else
+# define HAVE_CANONICALIZE_FILE_NAME 0
+# define canonicalize_file_name(name) NULL
+# endif
+#endif
/* This replacement assumes that a directory is not renamed while opened
- through a file descriptor. */
+ through a file descriptor.
+
+ FIXME: On mingw, this would be possible to enforce if we were to
+ also open a HANDLE to each directory currently visited by a file
+ descriptor, since mingw refuses to rename any in-use file system
+ object. */
-/* Array of file descriptors opened. If it points to a directory, it stores
- info about this directory; otherwise it stores an errno value of ENOTDIR. */
+/* Array of file descriptors opened. If REPLACE_OPEN_DIRECTORY or if it points
+ to a directory, it stores info about this directory. */
typedef struct
{
char *name; /* Absolute name of the directory, or NULL. */
- int saved_errno; /* If name == NULL: The error code describing the failure
- reason. */
+ /* FIXME - add a DIR* member to make dirfd possible on mingw? */
} dir_info_t;
static dir_info_t *dirs;
static size_t dirs_allocated;
-/* Try to ensure dirs has enough room for a slot at index fd. */
-static void
+/* Try to ensure dirs has enough room for a slot at index fd; free any
+ contents already in that slot. Return false and set errno to
+ ENOMEM on allocation failure. */
+static bool
ensure_dirs_slot (size_t fd)
{
- if (fd >= dirs_allocated)
+ if (fd < dirs_allocated)
+ free (dirs[fd].name);
+ else
{
size_t new_allocated;
dir_info_t *new_dirs;
- size_t i;
new_allocated = 2 * dirs_allocated + 1;
if (new_allocated <= fd)
- new_allocated = fd + 1;
+ new_allocated = fd + 1;
new_dirs =
- (dirs != NULL
- ? (dir_info_t *) realloc (dirs, new_allocated * sizeof (dir_info_t))
- : (dir_info_t *) malloc (new_allocated * sizeof (dir_info_t)));
- if (new_dirs != NULL)
- {
- for (i = dirs_allocated; i < new_allocated; i++)
- {
- new_dirs[i].name = NULL;
- new_dirs[i].saved_errno = ENOTDIR;
- }
- dirs = new_dirs;
- dirs_allocated = new_allocated;
- }
+ (dirs != NULL
+ ? (dir_info_t *) realloc (dirs, new_allocated * sizeof *dirs)
+ : (dir_info_t *) malloc (new_allocated * sizeof *dirs));
+ if (new_dirs == NULL)
+ return false;
+ memset (new_dirs + dirs_allocated, 0,
+ (new_allocated - dirs_allocated) * sizeof *dirs);
+ dirs = new_dirs;
+ dirs_allocated = new_allocated;
}
+ return true;
}
-/* Override open() and close(), to keep track of the open file descriptors. */
-
-int
-close (int fd)
-#undef close
+/* Return the canonical name of DIR in malloc'd storage. */
+static char *
+get_name (char const *dir)
{
- int retval = close (fd);
+ char *result;
+ if (REPLACE_OPEN_DIRECTORY || !HAVE_CANONICALIZE_FILE_NAME)
+ {
+ /* The function canonicalize_file_name has not yet been ported
+ to mingw, with all its drive letter and backslash quirks.
+ Fortunately, getcwd is reliable in this case, but we ensure
+ we can get back to where we started before using it. Treat
+ "." as a special case, as it is frequently encountered. */
+ char *cwd = getcwd (NULL, 0);
+ int saved_errno;
+ if (dir[0] == '.' && dir[1] == '\0')
+ return cwd;
+ if (chdir (cwd))
+ return NULL;
+ result = chdir (dir) ? NULL : getcwd (NULL, 0);
+ saved_errno = errno;
+ if (chdir (cwd))
+ abort ();
+ free (cwd);
+ errno = saved_errno;
+ }
+ else
+ {
+ /* Avoid changing the directory. */
+ result = canonicalize_file_name (dir);
+ }
+ return result;
+}
+
+/* Hook into the gnulib replacements for open() and close() to keep track
+ of the open file descriptors. */
- if (retval >= 0 && fd >= 0 && fd < dirs_allocated)
+/* Close FD, cleaning up any fd to name mapping if fd was visiting a
+ directory. */
+void
+_gl_unregister_fd (int fd)
+{
+ if (fd >= 0 && fd < dirs_allocated)
{
- if (dirs[fd].name != NULL)
- free (dirs[fd].name);
+ free (dirs[fd].name);
dirs[fd].name = NULL;
- dirs[fd].saved_errno = ENOTDIR;
}
- return retval;
}
+/* Mark FD as visiting FILENAME. FD must be non-negative, and refer
+ to an open file descriptor. If REPLACE_OPEN_DIRECTORY is non-zero,
+ this should only be called if FD is visiting a directory. Close FD
+ and return -1 if there is insufficient memory to track the
+ directory name; otherwise return FD. */
int
-open (const char *filename, int flags, ...)
-#undef open
+_gl_register_fd (int fd, const char *filename)
{
- mode_t mode;
- int fd;
struct stat statbuf;
- mode = 0;
- if (flags & O_CREAT)
+ assert (0 <= fd);
+ if (REPLACE_OPEN_DIRECTORY
+ || (fstat (fd, &statbuf) == 0 && S_ISDIR (statbuf.st_mode)))
{
- va_list arg;
- va_start (arg, flags);
-
- /* If mode_t is narrower than int, use the promoted type (int),
- not mode_t. Use sizeof to guess whether mode_t is narrower;
- we don't know of any practical counterexamples. */
- mode = (sizeof (mode_t) < sizeof (int)
- ? va_arg (arg, int)
- : va_arg (arg, mode_t));
+ if (!ensure_dirs_slot (fd)
+ || (dirs[fd].name = get_name (filename)) == NULL)
+ {
+ int saved_errno = errno;
+ close (fd);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+ return fd;
+}
- va_end (arg);
+/* Mark NEWFD as a duplicate of OLDFD; useful from dup, dup2, dup3,
+ and fcntl. Both arguments must be valid and distinct file
+ descriptors. Close NEWFD and return -1 if OLDFD is tracking a
+ directory, but there is insufficient memory to track the same
+ directory in NEWFD; otherwise return NEWFD. */
+int
+_gl_register_dup (int oldfd, int newfd)
+{
+ assert (0 <= oldfd && 0 <= newfd && oldfd != newfd);
+ if (oldfd < dirs_allocated && dirs[oldfd].name)
+ {
+ /* Duplicated a directory; must ensure newfd is allocated. */
+ if (!ensure_dirs_slot (newfd)
+ || (dirs[newfd].name = strdup (dirs[oldfd].name)) == NULL)
+ {
+ int saved_errno = errno;
+ close (newfd);
+ errno = saved_errno;
+ newfd = -1;
+ }
}
-#if defined GNULIB_OPEN && ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
- if (strcmp (filename, "/dev/null") == 0)
- filename = "NUL";
-#endif
- fd = open (filename, flags, mode);
- if (fd >= 0)
+ else if (newfd < dirs_allocated)
{
- ensure_dirs_slot (fd);
- if (fd < dirs_allocated
- && fstat (fd, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
- {
- dirs[fd].name = canonicalize_file_name (filename);
- if (dirs[fd].name == NULL)
- dirs[fd].saved_errno = errno;
- }
+ /* Duplicated a non-directory; ensure newfd is cleared. */
+ free (dirs[newfd].name);
+ dirs[newfd].name = NULL;
}
- return fd;
+ return newfd;
}
+/* If FD is currently visiting a directory, then return the name of
+ that directory. Otherwise, return NULL and set errno. */
+const char *
+_gl_directory_name (int fd)
+{
+ if (0 <= fd && fd < dirs_allocated && dirs[fd].name != NULL)
+ return dirs[fd].name;
+ /* At this point, fd is either invalid, or open but not a directory.
+ If dup2 fails, errno is correctly EBADF. */
+ if (0 <= fd)
+ {
+ if (dup2 (fd, fd) == fd)
+ errno = ENOTDIR;
+ }
+ else
+ errno = EBADF;
+ return NULL;
+}
+
+#if REPLACE_OPEN_DIRECTORY
+/* Return stat information about FD in STATBUF. Needed when
+ rpl_open() used a dummy file to work around an open() that can't
+ normally visit directories. */
+# undef fstat
+int
+rpl_fstat (int fd, struct stat *statbuf)
+{
+ if (0 <= fd && fd < dirs_allocated && dirs[fd].name != NULL)
+ return stat (dirs[fd].name, statbuf);
+ return fstat (fd, statbuf);
+}
+#endif
+
/* Override opendir() and closedir(), to keep track of the open file
descriptors. Needed because there is a function dirfd(). */
int
-closedir (DIR *dp)
+rpl_closedir (DIR *dp)
#undef closedir
{
int fd = dirfd (dp);
int retval = closedir (dp);
- if (retval >= 0 && fd >= 0 && fd < dirs_allocated)
- {
- if (dirs[fd].name != NULL)
- free (dirs[fd].name);
- dirs[fd].name = NULL;
- dirs[fd].saved_errno = ENOTDIR;
- }
+ if (retval >= 0)
+ _gl_unregister_fd (fd);
return retval;
}
DIR *
-opendir (const char *filename)
+rpl_opendir (const char *filename)
#undef opendir
{
DIR *dp;
@@ -166,118 +252,36 @@ opendir (const char *filename)
if (dp != NULL)
{
int fd = dirfd (dp);
- if (fd >= 0)
- {
- ensure_dirs_slot (fd);
- if (fd < dirs_allocated)
- {
- dirs[fd].name = canonicalize_file_name (filename);
- if (dirs[fd].name == NULL)
- dirs[fd].saved_errno = errno;
- }
- }
+ if (0 <= fd && _gl_register_fd (fd, filename) != fd)
+ {
+ int saved_errno = errno;
+ closedir (dp);
+ errno = saved_errno;
+ return NULL;
+ }
}
return dp;
}
-/* Override dup() and dup2(), to keep track of open file descriptors. */
+/* Override dup(), to keep track of open file descriptors. */
int
-dup (int oldfd)
+rpl_dup (int oldfd)
#undef dup
{
int newfd = dup (oldfd);
- if (oldfd >= 0 && newfd >= 0)
- {
- ensure_dirs_slot (newfd);
- if (newfd < dirs_allocated)
- {
- if (oldfd < dirs_allocated)
- {
- if (dirs[oldfd].name != NULL)
- {
- dirs[newfd].name = strdup (dirs[oldfd].name);
- if (dirs[newfd].name == NULL)
- dirs[newfd].saved_errno = ENOMEM;
- }
- else
- {
- dirs[newfd].name = NULL;
- dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
- }
- }
- else
- {
- dirs[newfd].name = NULL;
- dirs[newfd].saved_errno = ENOMEM;
- }
- }
- }
+ if (0 <= newfd)
+ newfd = _gl_register_dup (oldfd, newfd);
return newfd;
}
-int
-dup2 (int oldfd, int newfd)
-#undef dup2
-{
- int retval = dup2 (oldfd, newfd);
-
- if (retval >= 0 && oldfd >= 0 && newfd >= 0 && newfd != oldfd)
- {
- ensure_dirs_slot (newfd);
- if (newfd < dirs_allocated)
- {
- if (oldfd < dirs_allocated)
- {
- if (dirs[oldfd].name != NULL)
- {
- dirs[newfd].name = strdup (dirs[oldfd].name);
- if (dirs[newfd].name == NULL)
- dirs[newfd].saved_errno = ENOMEM;
- }
- else
- {
- dirs[newfd].name = NULL;
- dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
- }
- }
- else
- {
- dirs[newfd].name = NULL;
- dirs[newfd].saved_errno = ENOMEM;
- }
- }
- }
- return retval;
-}
/* Implement fchdir() in terms of chdir(). */
int
fchdir (int fd)
{
- if (fd >= 0)
- {
- if (fd < dirs_allocated)
- {
- if (dirs[fd].name != NULL)
- return chdir (dirs[fd].name);
- else
- {
- errno = dirs[fd].saved_errno;
- return -1;
- }
- }
- else
- {
- errno = ENOMEM;
- return -1;
- }
- }
- else
- {
- errno = EBADF;
- return -1;
- }
+ const char *name = _gl_directory_name (fd);
+ return name ? chdir (name) : -1;
}