bug-gnulib
[Top][All Lists]
Advanced

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

import from coreutils to gnulib for mkdir-p and mkancesdirs


From: Paul Eggert
Subject: import from coreutils to gnulib for mkdir-p and mkancesdirs
Date: Sun, 16 Jul 2006 23:10:19 -0700
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/21.4 (gnu/linux)

I installed the following into gnulib, imported from coreutils.

These changes turn the following files into orphans:

lib/chdir-safer.c
lib/chdir-safer.h
m4/chdir-safer.m4
m4/afs.m4

If nobody is using them I'd like to remove them from gnulib.

2006-07-16  Paul Eggert  <address@hidden>

        Import from coreutils.

        * MODULES.html.sh: Add mkancestors.
        * modules/mkancesdirs: New module.
        * modules/mkdir-p (Files): Remove lib/chdir-safer.c, lib/chdir-safer.h,
        lib/same-inode.h, m4/afs.m4, m4/chdir-safer.m4.
        The chdir-safer and afs files are now orphans; I'll remove them
        unless someone speaks up.
        Add lib/dirchownmod.c, lib/dirchownmod.h.
        (Depends-on): Remove alloca, chown, save-cwd, dirname.
        Add lchown, mkancesdirs.
        (Maintainer): Add self.

        * lib/dirchownmod.c, lib/dirchownmod.h:
        * lib/mkancesdirs.c, lib/mkancesdirs.h: New files.
        * lib/mkdir-p.c: Don't include alloca.h, stdio.h, sys/types.h,
        unistd.h, string.h, chdir-safer.h, dirname.h, lchmod.h, lchown.h,
        save-cwd.h.  Instead, include dirchownmod.h and mkancesdirs.h.
        (make_dir_parents): New args MAKE_ANCESTOR, OPTIONS, ANNOUNCE,
        MODE_BITS.  Remove options VERBOSE_FMT_STRING, CWD_ERRNO.  All
        callers changed.  Revamp internals significantly, by not
        attempting to create directories that are temporarily more
        permissive than the final results.  Do not attempt to use
        save_cwd/restore_cwd; it isn't worth it for mkdir and install.
        This removes some race conditions, fixes some bugs, and simplifies
        things.  Use new dirchownmod function to do owner and mode changes.
        * lib/mkdir-p.h: Likewise.
        * lib/modechange.c (octal_to_mode): New function.
        (struct mode_change): New member mentioned.
        (make_node_op_equals): New arg mentioned.  All callers changed.
        (mode_compile): Keep track of which mode bits the user has explicitly
        mentioned.
        (mode_adjust): New arg DIR, so that we implement the X op correctly.
        New arg PMODE_BITS, to keep track of which mode bits the user
        mentioned; it treats S_ISUID and S_ISGID speciall.
        All callers changed.
        * lib/modechange.h: Likewise.

        * mkancesdirs.m4: New file.
        * mkdir-p.m4 (gl_MKDIR_PARENTS): Mention dirchownmod.c, dirchownmod.h.
        Don't require AC_FUNC_ALLOCA, gl_AFS, gl_CHDIR_SAFER; no longer needed.
        Require gl_FUNC_LCHOWN, since dirchownmod.c needs it.

Index: MODULES.html.sh
===================================================================
RCS file: /cvsroot/gnulib/gnulib/MODULES.html.sh,v
retrieving revision 1.123
diff -p -u -r1.123 MODULES.html.sh
--- MODULES.html.sh     12 Jul 2006 15:19:16 -0000      1.123
+++ MODULES.html.sh     17 Jul 2006 05:56:51 -0000
@@ -1869,6 +1869,7 @@ func_all_modules ()
   func_module fts-lgpl
   func_module isdir
   func_module lchown
+  func_module mkancestors
   func_module mkdir-p
   func_module modechange
   func_module mountlist
Index: lib/mkdir-p.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/mkdir-p.c,v
retrieving revision 1.5
diff -p -u -r1.5 mkdir-p.c
--- lib/mkdir-p.c       9 Jan 2006 23:13:56 -0000       1.5
+++ lib/mkdir-p.c       17 Jul 2006 05:56:52 -0000
@@ -17,7 +17,7 @@
    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 David MacKenzie <address@hidden> and Jim Meyering.  */
+/* Written by Paul Eggert, David MacKenzie, and Jim Meyering.  */
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
@@ -25,333 +25,112 @@
 
 #include "mkdir-p.h"
 
-#include <alloca.h>
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <stdlib.h>
 #include <errno.h>
-#include <string.h>
+#include <sys/stat.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-#include "chdir-safer.h"
-#include "dirname.h"
+#include "dirchownmod.c"
 #include "error.h"
-#include "lchmod.h"
-#include "lchown.h"
 #include "quote.h"
-#include "save-cwd.h"
+#include "mkancesdirs.h"
 #include "stat-macros.h"
 
