bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

changes for openat, mkdir-p, lchmod


From: Paul Eggert
Subject: changes for openat, mkdir-p, lchmod
Date: Mon, 09 Jan 2006 15:30:13 -0800
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

I installed this to sync gnulib from coreutils.  This is the biggest
change hunk; the changes all tend to depend on each other.  It's still
mutating but I think this snapshot will work with other programs (it
works with GNU tar, anyway).

The lchmod business is a bit tricky, since it uses chmod as a
substitute for lchmod.  Callers are supposed to check that files are
not symbolic links before using lchmod, which obviously leads to race
conditions, but that's the best we can do on hosts that lack lchmod.

2006-01-09  Paul Eggert  <address@hidden>

        Sync from coreutils.

        * lib/lchmod.h: New file.
        * lib/mkdir-p.c: Include lchmod.h, lchown.h.
        (make_dir_parents): Use lchown rather than chown, and
        lchmod rather than chmod.

        * modules/mkdir-p (Files): Add chdir-safer.c, chdir-safer.h, lchmod.h,
        chdir-safer.m4, lchmod.m4.
        * modules/openat: Add mkdirat.c, openat-priv.h.

        * m4/lchmod.m4: New file.

2006-01-09  Jim Meyering  <address@hidden>

        Sync from coreutils.

        Rewrite fts.c not to change the current working directory,
        by using openat, fstatat, fdopendir, etc..

        * lib/fts.c [! _LIBC]: Include "openat.h", "unistd--.h", and 
