]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
mkdir: don't assume umask equals POSIX default ACL mask
authorPaul Eggert <eggert@cs.ucla.edu>
Sun, 12 May 2013 02:17:10 +0000 (19:17 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Sun, 12 May 2013 02:22:54 +0000 (19:22 -0700)
This fixes Bug#14371, reported by Killer Bassist.
* NEWS: Document this.
* src/mkdir.c (struct mkdir_options): Remove member ancestor_mode.
New member umask_value.  All uses changed.
* src/mkdir.c (make_ancestor): Fix umask assumption.
* src/mkdir.c, src/mkfifo.c, src/mknod.c (main):
Leave umask alone.  This requires invoking lchmod after creating
the file, which introduces a race condition, but this can't be
avoided on hosts with "POSIX" default ACLs, and there's no easy
way with network file systems to tell what kind of host the
directory is on.
* tests/local.mk (all_tests): Add tests/mkdir/p-acl.sh.
* tests/mkdir/p-acl.sh: New file.

NEWS
src/mkdir.c
src/mkfifo.c
src/mknod.c
tests/local.mk
tests/mkdir/p-acl.sh [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index ae6251de26d6c74ae135d4a5d1e2b7c054afc039..eec93dfd086e4ce1c4189189529ee0af14435d24 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,11 @@ GNU coreutils NEWS                                    -*- outline -*-
   the relative link on the dereferenced path of an existing link.
   [This bug was introduced when --relative was added in coreutils-8.16.]
 
+  mkdir, mkfifo, and mknod now work better when creating a file in a directory
+  with a default ACL whose umask disagrees with the process's umask, on a
+  system such as GNU/Linux where directory ACL umasks override process umasks.
+  [bug introduced in coreutils-6.0]
+
   tail --retry -f now waits for the files specified to appear.  Before, tail
   would immediately exit when such a file is inaccessible during the initial
   open.
index a94f96e14d2d7c4772b224e665f25b787b5cd756..b36237a3370fbce2df099bd098795ec48af78dca 100644 (file)
@@ -81,8 +81,8 @@ struct mkdir_options
      made.  */
   int (*make_ancestor_function) (char const *, char const *, void *);
 
-  /* Mode for ancestor directory.  */
-  mode_t ancestor_mode;
+  /* Umask value in effect.  */
+  mode_t umask_value;
 
   /* Mode for directory itself.  */
   mode_t mode;
@@ -112,10 +112,21 @@ static int
 make_ancestor (char const *dir, char const *component, void *options)
 {
   struct mkdir_options const *o = options;
-  int r = mkdir (component, o->ancestor_mode);
+  int r;
+  mode_t user_wx = S_IWUSR | S_IXUSR;
+  bool self_denying_umask = (o->umask_value & user_wx) != 0;
+  if (self_denying_umask)
+    umask (o->umask_value & ~user_wx);
+  r = mkdir (component, S_IRWXUGO);
+  if (self_denying_umask)
+    {
+      int mkdir_errno = errno;
+      umask (o->umask_value);
+      errno = mkdir_errno;
+    }
   if (r == 0)
     {
-      r = ! (o->ancestor_mode & S_IRUSR);
+      r = (o->umask_value & S_IRUSR) != 0;
       announce_mkdir (dir, options);
     }
   return r;
@@ -191,8 +202,8 @@ main (int argc, char **argv)
   if (options.make_ancestor_function || specified_mode)
     {
       mode_t umask_value = umask (0);
-
-      options.ancestor_mode = (S_IRWXUGO & ~umask_value) | (S_IWUSR | S_IXUSR);
+      umask (umask_value);
+      options.umask_value = umask_value;
 
       if (specified_mode)
         {
@@ -205,7 +216,7 @@ main (int argc, char **argv)
           free (change);
         }
       else
-        options.mode = S_IRWXUGO & ~umask_value;
+        options.mode = S_IRWXUGO;
     }
 
   exit (savewd_process_files (argc - optind, argv + optind,
index 76291e5bc251a0269cf6e1983c83622bf98d2a6c..78ff909cc355e79be767d3c0c4d266050f40f9e8 100644 (file)
@@ -116,10 +116,13 @@ main (int argc, char **argv)
   newmode = MODE_RW_UGO;
   if (specified_mode)
     {
+      mode_t umask_value;
       struct mode_change *change = mode_compile (specified_mode);
       if (!change)
         error (EXIT_FAILURE, 0, _("invalid mode"));
-      newmode = mode_adjust (newmode, false, umask (0), change, NULL);
+      umask_value = umask (0);
+      umask (umask_value);
+      newmode = mode_adjust (newmode, false, umask_value, change, NULL);
       free (change);
       if (newmode & ~S_IRWXUGO)
         error (EXIT_FAILURE, 0,
@@ -132,6 +135,12 @@ main (int argc, char **argv)
         error (0, errno, _("cannot create fifo %s"), quote (argv[optind]));
         exit_status = EXIT_FAILURE;
       }
+    else if (specified_mode && lchmod (argv[optind], newmode) != 0)
+      {
+        error (0, errno, _("cannot set permissions of `%s'"),
+               quote (argv[optind]));
+        exit_status = EXIT_FAILURE;
+      }
 
   exit (exit_status);
 }
index 7cfc708d30c369684ce8c599af89659a3e0ef210..a384ad35cd6c0462b1b53074288717af439a839e 100644 (file)
@@ -122,10 +122,13 @@ main (int argc, char **argv)
   newmode = MODE_RW_UGO;
   if (specified_mode)
     {
+      mode_t umask_value;
       struct mode_change *change = mode_compile (specified_mode);
       if (!change)
         error (EXIT_FAILURE, 0, _("invalid mode"));
-      newmode = mode_adjust (newmode, false, umask (0), change, NULL);
+      umask_value = umask (0);
+      umask (umask_value);
+      newmode = mode_adjust (newmode, false, umask_value, change, NULL);
       free (change);
       if (newmode & ~S_IRWXUGO)
         error (EXIT_FAILURE, 0,
@@ -226,5 +229,9 @@ main (int argc, char **argv)
       usage (EXIT_FAILURE);
     }
 
+  if (specified_mode && lchmod (argv[optind], newmode) != 0)
+    error (EXIT_FAILURE, errno, _("cannot set permissions of `%s'"),
+           quote (argv[optind]));
+
   exit (EXIT_SUCCESS);
 }
index fb5cc63b6938b82a1d47feca3de48460090dd57a..5ec7d9859aacbe3f8896b289c5fcfa027b5629e4 100644 (file)
@@ -555,6 +555,7 @@ all_tests =                                 \
   tests/mkdir/p-1.sh                           \
   tests/mkdir/p-2.sh                           \
   tests/mkdir/p-3.sh                           \
+  tests/mkdir/p-acl.sh                         \
   tests/mkdir/p-slashdot.sh                    \
   tests/mkdir/p-thru-slink.sh                  \
   tests/mkdir/p-v.sh                           \
diff --git a/tests/mkdir/p-acl.sh b/tests/mkdir/p-acl.sh
new file mode 100755 (executable)
index 0000000..f1be628
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+# Test "mkdir -p" with default ACLs.
+
+# Copyright (C) 1997-2013 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 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
+# 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, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ mkdir
+
+require_setfacl_
+
+mkdir d || framework_failure_
+setfacl -d -m group::rwx d || framework_failure_
+umask 077
+
+mkdir --parents d/e || fail=1
+ls_l=$(ls -ld d/e) || fail=1
+case $ls_l in
+  d???rw[sx]*) ;;
+  *) fail=1 ;;
+esac
+
+Exit $fail