-/* Ensure that the directory ARG exists.
+/* Ensure that the directory DIR exists.
 
-   Create any leading directories that don't already exist, with
-   permissions PARENT_MODE.
-   If the last element of ARG does not exist, create it as
-   a new directory with permissions MODE.
-   If OWNER and GROUP are non-negative, use them to set the UID and GID of
-   any created directories.
-   If VERBOSE_FMT_STRING is nonzero, use it as a printf format
-   string for printing a message after successfully making a directory,
-   with the name of the directory that was just made as an argument.
-   If PRESERVE_EXISTING is true and ARG is an existing directory,
-   then do not attempt to set its permissions and ownership.
-
-   Set *CWD_ERRNO to a (nonzero) error number if this
-   function has changed the current working directory and is unable to
-   restore it to its initial state.  Do not change
-   *CWD_ERRNO otherwise.
-
-   Return true iff ARG exists as a directory with the proper ownership
-   and permissions when done.  Note that this function returns true
-   even when it fails to return to the initial working directory.  */
+   If MAKE_ANCESTOR is not null, create any ancestor directories that
+   don't already exist, by invoking MAKE_ANCESTOR (ANCESTOR, OPTIONS).
+   This function should return zero if successful, -1 (setting errno)
+   otherwise.  In this case, DIR may be modified by storing '\0' bytes
+   into it, to access the ancestor directories, and this modification
+   is retained on return if the ancestor directories could not be
+   created.
+
+   Create DIR as a new directory with using mkdir with permissions
+   MODE.  It is also OK if MAKE_ANCESTOR_DIR is not null and a
+   directory DIR already exists.
+
+   Call ANNOUNCE (DIR, OPTIONS) just after successfully making DIR,
+   even if some of the following actions fail.
+
+   Set DIR's owner to OWNER and group to GROUP, but leave the owner
+   alone if OWNER is (uid_t) -1, and similarly for GROUP.
+
+   Set DIR's mode bits to MODE, except preserve any of the bits that
+   correspond to zero bits in MODE_BITS.  In other words, MODE_BITS is
+   a mask that specifies which of DIR's mode bits should be set or
+   cleared.  MODE should be a subset of MODE_BITS, which in turn
+   should be a subset of CHMOD_MODE_BITS.  Changing the mode in this
+   way is necessary if DIR already existed or if MODE and MODE_BITS
+   specify non-permissions bits like S_ISUID.
+
+   However, if PRESERVE_EXISTING is true and DIR already exists,
+   do not attempt to set DIR's ownership and file mode bits.
+
+   This implementation assumes the current umask is zero.
+
+   Return true if DIR exists as a directory with the proper ownership
+   and file mode bits when done.  Report a diagnostic and return false
+   on failure, storing '\0' into *DIR if an ancestor directory had
+   problems.  */
 
 bool