"fcntl--.h".
        [_LIBC] (fchdir): Don't undef or define; no longer used.
        (FCHDIR): Define in terms of cwd_advance_fd rather than fchdir.
        Now, this `function' always succeeds, and consumes its file descriptor
        parameter -- so callers must not close such FDs.  Update callers.
        (diropen_fd, opendirat, cwd_advance_fd): New functions.
        (diropen): Add parameter, SP.  Adjust all callers.
        Implement using diropen_fd, rather than open.
        (fts_open): Initialize new member, fts_cwd_fd.
        Remove fts_rft-setting code.
        (fts_close): Close fts_cwd_fd, if necessary.
        (__opendir2): Define in terms of opendir or opendirat,
        depending on whether the FST_NOCHDIR flag is set.
        (fts_build): Since fts_safe_changedir consumes its FD, and since
        this code must do `closedir(dirp)', dup the dirfd(dirp) argument,
        and close the dup'd file descriptor upon failure.
        (fts_stat): Use fstatat(...AT_SYMLINK_NOFOLLOW) in place of lstat.
        (fts_safe_changedir): Tweak semantics to reflect that this function
        now calls cwd_advance_fd and hence consumes its FD argument.
        * lib/fts_.h [struct FTS] (fts_cwd_fd): New member.
        (fts_rft): Remove now-unused member.

        * lib/openat.c (fchownat): New function.
        * lib/openat.h (fchmodat, fchownat): Declare.
        (chmodat, lchmodat): Define convenience functions.
        (chownat, lchownat): Likewise.

        * lib/chdir-safer.h, chdir-safer.c: New files.

        * lib/openat.c: Include "fcntl--.h" and "unistd--.h", to map open
        and dup to open_safer and dup_safer, respectively.
        (openat_permissive): Fix typo in comment.

        * lib/openat.c: Don't include <stdlib.h>, <unistd.h>, <fcntl.h>,
        "gettext.h"; either no longer needed or are guaranteed by openat.h.
        (_): Remove; no longer needed.
        (openat): Renamed from rpl_openat; no need for rpl_openat
        since openat.h renames openat for us.
        Replace most of the body with a call to openat_permissive,
        to avoid duplicate code.
        Port to (probably hypothetical) environments were mode_t is
        wider than int.
        (openat_permissive): Require mode arg, so that we can check
        types better.  Put it just after flags.  Change cwd failure
        indicator from pointer-to-bool to pointer-to-errno-value.
        All callers changed.
        Invoke openat_save_fail and/or openat_restore_fail if
        cwd_errno is null, so that openat can call us.
        (openat_permissive, fdopendir, fstatat, unlinkat):
        Simplify errno handling to avoid some duplicate code,
        as it's OK to set errno on success.
        * lib/openat.h: Revamp code so that function macros depend on
        __OPENAT_PREFIX only, not also on AT_FDCWD.
        (openat_ro): Remove.  Caller changed to use openat_permissive.
        (openat_permissive): Now a macro, if not a function.
        (openat_restore_fail, openat_save_fail): Now always functions,
        since mkdirat needs them even if __OPENAT_PREFIX is defined.

        * lib/openat-priv.h: New file, defining macros used by mkdirat.c
        and openat.c.
        * lib/mkdirat.c: Include openat-priv.h.
        Remove definitions of macros defined therein.
        * lib/openat.c: Likewise.

        * lib/mkdirat.c (mkdirat): New file and function.
        * lib/openat.h (mkdirat): Declare.

        * lib/openat.c (fdopendir): Don't change errno when returning non-NULL.

        * lib/openat.h (openat_permissive): Declare.
        (openat_ro): Define.

        * lib/openat.c (EXPECTED_ERRNO): New macro.
        (openat_permissive): New function -- used in remove.c rewrite.
        (all functions): Set errno just before returning, only if there
        was an actual failure.
        Use EXPECTED_ERRNO rather than comparing against only ENOTDIR.

        Emulate openat-family functions using Linux's procfs, if possible.
        Idea and some code based on Ulrich Drepper's glibc changes.

        * lib/openat.c: (BUILD_PROC_NAME): New macro.
        Include <stdio.h>, <string.h>, "alloca.h" and "intprops.h".
        (rpl_openat): Emulate by trying to open /proc/self/fd/%d/%s,
        before falling back on save_cwd and restore_cwd.
        (fdopendir, fstatat, unlinkat): Likewise.

        * lib/openat.c (fstatat, unlinkat): Perform the syscall directly,
        skipping the save_cwd...restore_cwd overhead, if FILE is absolute.

        * lib/openat.c (rpl_openat): Use the promoted type (int), not mode_t,
        as second argument to va_arg.  Otherwise, some versions of gcc
        warn that `if this code is reached, the program will abort'.

        * m4/chdir-safer.m4: New file.
        * m4/openat.m4 (gl_FUNC_OPENAT): Require and compile mkdirat.c.
        Require openat-priv.h.

Index: lib/mkdir-p.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/mkdir-p.c,v
retrieving revision 1.4
diff -p -u -r1.4 mkdir-p.c
--- lib/mkdir-p.c       22 Sep 2005 23:24:03 -0000      1.4
+++ lib/mkdir-p.c       9 Jan 2006 23:02:07 -0000
@@ -1,6 +1,6 @@
 /* mkdir-p.c -- Ensure that a directory and its parents exist.
 
-   Copyright (C) 1990, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005
+   Copyright (C) 1990, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
@@ -39,18 +39,15 @@
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-#include "save-cwd.h"
+#include "chdir-safer.h"
 #include "dirname.h"
 #include "error.h"
+#include "lchmod.h"
+#include "lchown.h"
 #include "quote.h"
+#include "save-cwd.h"
 #include "stat-macros.h"
 
-#ifndef ENOSYS
-# define ENOSYS EEXIST
-#endif
-
-#define WX_USR (S_IWUSR | S_IXUSR)
-
 /* Ensure that the directory ARG exists.
 
    Create any leading directories that don't already exist, with
@@ -127,15 +124,15 @@ make_dir_parents (char const *arg,
       mode_t oldmask = umask (0);
 
       /* Make a copy of ARG that we can scribble NULs on.  */
-      dir = (char *) alloca (strlen (arg) + 1);
+      dir = alloca (strlen (arg) + 1);
       strcpy (dir, arg);
       strip_trailing_slashes (dir);
       full_dir = dir;
 
-      /* If leading directories shouldn't be writable or executable,
+      /* If leading directories shouldn't be readable, writable or executable,
         or should have set[ug]id or sticky bits set and we are setting
         their owners, we need to fix their permissions after making them.  */
-      if (((parent_mode & WX_USR) != WX_USR)
+      if (((parent_mode & S_IRWXU) != S_IRWXU)
          || ((owner != (uid_t) -1 || group != (gid_t) -1)
              && (parent_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0))
        {
@@ -175,6 +172,9 @@ make_dir_parents (char const *arg,
 
       while (true)
        {
+         bool dir_known_to_exist;
+         int mkdir_errno;
+
          /* slash points to the leftmost unprocessed component of dir.  */
          basename_dir = slash;
 
@@ -188,13 +188,16 @@ make_dir_parents (char const *arg,
            basename_dir = dir;
 
          *slash = '\0';
-         if (mkdir (basename_dir, tmp_mode) == 0)
+         dir_known_to_exist = (mkdir (basename_dir, tmp_mode) == 0);
+         mkdir_errno = errno;
+
+         if (dir_known_to_exist)
            {
              if (verbose_fmt_string)
                error (0, 0, verbose_fmt_string, quote (dir));
 
              if ((owner != (uid_t) -1 || group != (gid_t) -1)
-                 && chown (basename_dir, owner, group)
+                 && lchown (basename_dir, owner, group)
 #if defined AFS && defined EPERM
                  && errno != EPERM
 #endif
@@ -208,36 +211,43 @@ make_dir_parents (char const *arg,
 
              if (re_protect)
                {
-                 struct ptr_list *new = (struct ptr_list *)
-                   alloca (sizeof *new);
+                 struct ptr_list *new = alloca (sizeof *new);
                  new->dirname_end = slash;
                  new->next = leading_dirs;
                  leading_dirs = new;
                }
            }
-         else if (errno == EEXIST || errno == ENOSYS)
-           {
-             /* A file is already there.  Perhaps it is a directory.
-                If not, it will be diagnosed later.
-
-                The ENOSYS is for Solaris 8 NFS clients, which can
-                fail with errno == ENOSYS if mkdir is invoked on an
-                NFS mount point.  */
-           }
-         else
-           {
-             error (0, errno, _("cannot create directory %s"), quote (dir));
-             retval = false;
-             break;
-           }
 
          /* If we were able to save the initial working directory,
             then we can use chdir to change into each directory before
             creating an entry in that directory.  This avoids making
             mkdir process O(n^2) file name components.  */
-         if (do_chdir && chdir (basename_dir) < 0)
+         if (do_chdir)
+           {
+             /* If we know that basename_dir is a directory (because we've
+                just created it), then ensure that when we change to it,
+                that final component is not a symlink.  Otherwise, we must
+                accept the possibility that basename_dir is a preexisting
+                symlink-to-directory and chdir through the symlink.  */
+             if ((dir_known_to_exist
+                  ? chdir_no_follow (basename_dir)
+                  : chdir (basename_dir)) == 0)
+               dir_known_to_exist = true;
+             else if (dir_known_to_exist)
+               {
+                 error (0, errno, _("cannot chdir to directory %s"),
+                        quote (dir));
+                 retval = false;
+                 break;
+               }
+           }
+         else if (!dir_known_to_exist)
+           dir_known_to_exist = (stat (basename_dir, &stats) == 0
+                                 && S_ISDIR (stats.st_mode));
+
+         if (!dir_known_to_exist)
            {
-             error (0, errno, _("cannot chdir to directory %s"),
+             error (0, mkdir_errno, _("cannot create directory %s"),
                     quote (dir));
              retval = false;
              break;
@@ -261,9 +271,18 @@ make_dir_parents (char const *arg,
         Create the final component of the file name.  */
       if (retval)
        {
-         if (mkdir (basename_dir, mode) != 0)
+         bool dir_known_to_exist = (mkdir (basename_dir, mode) == 0);
+         int mkdir_errno = errno;
+         struct stat sbuf;
+
+         if ( ! dir_known_to_exist)
+           dir_known_to_exist = (stat (basename_dir, &sbuf) == 0
+                                 && S_ISDIR (sbuf.st_mode));
+
+         if ( ! dir_known_to_exist)
            {
-             error (0, errno, _("cannot create directory %s"), quote (dir));
+             error (0, mkdir_errno,
+                    _("cannot create directory %s"), quote (dir));
              retval = false;
            }
          else
@@ -285,7 +304,7 @@ make_dir_parents (char const *arg,
 
       if (owner != (uid_t) -1 || group != (gid_t) -1)
        {
-         if (chown (fixup_permissions_dir, owner, group) != 0
+         if (lchown (fixup_permissions_dir, owner, group) != 0
 #ifdef AFS
              && errno != EPERM
 #endif
@@ -302,7 +321,7 @@ make_dir_parents (char const *arg,
         required to honor only the file permission bits.  In particular,
         it need not honor the `special' bits, so if MODE includes any
         special bits, set them here.  */