-make_dir_parents (char const *arg,
+make_dir_parents (char *dir,
+                 int (*make_ancestor) (char const *, void *),
+                 void *options,
                  mode_t mode,
-                 mode_t parent_mode,
+                 void (*announce) (char const *, void *),
+                 mode_t mode_bits,
                  uid_t owner,
                  gid_t group,
-                 bool preserve_existing,
-                 char const *verbose_fmt_string,
-                 int *cwd_errno)
+                 bool preserve_existing)
 {
-  struct stat stats;
-  bool retval = true;
-  bool do_chdir = false;       /* Whether to chdir before each mkdir.  */
-  struct saved_cwd cwd;
-  bool cwd_problem = false;
-  char const *fixup_permissions_dir = NULL;
-  char const *full_dir = arg;
-
-  struct ptr_list
-  {
-    char *dirname_end;
-    struct ptr_list *next;
-  };
-  struct ptr_list *leading_dirs = NULL;
-
-  if (stat (arg, &stats) == 0)
-    {
-      if (! S_ISDIR (stats.st_mode))
-       {
-         error (0, 0, _("%s exists but is not a directory"), quote (arg));
-         return false;
-       }
+  bool made_dir = (mkdir (dir, mode) == 0);
 
-      if (!preserve_existing)
-       fixup_permissions_dir = arg;
-    }
-  else if (errno != ENOENT || !*arg)
+  if (!made_dir && make_ancestor && errno == ENOENT)
     {
-      error (0, errno, "%s", quote (arg));
-      return false;
-    }
-  else
-    {
-      char *slash;
-      mode_t tmp_mode;         /* Initial perms for leading dirs.  */
-      bool re_protect;         /* Should leading dirs be unwritable? */
-      char *basename_dir;
-      char *dir;
-
-      /* Temporarily relax umask in case it's overly restrictive.  */
-      mode_t oldmask = umask (0);
-
-      /* Make a copy of ARG that we can scribble NULs on.  */
-      dir = alloca (strlen (arg) + 1);
-      strcpy (dir, arg);
-      strip_trailing_slashes (dir);
-      full_dir = dir;
-
-      /* 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 & S_IRWXU) != S_IRWXU)
-         || ((owner != (uid_t) -1 || group != (gid_t) -1)
-             && (parent_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0))
-       {
-         tmp_mode = S_IRWXU;
-         re_protect = true;
-       }
+      if (mkancesdirs (dir, make_ancestor, options) == 0)
+       made_dir = (mkdir (dir, mode) == 0);
       else
        {
-         tmp_mode = parent_mode;
-         re_protect = false;
-       }
-
-      /* If we can record the current working directory, we may be able
-        to do the chdir optimization.  */
-      do_chdir = (save_cwd (&cwd) == 0);
-
-      /* If we've saved the cwd and DIR is an absolute file name,
-        we must chdir to `/' in order to enable the chdir optimization.
-         So if chdir ("/") fails, turn off the optimization.  */
-      if (do_chdir && dir[0] == '/')
-       {
-         /* POSIX says "//" might be special, so chdir to "//" if the
-            file name starts with exactly two slashes.  */
-         char const *root = "//" + (dir[1] != '/' || dir[2] == '/');
-         if (chdir (root) != 0)
-           {
-             free_cwd (&cwd);
-             do_chdir = false;
-           }
-       }
-
-      slash = dir;
-
-      /* Skip over leading slashes.  */
-      while (*slash == '/')
-       slash++;
-
-      while (true)
-       {
-         bool dir_known_to_exist;
-         int mkdir_errno;
-
-         /* slash points to the leftmost unprocessed component of dir.  */
-         basename_dir = slash;
-
-         slash = strchr (slash, '/');
-         if (slash == NULL)
-           break;
-
-         /* If we're *not* doing chdir before each mkdir, then we have to refer
-            to the target using the full (multi-component) directory name.  */
-         if (!do_chdir)
-           basename_dir = dir;
-
-         *slash = '\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)
-                 && lchown (basename_dir, owner, group)
-#if defined AFS && defined EPERM
-                 && errno != EPERM
-#endif
-                 )
-               {
-                 error (0, errno, _("cannot change owner and/or group of %s"),
-                        quote (dir));
-                 retval = false;
-                 break;
-               }
-
-             if (re_protect)
-               {
-                 struct ptr_list *new = alloca (sizeof *new);
-                 new->dirname_end = slash;
-                 new->next = leading_dirs;
-                 leading_dirs = new;
-               }
-           }
-
-         /* 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)
-           {
-             /* 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, mkdir_errno, _("cannot create directory %s"),
-                    quote (dir));
-             retval = false;
-             break;
-           }
-
-         *slash++ = '/';
-
-         /* Avoid unnecessary calls to mkdir when given
-            file names containing multiple adjacent slashes.  */
-         while (*slash == '/')
-           slash++;
-       }
-
-      if (!do_chdir)
-       basename_dir = dir;
-
-      /* Done creating leading directories.  Restore original umask.  */
-      umask (oldmask);
-
-      /* We're done making leading directories.
-        Create the final component of the file name.  */
-      if (retval)
-       {
-         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, mkdir_errno,
-                    _("cannot create directory %s"), quote (dir));
-             retval = false;
-           }
-         else
-           {
-             if (verbose_fmt_string)
-               error (0, 0, verbose_fmt_string, quote (dir));
-             fixup_permissions_dir = basename_dir;
-           }
+         /* mkancestdirs updated DIR for a better-looking
+            diagnostic, so don't try to stat DIR below.  */
+         make_ancestor = NULL;
        }
     }
 
-  if (fixup_permissions_dir)
+  if (made_dir)
     {
-      /* chown must precede chmod because on some systems,
-        chown clears the set[ug]id bits for non-superusers,
-        resulting in incorrect permissions.
-        On System V, users can give away files with chown and then not
-        be able to chmod them.  So don't give files away.  */
-
-      if (owner != (uid_t) -1 || group != (gid_t) -1)
-       {
-         if (lchown (fixup_permissions_dir, owner, group) != 0
-#ifdef AFS
-             && errno != EPERM
-#endif
-             )
-           {
-             error (0, errno, _("cannot change owner and/or group of %s"),
-                    quote (full_dir));
-             retval = false;
-           }
-       }
-
-      /* The above chown may have turned off some permission bits in MODE.
-        Another reason we may have to use chmod here is that mkdir(2) is
-        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) && lchmod (fixup_permissions_dir, mode) != 0)
-       {
-         error (0, errno, _("cannot change permissions of %s"),
-                quote (full_dir));
-         retval = false;
-       }
+      announce (dir, options);
+      preserve_existing =
+       (owner == (uid_t) -1 && group == (gid_t) -1
+        && ! ((mode_bits & (S_ISUID | S_ISGID)) | (mode & S_ISVTX)));
     }
-
-  if (do_chdir)
+  else
     {
-      if (restore_cwd (&cwd) != 0)
+      int mkdir_errno = errno;
+      struct stat st;
+      if (! (make_ancestor && mkdir_errno != ENOENT
+            && stat (dir, &st) == 0 && S_ISDIR (st.st_mode)))
        {
-         *cwd_errno = errno;
-         cwd_problem = true;
+         error (0, mkdir_errno, _("cannot create directory %s"), quote (dir));
+         return false;
        }
-      free_cwd (&cwd);
     }
 
-  /* If the mode for leading directories didn't include owner "wx"
-     privileges, reset their protections to the correct value.  */
-  for (; leading_dirs != NULL; leading_dirs = leading_dirs->next)
-    {
-      leading_dirs->dirname_end[0] = '\0';
-      if ((cwd_problem && *full_dir != '/')
-         || lchmod (full_dir, parent_mode) != 0)
-       {
-         error (0, (cwd_problem ? 0 : errno),
-                _("cannot change permissions of %s"), quote (full_dir));
-         retval = false;
-       }
+  if (! preserve_existing
+      && (dirchownmod (dir, (made_dir ? mode : (mode_t) -1),
+                      owner, group, mode, mode_bits)
+         != 0))
+    {
+      error (0, errno,
+            _(owner == (uid_t) -1 && group == (gid_t) -1
+              ? "cannot change permissions of %s"
+              : "cannot change owner and permissions of %s"),
+            quote (dir));
+      return false;
     }
 
-  return retval;
+  return true;
 }
Index: lib/mkdir-p.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/mkdir-p.h,v
retrieving revision 1.2
diff -p -u -r1.2 mkdir-p.h
--- lib/mkdir-p.h       22 Sep 2005 23:24:03 -0000      1.2
+++ lib/mkdir-p.h       17 Jul 2006 05:56:52 -0000
@@ -17,16 +17,17 @@
    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 David MacKenzie <address@hidden> and Jim Meyering.  */
+/* Written by Paul Eggert, David MacKenzie, and Jim Meyering.  */
 
 #include <stdbool.h>
 #include <sys/types.h>
 
-bool make_dir_parents (char const *argname,
+bool make_dir_parents (char *dir,
+                      int (*make_ancestor) (char const *, void *),
+                      void *options,
                       mode_t mode,
-                      mode_t parent_mode,
+                      void (*announce) (char const *, void *),
+                      mode_t mode_bits,
                       uid_t owner,
                       gid_t group,
-                      bool preserve_existing,
-                      char const *verbose_fmt_string,
-                      int *cwd_errno);
+                      bool preserve_existing);
Index: lib/modechange.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/modechange.c,v
retrieving revision 1.32
diff -p -u -r1.32 modechange.c
--- lib/modechange.c    9 Jan 2006 23:13:56 -0000       1.32
+++ lib/modechange.c    17 Jul 2006 05:56:52 -0000
@@ -1,7 +1,7 @@
 /* modechange.c -- file mode manipulation
 
-   Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001, 2003, 2004, 2005
-   Free Software Foundation, Inc.
+   Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001, 2003, 2004, 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
@@ -51,6 +51,32 @@
 #define XOTH 00001
 #define ALLM 07777 /* all octal mode bits */
 
+/* Convert OCTAL, which uses one of the traditional octal values, to
+   an internal mode_t value.  */
+static mode_t
+octal_to_mode (unsigned int octal)
+{
+  /* Help the compiler optimize the usual case where mode_t uses
+     the traditional octal representation.  */
+  return ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
+          && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
+          && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
+          && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
+         ? octal
+         : (mode_t) ((octal & SUID ? S_ISUID : 0)
+                     | (octal & SGID ? S_ISGID : 0)
+                     | (octal & SVTX ? S_ISVTX : 0)
+                     | (octal & RUSR ? S_IRUSR : 0)
+                     | (octal & WUSR ? S_IWUSR : 0)
+                     | (octal & XUSR ? S_IXUSR : 0)
+                     | (octal & RGRP ? S_IRGRP : 0)
+                     | (octal & WGRP ? S_IWGRP : 0)
+                     | (octal & XGRP ? S_IXGRP : 0)
+                     | (octal & ROTH ? S_IROTH : 0)
+                     | (octal & WOTH ? S_IWOTH : 0)
+                     | (octal & XOTH ? S_IXOTH : 0)));
+}
+
 /* Special operations flags.  */
 enum
   {
@@ -78,19 +104,22 @@ struct mode_change
   char flag;                   /* Special operations flag.  */
   mode_t affected;             /* Set for u, g, o, or a.  */
   mode_t value;                        /* Bits to add/remove.  */
+  mode_t mentioned;            /* Bits explicitly mentioned.  */
 };
 
 /* Return a mode_change array with the specified `=ddd'-style
-   mode change operation, where NEW_MODE is `ddd'.  */
+   mode change operation, where NEW_MODE is `ddd' and MENTIONED
+   contains the bits explicitly mentioned in the mode are MENTIONED.  */
 
 static struct mode_change *
-make_node_op_equals (mode_t new_mode)
+make_node_op_equals (mode_t new_mode, mode_t mentioned)
 {
   struct mode_change *p = xmalloc (2 * sizeof *p);
   p->op = '=';
   p->flag = MODE_ORDINARY_CHANGE;
   p->affected = CHMOD_MODE_BITS;
   p->value = new_mode;
+  p->mentioned = mentioned;
   p[1].flag = MODE_DONE;
   return p;
 }
@@ -113,13 +142,14 @@ mode_compile (char const *mode_string)
 
   if ('0' <= *mode_string && *mode_string < '8')
     {
-      mode_t mode;
-      unsigned int octal_value = 0;
+      unsigned int octal_mode = 0;
+      unsigned int octal_mentioned = 0;
 
       do
        {
-         octal_value = 8 * octal_value + *mode_string++ - '0';
-         if (ALLM < octal_value)
+         octal_mode = 8 * octal_mode + *mode_string++ - '0';
+         octal_mentioned = 8 * octal_mentioned + 7;
+         if (ALLM < octal_mode)
            return NULL;
        }
       while ('0' <= *mode_string && *mode_string < '8');
@@ -127,27 +157,8 @@ mode_compile (char const *mode_string)
       if (*mode_string)
        return NULL;
 
-      /* Help the compiler optimize the usual case where mode_t uses
-        the traditional octal representation.  */
-      mode = ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
-              && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
-              && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
-              && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
-             ? octal_value
-             : (mode_t) ((octal_value & SUID ? S_ISUID : 0)
-                         | (octal_value & SGID ? S_ISGID : 0)
-                         | (octal_value & SVTX ? S_ISVTX : 0)
-                         | (octal_value & RUSR ? S_IRUSR : 0)
-                         | (octal_value & WUSR ? S_IWUSR : 0)
-                         | (octal_value & XUSR ? S_IXUSR : 0)
-                         | (octal_value & RGRP ? S_IRGRP : 0)
-                         | (octal_value & WGRP ? S_IWGRP : 0)
-                         | (octal_value & XGRP ? S_IXGRP : 0)
-                         | (octal_value & ROTH ? S_IROTH : 0)
-                         | (octal_value & WOTH ? S_IWOTH : 0)
-                         | (octal_value & XOTH ? S_IXOTH : 0)));
-
-      return make_node_op_equals (mode);
+      return make_node_op_equals (octal_to_mode (octal_mode),
+                                 octal_to_mode (octal_mentioned & ALLM));
     }
 
   /* Allocate enough space to hold the result.  */
@@ -251,6 +262,7 @@ mode_compile (char const *mode_string)
          change->flag = flag;
          change->affected = affected;
          change->value = value;
+         change->mentioned = (affected ? affected & value : value);
        }
       while (*mode_string == '=' || *mode_string == '+'
             || *mode_string == '-');
@@ -280,25 +292,36 @@ mode_create_from_ref (const char *ref_fi
 
   if (stat (ref_file, &ref_stats) != 0)
     return NULL;
-  return make_node_op_equals (ref_stats.st_mode);
+  return make_node_op_equals (ref_stats.st_mode, CHMOD_MODE_BITS);
 }
 
-/* Return file mode OLDMODE, adjusted as indicated by the list of change
-   operations CHANGES, which are interpreted assuming the umask is
-   UMASK_VALUE.  If OLDMODE is a directory, the type `X'
-   change affects it even if no execute bits were set in OLDMODE.
-   The returned value has the S_IFMT bits cleared.  */
+/* Return the file mode bits of OLDMODE (which is the mode of a
+   directory if DIR), assuming the umask is UMASK_VALUE, adjusted as
+   indicated by the list of change operations CHANGES.  If DIR, the
+   type 'X' change affects the returned value even if no execute bits
+   were set in OLDMODE.  If PMODE_BITS is not null, store into
+   *PMODE_BITS a mask denoting file mode bits that are affected by
+   CHANGES.
+
+   The returned value and *PMODE_BITS contain only file mode bits.
+   For example, they have the S_IFMT bits cleared on a standard
+   Unix-like host.  */
 
 mode_t