-      if ((mode & ~S_IRWXUGO) && chmod (fixup_permissions_dir, mode) != 0)
+      if ((mode & ~S_IRWXUGO) && lchmod (fixup_permissions_dir, mode) != 0)
        {
          error (0, errno, _("cannot change permissions of %s"),
                 quote (full_dir));
@@ -326,7 +345,7 @@ make_dir_parents (char const *arg,
     {
       leading_dirs->dirname_end[0] = '\0';
       if ((cwd_problem && *full_dir != '/')
-         || chmod (full_dir, parent_mode) != 0)
+         || lchmod (full_dir, parent_mode) != 0)
        {
          error (0, (cwd_problem ? 0 : errno),
                 _("cannot change permissions of %s"), quote (full_dir));
Index: lib/openat.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/openat.c,v
retrieving revision 1.7
diff -p -u -r1.7 openat.c
--- lib/openat.c        2 Oct 2005 22:38:45 -0000       1.7
+++ lib/openat.c        9 Jan 2006 23:02:07 -0000
@@ -23,17 +23,15 @@
 
 #include "openat.h"
 
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-
 #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
+#include "fcntl--.h"
+#include "openat-priv.h"
 #include "save-cwd.h"
+#include "unistd--.h"
 
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
+#include <stdarg.h>
+#include <stddef.h>
+#include <errno.h>
 
 /* Replacement for Solaris' openat function.
    <http://www.google.com/search?q=openat+site:docs.sun.com>
@@ -44,11 +42,8 @@
    Otherwise, upon failure, set errno and return -1, as openat does.
    Upon successful completion, return a file descriptor.  */
 int
-rpl_openat (int fd, char const *file, int flags, ...)
+openat (int fd, char const *file, int flags, ...)
 {
-  struct saved_cwd saved_cwd;
-  int saved_errno;
-  int new_fd;
   mode_t mode = 0;
 
   if (flags & O_CREAT)
@@ -56,37 +51,80 @@ rpl_openat (int fd, char const *file, in
       va_list arg;
       va_start (arg, flags);
 
-      /* Assume that mode_t is passed compatibly with mode_t's type
-        after argument promotion.  */
-      mode = va_arg (arg, mode_t);
+      /* If mode_t is narrower than int, use the promoted type (int),
+         not mode_t.  Use sizeof to guess whether mode_t is nerrower;
+         we don't know of any practical counterexamples.  */
+      if (sizeof (mode_t) < sizeof (int))
+       mode = va_arg (arg, int);
+      else
+       mode = va_arg (arg, mode_t);
 
       va_end (arg);
     }
 
+  return openat_permissive (fd, file, flags, mode, NULL);
+}
+
+/* Like openat (FD, FILE, FLAGS, MODE), but if CWD_ERRNO is
+   nonnull, set *CWD_ERRNO to an errno value if unable to save
+   or restore the initial working directory.  This is needed only
+   the first time remove.c's remove_dir opens a command-line
+   directory argument.
+
+   If a previous attempt to restore the current working directory
+   failed, then we must not even try to access a `.'-relative name.
+   It is the caller's responsibility not to call this function
+   in that case.  */
+
+int
+openat_permissive (int fd, char const *file, int flags, mode_t mode,
+                  int *cwd_errno)
+{
+  struct saved_cwd saved_cwd;
+  int saved_errno;
+  int err;
+  bool save_ok;
+
   if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
     return open (file, flags, mode);
 
-  if (save_cwd (&saved_cwd) != 0)
-    openat_save_fail (errno);
+  {
+    char *proc_file;
+    BUILD_PROC_NAME (proc_file, fd, file);
+    err = open (proc_file, flags, mode);
+    /* If the syscall succeeds, or if it fails with an unexpected
+       errno value, then return right away.  Otherwise, fall through
+       and resort to using save_cwd/restore_cwd.  */
+    if (0 <= err || ! EXPECTED_ERRNO (errno))
+      return err;
+  }
 
-  if (fchdir (fd) != 0)
+  save_ok = (save_cwd (&saved_cwd) == 0);
+  if (! save_ok)
     {
-      saved_errno = errno;
-      free_cwd (&saved_cwd);
-      errno = saved_errno;
-      return -1;
+      if (! cwd_errno)
+       openat_save_fail (errno);
+      *cwd_errno = errno;
     }
 
-  new_fd = open (file, flags, mode);
+  err = fchdir (fd);
   saved_errno = errno;
 
-  if (restore_cwd (&saved_cwd) != 0)
-    openat_restore_fail (errno);
+  if (! err)
+    {
+      err = open (file, flags, mode);
+      saved_errno = errno;
+      if (save_ok && restore_cwd (&saved_cwd) != 0)
+       {
+         if (! cwd_errno)
+           openat_restore_fail (errno);
+         *cwd_errno = errno;
+       }
+    }
 
   free_cwd (&saved_cwd);
-
   errno = saved_errno;
-  return new_fd;
+  return err;
 }
 
 #if !HAVE_FDOPENDIR
@@ -110,27 +148,37 @@ fdopendir (int fd)
   int saved_errno;
   DIR *dir;
 
-  if (save_cwd (&saved_cwd) != 0)
-    openat_save_fail (errno);
+  char *proc_file;
+  BUILD_PROC_NAME (proc_file, fd, ".");
+  dir = opendir (proc_file);
+  saved_errno = errno;
 
-  if (fchdir (fd) != 0)
+  /* If the syscall fails with an expected errno value, resort to
+     save_cwd/restore_cwd.  */
+  if (! dir && EXPECTED_ERRNO (saved_errno))
     {
-      saved_errno = errno;
-      free_cwd (&saved_cwd);
-      errno = saved_errno;
-      return NULL;
-    }
+      if (save_cwd (&saved_cwd) != 0)
+       openat_save_fail (errno);
 
-  dir = opendir (".");
-  saved_errno = errno;
+      if (fchdir (fd) != 0)
+       {
+         dir = NULL;
+         saved_errno = errno;
+       }
+      else
+       {
+         dir = opendir (".");
+         saved_errno = errno;
+
+         if (restore_cwd (&saved_cwd) != 0)
+           openat_restore_fail (errno);
+       }
 
-  if (restore_cwd (&saved_cwd) != 0)
-    openat_restore_fail (errno);
+      free_cwd (&saved_cwd);
+    }
 
-  free_cwd (&saved_cwd);
   if (dir)
     close (fd);
-
   errno = saved_errno;
   return dir;
 }
@@ -151,32 +199,42 @@ fstatat (int fd, char const *file, struc
   int saved_errno;
   int err;
 
-  if (fd == AT_FDCWD)
+  if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
     return (flag == AT_SYMLINK_NOFOLLOW
            ? lstat (file, st)
            : stat (file, st));
 
+  {
+    char *proc_file;
+    BUILD_PROC_NAME (proc_file, fd, file);
+    err = (flag == AT_SYMLINK_NOFOLLOW
+          ? lstat (proc_file, st)
+          : stat (proc_file, st));
+    /* If the syscall succeeds, or if it fails with an unexpected
+       errno value, then return right away.  Otherwise, fall through
+       and resort to using save_cwd/restore_cwd.  */
+    if (0 <= err || ! EXPECTED_ERRNO (errno))
+      return err;
+  }
+
   if (save_cwd (&saved_cwd) != 0)
     openat_save_fail (errno);
 
-  if (fchdir (fd) != 0)
+  err = fchdir (fd);
+  saved_errno = errno;
+
+  if (! err)
     {
+      err = (flag == AT_SYMLINK_NOFOLLOW
+            ? lstat (file, st)
+            : stat (file, st));
       saved_errno = errno;
-      free_cwd (&saved_cwd);
-      errno = saved_errno;
-      return -1;
-    }
 
-  err = (flag == AT_SYMLINK_NOFOLLOW
-        ? lstat (file, st)
-        : stat (file, st));
-  saved_errno = errno;
-
-  if (restore_cwd (&saved_cwd) != 0)
-    openat_restore_fail (errno);
+      if (restore_cwd (&saved_cwd) != 0)
+       openat_restore_fail (errno);
+    }
 
   free_cwd (&saved_cwd);
-
   errno = saved_errno;
   return err;
 }