-mode_adjust (mode_t oldmode, struct mode_change const *changes,
-            mode_t umask_value)
+mode_adjust (mode_t oldmode, bool dir, mode_t umask_value,
+            struct mode_change const *changes, mode_t *pmode_bits)
 {
   /* The adjusted mode.  */
   mode_t newmode = oldmode & CHMOD_MODE_BITS;
 
+  /* File mode bits that CHANGES cares about.  */
+  mode_t mode_bits = 0;
+
   for (; changes->flag != MODE_DONE; changes++)
     {
       mode_t affected = changes->affected;
+      mode_t omit_change =
+       (dir ? S_ISUID | S_ISGID : 0) & ~ changes->mentioned;
       mode_t value = changes->value;
 
       switch (changes->flag)
@@ -322,14 +345,15 @@ mode_adjust (mode_t oldmode, struct mode
        case MODE_X_IF_ANY_X:
          /* Affect the execute bits if execute bits are already set
             or if the file is a directory.  */
-         if ((newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) || S_ISDIR (oldmode))
+         if ((newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) | dir)
            value |= S_IXUSR | S_IXGRP | S_IXOTH;
          break;
        }
 
       /* If WHO was specified, limit the change to the affected bits.
-        Otherwise, apply the umask.  */
-      value &= (affected ? affected : ~umask_value);
+        Otherwise, apply the umask.  Either way, omit changes as
+        requested.  */
+      value &= (affected ? affected : ~umask_value) & ~ omit_change;
 
       switch (changes->op)
        {
@@ -337,17 +361,26 @@ mode_adjust (mode_t oldmode, struct mode
          /* If WHO was specified, preserve the previous values of
             bits that are not affected by this change operation.
             Otherwise, clear all the bits.  */
-         newmode = (affected ? newmode & ~affected : 0);
-         /* Fall through.  */
+         {
+           mode_t preserved = (affected ? ~affected : 0) | omit_change;
+           mode_bits |= CHMOD_MODE_BITS & ~preserved;
+           newmode = (newmode & preserved) | value;
+           break;
+         }
+
        case '+':
+         mode_bits |= value;
          newmode |= value;
          break;
 
        case '-':
+         mode_bits |= value;
          newmode &= ~value;
          break;
        }
     }
 
+  if (pmode_bits)
+    *pmode_bits = mode_bits;
   return newmode;
 }
Index: lib/modechange.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/modechange.h,v
retrieving revision 1.15
diff -p -u -r1.15 modechange.h
--- lib/modechange.h    14 May 2005 06:03:58 -0000      1.15
+++ lib/modechange.h    17 Jul 2006 05:56:52 -0000
@@ -1,7 +1,7 @@
 /* modechange.h -- definitions for file mode manipulation
 
-   Copyright (C) 1989, 1990, 1997, 2003, 2004, 2005 Free Software
-   Foundation, Inc.
+   Copyright (C) 1989, 1990, 1997, 2003, 2004, 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
@@ -22,10 +22,12 @@
 #if ! defined MODECHANGE_H_
 # define MODECHANGE_H_
 
+# include <stdbool.h>
 # include <sys/types.h>
 
 struct mode_change *mode_compile (const char *);
 struct mode_change *mode_create_from_ref (const char *);
-mode_t mode_adjust (mode_t, struct mode_change const *, mode_t);
+mode_t mode_adjust (mode_t, bool, mode_t, struct mode_change const *,
+                   mode_t *);
 
 #endif
Index: lib/userspec.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/userspec.c,v
retrieving revision 1.51
diff -p -u -r1.51 userspec.c
--- lib/userspec.c      6 Jul 2006 21:51:32 -0000       1.51
+++ lib/userspec.c      17 Jul 2006 05:56:52 -0000
@@ -1,5 +1,5 @@
 /* userspec.c -- Parse a user and group string.
-   Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2005 Free Software
+   Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2006 Free Software
    Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
@@ -109,8 +109,7 @@ parse_with_separator (char const *spec, 
 {
   static const char *E_invalid_user = N_("invalid user");
   static const char *E_invalid_group = N_("invalid group");
-  static const char *E_bad_spec =
-    N_("cannot get the login group of a numeric UID");
+  static const char *E_bad_spec = N_("invalid spec");
 
   const char *error_msg;
   struct passwd *pwd;
@@ -164,7 +163,11 @@ parse_with_separator (char const *spec, 
        {
          bool use_login_group = (separator != NULL && g == NULL);
          if (use_login_group)
-           error_msg = E_bad_spec;
+           {
+             /* If there is no group,
+                then there may not be a trailing ":", either.  */
+             error_msg = E_bad_spec;
+           }
          else
            {
              unsigned long int tmp;
Index: m4/mkdir-p.m4
===================================================================
RCS file: /cvsroot/gnulib/gnulib/m4/mkdir-p.m4,v
retrieving revision 1.3
diff -p -u -r1.3 mkdir-p.m4
--- m4/mkdir-p.m4       1 Mar 2006 00:11:04 -0000       1.3
+++ m4/mkdir-p.m4       17 Jul 2006 05:56:52 -0000
@@ -1,4 +1,4 @@
-# mkdir-p.m4 serial 10
+# mkdir-p.m4 serial 11
 dnl Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -6,12 +6,11 @@ dnl with or without modifications, as lo
 
 AC_DEFUN([gl_MKDIR_PARENTS],
 [
-  AC_LIBSOURCES([mkdir-p.c, mkdir-p.h])
+  AC_LIBSOURCES([dirchownmod.c, dirchownmod.h, mkdir-p.c, mkdir-p.h])
+  AC_LIBOBJ([dirchownmod])
   AC_LIBOBJ([mkdir-p])
 
-  dnl Prerequisites of lib/mkdir-p.c.
-  AC_REQUIRE([AC_FUNC_ALLOCA])
-  AC_REQUIRE([gl_AFS])
+  dnl Prerequisites of lib/dirchownmod.c.
   AC_REQUIRE([gl_FUNC_LCHMOD])
-  AC_REQUIRE([gl_CHDIR_SAFER])
+  AC_REQUIRE([gl_FUNC_LCHOWN])
 ])
Index: modules/mkdir-p
===================================================================
RCS file: /cvsroot/gnulib/gnulib/modules/mkdir-p,v
retrieving revision 1.4
diff -p -u -r1.4 mkdir-p
--- modules/mkdir-p     3 Jul 2006 08:32:46 -0000       1.4
+++ modules/mkdir-p     17 Jul 2006 05:56:52 -0000
@@ -2,24 +2,19 @@ Description:
 Ensure that a directory and its parents exist.
 
 Files:
-lib/chdir-safer.c
-lib/chdir-safer.h
+lib/dirchownmod.c
+lib/dirchownmod.h
 lib/lchmod.h
 lib/mkdir-p.c
 lib/mkdir-p.h
-lib/same-inode.h
-m4/afs.m4
-m4/chdir-safer.m4
 m4/lchmod.m4
 m4/mkdir-p.m4
 
 Depends-on:
-alloca
-chown
-gettext-h
-save-cwd
-dirname
 error
+gettext-h
+lchown
+mkancesdirs
 quote
 stat-macros
 stdbool
@@ -36,4 +31,4 @@ License:
 GPL
 
 Maintainer:
-Jim Meyering
+Paul Eggert, Jim Meyering
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ lib/dirchownmod.c   2006-07-16 20:56:49.000000000 -0700
@@ -0,0 +1,159 @@
+/* Change the ownership and mode bits of a directory.
+
+   Copyright (C) 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 Paul Eggert.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "dirchownmod.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "lchmod.h"
+#include "stat-macros.h"
+
+#ifndef O_DIRECTORY
+# define O_DIRECTORY 0
+#endif
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
+/* Change the ownership and mode bits of the directory DIR.
+
+   If MKDIR_MODE is not (mode_t) -1, mkdir (DIR, MKDIR_MODE) has just
+   been executed successfully with umask zero, so DIR should be a
+   directory (not a symbolic link).
+
+   First, set the file's owner to OWNER and group to GROUP, but leave
+   the owner alone if OWNER is (uid_t) -1, and similarly for GROUP.
+
+   Then, set the file's mode bits to MODE, except preserve any of the
+   bits that correspond to zero bits in MODE_BITS.  In other words,
+   MODE_BITS is a mask that specifies which of the file's mode bits
+   should be set or cleared.  MODE should be a subset of MODE_BITS,
+   which in turn should be a subset of CHMOD_MODE_BITS.
+
+   This implementation assumes the current umask is zero.
+
+   Return 0 if successful, -1 (setting errno) otherwise.  Unsuccessful
+   calls may do the chown but not the chmod.  */
+
+int
+dirchownmod (char const *dir, mode_t mkdir_mode,
+            uid_t owner, gid_t group,
+            mode_t mode, mode_t mode_bits)
+{
+  struct stat st;
+  int result;
+
+  /* Manipulate DIR via a file descriptor if possible, to avoid some races.  */
+  int open_flags = O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
+  int fd = open (dir, open_flags);
+
+  /* Fail if the directory is unreadable, the directory previously
+     existed or was created without read permission.  Otherwise, get
+     the file's status.  */
+  if (0 <= fd)
+    result = fstat (fd, &st);
+  else if (errno != EACCES
+          || (mkdir_mode != (mode_t) -1 && mkdir_mode & S_IRUSR))
+    return fd;
+  else
+    result = stat (dir, &st);
+
+  if (result == 0)
+    {
+      mode_t dir_mode = st.st_mode;
+
+      /* Check whether DIR is a directory.  If FD is nonnegative, this
+        check avoids changing the ownership and mode bits of the
+        wrong file in many cases.  This doesn't fix all the race
+        conditions, but it is better than nothing.  */
+      if (! S_ISDIR (dir_mode))
+       {
+         errno = ENOTDIR;
+         result = -1;
+       }
+      else
+       {
+         /* If at least one of the S_IXUGO bits are set, chown might
+            clear the S_ISUID and S_SGID bits.  Keep track of any
+            file mode bits whose values are indeterminate due to this
+            issue.  */
+         mode_t indeterminate = 0;
+
+         /* On some systems, chown clears S_ISUID and S_ISGID, so do
+            chown before chmod.  On older System V hosts, ordinary
+            users can give their files away via chown; don't worry
+            about that here, since users shouldn't do that.  */
+
+         if ((owner != (uid_t) -1 && owner != st.st_uid)
+             || (group != (gid_t) -1 && group != st.st_gid))
+           {
+             result = (0 <= fd
+                       ? fchown (fd, owner, group)
+                       : mkdir_mode != (mode_t) -1
+                       ? lchown (dir, owner, group)
+                       : chown (dir, owner, group));
+
+             /* Either the user cares about an indeterminate bit and
+                it'll be set properly by chmod below, or the user
+                doesn't care and it's OK to use the bit's pre-chown
+                value.  So there's no need to re-stat DIR here.  */
+
+             if (result == 0 && (dir_mode & S_IXUGO))
+               indeterminate = dir_mode & (S_ISUID | S_ISGID);
+           }
+
+         /* If the file mode bits might not be right, use chmod to
+            change them.  Don't change bits the user doesn't care
+            about.  */
+         if (result == 0 && (((dir_mode ^ mode) | indeterminate) & mode_bits))
+           {
+             mode_t chmod_mode =
+               mode | (dir_mode & CHMOD_MODE_BITS & ~mode_bits);
+             result = (0 <= fd
+                       ? fchmod (fd, chmod_mode)
+                       : mkdir_mode != (mode_t) -1
+                       ? lchmod (dir, chmod_mode)
+                       : chmod (dir, chmod_mode));
+           }
+       }
+    }
+
+  if (0 <= fd)
+    {
+      if (result == 0)
+       result = close (fd);
+      else
+       {
+         int e = errno;
+         close (fd);
+         errno = e;
+       }
+    }
+
+  return result;
+}
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ lib/dirchownmod.h   2006-07-16 20:56:49.000000000 -0700
@@ -0,0 +1,2 @@
+#include <sys/types.h>
+int dirchownmod (char const *, mode_t, uid_t, gid_t, mode_t, mode_t);
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ lib/mkancesdirs.c   2006-07-16 20:56:41.000000000 -0700
@@ -0,0 +1,132 @@
+/* Make a file's ancestor directories.
+
+   Copyright (C) 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 Paul Eggert.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "mkancesdirs.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "dirname.h"
+#include "stat-macros.h"
+
+/* Return 0 if FILE is a directory, otherwise -1 (setting errno).  */
+
+static int
+test_dir (char const *file)
+{
+  struct stat st;
+  if (stat (file, &st) == 0)
+    {
+      if (S_ISDIR (st.st_mode))
+       return 0;
+      errno = ENOTDIR;
+    }
+  return -1;
+}
+
+/* Ensure that the ancestor directories of FILE exist, using an
+   algorithm that should work even if two processes execute this
+   function in parallel.  Temporarily modify FILE by storing '\0'
+   bytes into it, to access the ancestor directories.
+
+   Create any ancestor directories that don't already exist, by
+   invoking MAKE_DIR (ANCESTOR, MAKE_DIR_ARG).  This function should
+   return zero if successful, -1 (setting errno) otherwise.
+
+   If successful, return 0 with FILE set back to its original value;
+   otherwise, return -1 (setting errno), storing a '\0' into *FILE so
+   that it names the ancestor directory that had problems.  */
+
+int
+mkancesdirs (char *file,
+            int (*make_dir) (char const *, void *),
+            void *make_dir_arg)
+{
+  /* This algorithm is O(N**2) but in typical practice the fancier
+     O(N) algorithms are slower.  */
+
+  /* Address of the previous directory separator that follows an
+     ordinary byte in a file name in the left-to-right scan, or NULL
+     if no such separator precedes the current location P.  */
+  char *sep = NULL;
+
+  char const *prefix_end = file + FILE_SYSTEM_PREFIX_LEN (file);
+  char *p;
+  char c;
+
+  /* Search backward through FILE using mkdir to create the
+     furthest-away ancestor that is needed.  This loop isn't needed
+     for correctness, but typically ancestors already exist so this
+     loop speeds things up a bit.
+
+     This loop runs a bit faster if errno initially contains an error
+     number corresponding to a failed access to FILE.  However, things
+     work correctly regardless of errno's initial value.  */
+
+  for (p = last_component (file); prefix_end < p; p--)
+    if (ISSLASH (*p) && ! ISSLASH (p[-1]))
+      {
+       *p = '\0';
+
+       if (errno == ENOENT && make_dir (file, make_dir_arg) == 0)
+         {
+           *p = '/';
+           break;
+         }
+
+       if (errno != ENOENT)
+         {
+           if (test_dir (file) == 0)
+             {
+               *p = '/';
+               break;
+             }
+           if (errno != ENOENT)
+             return -1;
+         }
+
+       *p = '/';
+      }
+
+  /* Scan forward through FILE, creating directories along the way.
+     Try mkdir before stat, so that the procedure works even when two
+     or more processes are executing it in parallel.  */
+
+  while ((c = *p++))
+    if (ISSLASH (*p))
+      {
+       if (! ISSLASH (c))
+         sep = p;
+      }
+    else if (ISSLASH (c) && *p && sep)
+      {
+       *sep = '\0';
+       if (make_dir (file, make_dir_arg) != 0 && test_dir (file) != 0)
+         return -1;
+       *sep = '/';
+      }
+
+
+  return 0;
+}
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ lib/mkancesdirs.h   2006-07-16 20:56:41.000000000 -0700
@@ -0,0 +1 @@
+int mkancesdirs (char *, int (*) (char const *, void *), void *);
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ m4/mkancesdirs.m4   2006-07-16 20:56:34.000000000 -0700
@@ -0,0 +1,11 @@
+# Make a file's ancestor directories.
+dnl Copyright (C) 2006 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.
+
+AC_DEFUN([gl_MKANCESDIRS],
+[
+  AC_LIBSOURCES([mkancesdirs.c, mkancesdirs.h])
+  AC_LIBOBJ([mkancesdirs])
+])
--- /dev/null   2005-09-24 22:00:15.000000000 -0700
+++ modules/mkancesdirs 2006-07-16 21:18:14.000000000 -0700
@@ -0,0 +1,25 @@
+Description:
+Ensure the existence of the ancestor directories of a file.
+
+Files:
+lib/mkancesdirs.c
+lib/mkancesdirs.h
+m4/mkancesdirs.m4
+
+Depends-on:
+dirname
+stat-macros
+
+configure.ac:
+gl_MKANCESDIRS
+
+Makefile.am:
+
+Include:
+"mkancesdirs.h"
+
+License:
+GPL
+
+Maintainer:
+Paul Eggert, Jim Meyering




reply via email to

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