@@ -195,28 +253,36 @@ unlinkat (int fd, char const *file, int 
   int saved_errno;
   int err;
 
-  if (fd == AT_FDCWD)
+  if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
     return (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
 
+  {
+    char *proc_file;
+    BUILD_PROC_NAME (proc_file, fd, file);
+    err = (flag == AT_REMOVEDIR ? rmdir (proc_file) : unlink (proc_file));
+    /* If the syscall succeeds, or if it fails with an unexpected
+       errno value, then return right away.  Otherwise, fall through
+       and resort to using save_cwd/restore_cwd.  */
+    if (0 <= err || ! EXPECTED_ERRNO (errno))
+      return err;
+  }
+
   if (save_cwd (&saved_cwd) != 0)
     openat_save_fail (errno);
 
-  if (fchdir (fd) != 0)
+  err = fchdir (fd);
+  saved_errno = errno;
+
+  if (! err)
     {
+      err = (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
       saved_errno = errno;
-      free_cwd (&saved_cwd);
-      errno = saved_errno;
-      return -1;
-    }
 
-  err = (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
-  saved_errno = errno;
-
-  if (restore_cwd (&saved_cwd) != 0)
-    openat_restore_fail (errno);
+      if (restore_cwd (&saved_cwd) != 0)
+       openat_restore_fail (errno);
+    }
 
   free_cwd (&saved_cwd);
-
   errno = saved_errno;
   return err;
 }
Index: lib/openat.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/openat.h,v
retrieving revision 1.7
diff -p -u -r1.7 openat.h
--- lib/openat.h        2 Oct 2005 22:38:45 -0000       1.7
+++ lib/openat.h        9 Jan 2006 23:02:07 -0000
@@ -23,6 +23,7 @@
 #include <sys/stat.h>
 #include <dirent.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 #ifndef __attribute__
 # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
@@ -35,30 +36,39 @@
 #endif
 
 #ifndef AT_FDCWD
-# define AT_FDCWD (-3041965)           /* same value as Solaris 9 */
-# define AT_SYMLINK_NOFOLLOW 4096      /* same value as Solaris 9 */
-# define AT_REMOVEDIR (0x1)            /* same value as Solaris 9 */
-
-# ifdef __OPENAT_PREFIX
-#  undef openat
-#  define __OPENAT_CONCAT(x, y) x ## y
-#  define __OPENAT_XCONCAT(x, y) __OPENAT_CONCAT (x, y)
-#  define __OPENAT_ID(y) __OPENAT_XCONCAT (__OPENAT_PREFIX, y)
-#  define openat __OPENAT_ID (openat)
+/* Use the same values as Solaris 9.  This shouldn't matter, but
+   there's no real reason to differ.  */
+# define AT_FDCWD (-3041965)
+# define AT_SYMLINK_NOFOLLOW 4096
+# define AT_REMOVEDIR 1
+#endif
+
+#ifdef __OPENAT_PREFIX
+
+# undef openat
+# define __OPENAT_CONCAT(x, y) x ## y
+# define __OPENAT_XCONCAT(x, y) __OPENAT_CONCAT (x, y)
+# define __OPENAT_ID(y) __OPENAT_XCONCAT (__OPENAT_PREFIX, y)
+# define openat __OPENAT_ID (openat)
 int openat (int fd, char const *file, int flags, /* mode_t mode */ ...);
-#  if ! HAVE_FDOPENDIR
-#   define fdopendir __OPENAT_ID (fdopendir)
-#  endif
+int openat_permissive (int fd, char const *file, int flags, mode_t mode,
+                      int *cwd_errno);
+# if ! HAVE_FDOPENDIR
+#  define fdopendir __OPENAT_ID (fdopendir)
+# endif
 DIR *fdopendir (int fd);
-#  define fstatat __OPENAT_ID (fstatat)
+# define fstatat __OPENAT_ID (fstatat)
 int fstatat (int fd, char const *file, struct stat *st, int flag);
-#  define unlinkat __OPENAT_ID (unlinkat)
+# define unlinkat __OPENAT_ID (unlinkat)
 int unlinkat (int fd, char const *file, int flag);
-void openat_restore_fail (int) ATTRIBUTE_NORETURN;
-void openat_save_fail (int) ATTRIBUTE_NORETURN;
-# else
-#  define openat_restore_fail(Errno) /* empty */
-#  define openat_save_fail(Errno) /* empty */
-# endif
+
+#else
+
+# define openat_permissive(Fd, File, Flags, Mode, Cwd_errno) \
+    openat (Fd, File, Flags, Mode)
 
 #endif
+
+int mkdirat (int fd, char const *file, mode_t mode);
+void openat_restore_fail (int) ATTRIBUTE_NORETURN;
+void openat_save_fail (int) ATTRIBUTE_NORETURN;
Index: m4/openat.m4
===================================================================
RCS file: /cvsroot/gnulib/gnulib/m4/openat.m4,v
retrieving revision 1.3
diff -p -u -r1.3 openat.m4
--- m4/openat.m4        2 Oct 2005 22:38:45 -0000       1.3
+++ m4/openat.m4        9 Jan 2006 23:02:07 -0000
@@ -1,4 +1,4 @@
-#serial 6
+#serial 7
 # See if we need to use our replacement for Solaris' openat function.
 
 dnl Copyright (C) 2004, 2005 Free Software Foundation, Inc.
@@ -10,7 +10,12 @@ dnl with or without modifications, as lo
 
 AC_DEFUN([gl_FUNC_OPENAT],
 [
-  AC_LIBSOURCES([openat.c, openat.h, openat-die.c])
+  AC_LIBSOURCES([openat.c, openat.h, openat-priv.h, openat-die.c])
+  AC_LIBSOURCES([mkdirat.c])
+
+  # No system provides a mkdirat function; compile it unconditionally.
+  AC_LIBOBJ([mkdirat])
+
   AC_LIBOBJ([openat-die])
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   AC_CHECK_FUNCS_ONCE([fdopendir])
Index: modules/mkdir-p
===================================================================
RCS file: /cvsroot/gnulib/gnulib/modules/mkdir-p,v
retrieving revision 1.2
diff -p -u -r1.2 mkdir-p
--- modules/mkdir-p     6 Jul 2005 15:58:47 -0000       1.2
+++ modules/mkdir-p     9 Jan 2006 23:02:07 -0000
@@ -2,9 +2,14 @@ Description:
 Ensure that a directory and its parents exist.
 
 Files:
-lib/mkdir-p.h
+lib/chdir-safer.c
+lib/chdir-safer.h
+lib/lchmod.h
 lib/mkdir-p.c
+lib/mkdir-p.h
 m4/afs.m4
+m4/chdir-safer.m4
+m4/lchmod.m4
 m4/mkdir-p.m4
 
 Depends-on:
Index: modules/openat
===================================================================
RCS file: /cvsroot/gnulib/gnulib/modules/openat,v
retrieving revision 1.4
diff -p -u -r1.4 openat
--- modules/openat      22 Sep 2005 23:30:37 -0000      1.4
+++ modules/openat      9 Jan 2006 23:02:07 -0000
@@ -2,9 +2,11 @@ Description:
 Open a file at a directory.
 
 Files:
+lib/mkdirat.c
 lib/openat.c
 lib/openat.h
 lib/openat-die.c
+lib/openat-priv.h
 m4/openat.m4
 
 Depends-on:
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ lib/chdir-safer.c   2006-01-09 14:58:34.000000000 -0800
@@ -0,0 +1,87 @@
+/* much like chdir(2), but safer
+
+   Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+
+   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.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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.  */
+
+/* written by Jim Meyering */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "chdir-safer.h"
+
+#include <stdbool.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef O_DIRECTORY
+# define O_DIRECTORY 0
+#endif
+
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
+#define SAME_INODE(Stat_buf_1, Stat_buf_2) \
+  ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \
+   && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)
+
+/* Like chdir, but fail if DIR is a symbolic link to a directory (or
+   similar funny business), or if DIR is not readable.  This avoids a
+   minor race condition between when a directory is created or statted
+   and when the process chdirs into it.  */
+int
+chdir_no_follow (char const *dir)
+{
+  int result = 0;
+  int saved_errno;
+  int fd = open (dir,
+                O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK);
+  if (fd < 0)
+    return -1;
+
+  /* If open follows symlinks, lstat DIR and fstat FD to ensure that
+     they are the same file; if they are different files, set errno to
+     ELOOP (the same value that open uses for symlinks with
+     O_NOFOLLOW) so the caller can report a failure.  */
+  if (! O_NOFOLLOW)
+    {
+      struct stat sb1;
+      result = lstat (dir, &sb1);
+      if (result == 0)
+       {
+         struct stat sb2;
+         result = fstat (fd, &sb2);
+         if (result == 0 && ! SAME_INODE (sb1, sb2))
+           {
+             errno = ELOOP;
+             result = -1;
+           }
+       }
+    }
+
+  if (result == 0)
+    result = fchdir (fd);
+
+  saved_errno = errno;
+  close (fd);
+  errno = saved_errno;
+  return result;
+}
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ lib/chdir-safer.h   2006-01-09 10:35:05.000000000 -0800
@@ -0,0 +1,21 @@
+/* much like chdir(2), but safer
+
+   Copyright (C) 2005 Free Software Foundation, Inc.
+
+   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.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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.  */
+
+/* Written by Jim Meyering.  */
+
+int chdir_no_follow (char const *file);
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ lib/lchmod.h        2006-01-09 10:31:28.000000000 -0800
@@ -0,0 +1,35 @@
+/* Provide a replacement for lchmod on hosts that lack it.
+
+   Copyright (C) 2005 Free Software Foundation, Inc.
+
+   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.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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.  */
+
+/* Written by Paul Eggert.  */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef HAVE_LCHMOD
+
+/* The lchmod replacement follows symbolic links.  Callers should take
+   this into account; lchmod should be applied only to arguments that
+   are known to not be symbolic links.  On hosts that lack lchmod,
+   this can lead to race conditions between the check and the
+   invocation of lchmod, but we know of no workarounds that are
+   reliable in general.  You might try requesting support for lchmod
+   from your operating system supplier.  */
+
+# define lchmod chmod
+#endif
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ lib/mkdirat.c       2006-01-09 10:37:20.000000000 -0800
@@ -0,0 +1,88 @@
+/* fd-relative mkdir
+   Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+
+   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.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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.  */
+
+/* written by Jim Meyering */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "openat.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
+#include "save-cwd.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+#include "openat-priv.h"
+
+/* Solaris 10 has no function like this.
+   Create a subdirectory, FILE, with mode MODE, in the directory
+   open on descriptor FD.  If possible, do it without changing the
+   working directory.  Otherwise, resort to using save_cwd/fchdir,
+   then mkdir/restore_cwd.  If either the save_cwd or the restore_cwd
+   fails, then give a diagnostic and exit nonzero.  */
+int
+mkdirat (int fd, char const *file, mode_t mode)
+{
+  struct saved_cwd saved_cwd;
+  int saved_errno;
+  int err;
+
+  if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
+    return mkdir (file, mode);
+
+  {
+    char *proc_file;
+    BUILD_PROC_NAME (proc_file, fd, file);
+    err = mkdir (proc_file, mode);
+    /* If the syscall succeeds, or if it fails with an unexpected
+       errno value, then return right away.  Otherwise, fall through
+       and resort to using save_cwd/restore_cwd.  */
+    if (0 <= err || ! EXPECTED_ERRNO (errno))
+      return err;
+  }
+
+  if (save_cwd (&saved_cwd) != 0)
+    openat_save_fail (errno);
+
+  if (fchdir (fd) != 0)
+    {
+      saved_errno = errno;
+      free_cwd (&saved_cwd);
+      errno = saved_errno;
+      return -1;
+    }
+
+  err = mkdir (file, mode);
+  saved_errno = (err < 0 ? errno : 0);
+
+  if (restore_cwd (&saved_cwd) != 0)
+    openat_restore_fail (errno);
+
+  free_cwd (&saved_cwd);
+
+  if (saved_errno)
+    errno = saved_errno;
+  return err;
+}
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ lib/openat-priv.h   2006-01-09 10:37:05.000000000 -0800
@@ -0,0 +1,52 @@
+/* macros used by openat-like functions
+   Copyright (C) 2005 Free Software Foundation, Inc.
+
+   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.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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.  */
+
+/* written by Jim Meyering */
+
+#include <stdio.h>
+#include <string.h>
+#include "alloca.h"
+#include "intprops.h"
+
+/* Set PROC_FD_FILENAME to the expansion of "/proc/self/fd/%d/%s" in
+   alloca'd memory, using FD and FILE, respectively for %d and %s. */
+#define BUILD_PROC_NAME(Proc_fd_filename, Fd, File)                    \
+  do                                                                   \
+    {                                                                  \
+      size_t filelen = strlen (File);                                  \
+      static const char procfd[] = "/proc/self/fd/%d/%s";              \
+      /* Buffer for the file name we are going to use.  It consists of \
+        - the string /proc/self/fd/                                    \
+        - the file descriptor number                                   \
+        - the file name provided.                                      \
+        The final NUL is included in the sizeof.                       \
+        Subtract 4 to account for %d and %s.  */                       \
+      size_t buflen = sizeof (procfd) - 4 + INT_STRLEN_BOUND (Fd) + filelen; \
+      (Proc_fd_filename) = alloca (buflen);                            \
+      snprintf ((Proc_fd_filename), buflen, procfd, (Fd), (File));     \
+    }                                                                  \
+  while (0)
+
+/* Trying to access a BUILD_PROC_NAME file will fail on systems without
+   /proc support, and even on systems *with* ProcFS support.  Return
+   nonzero if the failure may be legitimate, e.g., because /proc is not
+   readable, or the particular .../fd/N directory is not present.  */
+#define EXPECTED_ERRNO(Errno)                  \
+  ((Errno) == ENOTDIR || (Errno) == ENOENT     \
+   || (Errno) == EPERM || (Errno) == EACCES    \
+   || (Errno) == ENOSYS /* Solaris 8 */                \
+   || (Errno) == EOPNOTSUPP /* FreeBSD */)
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ m4/lchmod.m4        2006-01-09 10:32:02.000000000 -0800
@@ -0,0 +1,15 @@
+#serial 1
+
+dnl Copyright (C) 2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+dnl Provide a replacement for lchmod on hosts that lack it.
+
+AC_DEFUN([gl_FUNC_LCHMOD],
+[
+  AC_LIBSOURCES([lchmod.h])
+  AC_CHECK_FUNCS_ONCE([lchmod])
+])




reply via email to

[Prev in Thread] Current Thread [Next in Thread]