]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
chgrp: add --from parameter similar to chown
authorPádraig Brady <P@draigBrady.com>
Wed, 27 Sep 2023 19:32:06 +0000 (20:32 +0100)
committerPádraig Brady <P@draigBrady.com>
Fri, 15 Dec 2023 13:19:27 +0000 (13:19 +0000)
* doc/coreutils.texi (chown invocation): Convert --from option
description to a macro and call from ...
(chgrp description): ... here.
* src/chown-core.h (emit_from_option_description): A new function
refactored from ...
* src/chown.c (usage): ... here, and called from ...
* src/chgrp.c (usage): ... here.
(main): Accept the --from option as chown(1) does.
* po/POTFILES.in: Add chown-core.h as now translated.
* tests/chown/basic.sh: Decouple the root user from id 0.
* tests/chgrp/from.sh: A new test largely based on chown/basic.sh.
* tests/local.mk: Reference the new test.
* NEWS: Mention the new feature.
Suggested by Ed Neville.

NEWS
doc/coreutils.texi
po/POTFILES.in
src/chgrp.c
src/chown-core.h
src/chown.c
tests/chgrp/from.sh [new file with mode: 0755]
tests/chown/basic.sh
tests/local.mk

diff --git a/NEWS b/NEWS
index 0823f0d9f5cb8807f23c5a12f0ca33cbcab2983f..a4fd77073bdd205870ba503be7a39bb0393e20c9 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -45,6 +45,9 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** New features
 
+  chgrp now accepts the --from=OWNER:GROUP option to restrict changes to files
+  with matching current OWNER and/or GROUP, as already supported by chown(1).
+
   tail now supports following multiple processes, with repeated --pid options.
 
 ** Improvements
index d445ea228071afeb70bcf62c484da794a3336be1..1f8b356d12191be25ca532e1bd4698859365ce34 100644 (file)
@@ -11601,6 +11601,7 @@ actually changes.
 Do not print error messages about files whose ownership cannot be
 changed.
 
+@macro chownFromOption{cmd}
 @item --from=@var{old-owner}
 @opindex --from
 @cindex symbolic links, changing owner
@@ -11613,17 +11614,17 @@ For example, to reflect a user ID numbering change for one user's files
 without an option like this, @code{root} might run
 
 @example
-find / -owner OLDUSER -print0 | xargs -0 chown -h NEWUSER
+find / -owner OLDUSER -print0 | xargs -0 \cmd\ -h NEWUSER
 @end example
 
 But that is dangerous because the interval between when the @command{find}
-tests the existing file's owner and when the @command{chown} is actually run
+tests the existing file's owner and when the @command{\cmd\} is actually run
 may be quite large.
-One way to narrow the gap would be to invoke chown for each file
+One way to narrow the gap would be to invoke \cmd\ for each file
 as it is found:
 
 @example
-find / -owner OLDUSER -exec chown -h NEWUSER @{@} \;
+find / -owner OLDUSER -exec \cmd\ -h NEWUSER @{@} \\;
 @end example
 
 But that is very slow if there are many affected files.
@@ -11631,8 +11632,10 @@ With this option, it is safer (the gap is narrower still)
 though still not perfect:
 
 @example
-chown -h -R --from=OLDUSER NEWUSER /
+\cmd\ -h -R --from=OLDUSER NEWUSER /
 @end example
+@end macro
+@chownFromOption{chown}
 
 @item --dereference
 @opindex --dereference
@@ -11766,6 +11769,8 @@ changes.
 Do not print error messages about files whose group cannot be
 changed.
 
+@chownFromOption{chgrp}
+
 @item --dereference
 @opindex --dereference
 @cindex symbolic links, changing owner
index 3029d1e3bc9e326db80b072f399501b3643f3e83..751af7cc62b4ab1ace6bbee949fb0841a1475304 100644 (file)
@@ -41,6 +41,7 @@ src/chcon.c
 src/chgrp.c
 src/chmod.c
 src/chown-core.c
+src/chown-core.h
 src/chown.c
 src/chroot.c
 src/cksum.c
index 6ec3b4d998c8112e6c531242b0705458aa248bed..51fc10782cc3dac2b6284082f83820a4a0320645 100644 (file)
@@ -28,6 +28,7 @@
 #include "quote.h"
 #include "root-dev-ino.h"
 #include "xstrtol.h"
+#include "userspec.h"
 
 /* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "chgrp"
@@ -49,6 +50,7 @@ static char *reference_file;
 enum
 {
   DEREFERENCE_OPTION = CHAR_MAX + 1,
+  FROM_OPTION,
   NO_PRESERVE_ROOT,
   PRESERVE_ROOT,
   REFERENCE_FILE_OPTION
@@ -59,6 +61,7 @@ static struct option const long_options[] =
   {"recursive", no_argument, nullptr, 'R'},
   {"changes", no_argument, nullptr, 'c'},
   {"dereference", no_argument, nullptr, DEREFERENCE_OPTION},
+  {"from", required_argument, nullptr, FROM_OPTION},
   {"no-dereference", no_argument, nullptr, 'h'},
   {"no-preserve-root", no_argument, nullptr, NO_PRESERVE_ROOT},
   {"preserve-root", no_argument, nullptr, PRESERVE_ROOT},
@@ -129,6 +132,7 @@ With --reference, change the group of each FILE to that of RFILE.\n\
                          (useful only on systems that can change the\n\
                          ownership of a symlink)\n\
 "), stdout);
+      emit_from_option_description (false);
       fputs (_("\
       --no-preserve-root  do not treat '/' specially (the default)\n\
       --preserve-root    fail to operate recursively on '/'\n\
@@ -184,6 +188,11 @@ main (int argc, char **argv)
   bool ok;
   int optc;
 
+    /* Change the group of a file only if it has this uid/gid.
+     * -1 means there's no restriction.  */
+  uid_t required_uid = -1;
+  gid_t required_gid = -1;
+
   initialize_main (&argc, &argv);
   set_program_name (argv[0]);
   setlocale (LC_ALL, "");
@@ -232,6 +241,17 @@ main (int argc, char **argv)
           reference_file = optarg;
           break;
 
+        case FROM_OPTION:
+          {
+            bool warn;
+            char const *e = parse_user_spec_warn (optarg,
+                                                  &required_uid, &required_gid,
+                                                  nullptr, nullptr, &warn);
+            if (e)
+              error (warn ? 0 : EXIT_FAILURE, 0, "%s: %s", e, quote (optarg));
+            break;
+          }
+
         case 'R':
           chopt.recurse = true;
           break;
@@ -309,7 +329,7 @@ main (int argc, char **argv)
   bit_flags |= FTS_DEFER_STAT;
   ok = chown_files (argv + optind, bit_flags,
                     (uid_t) -1, gid,
-                    (uid_t) -1, (gid_t) -1, &chopt);
+                    required_uid, required_gid, &chopt);
 
   main_exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
 }
index 8afdc0b1e52889ef5546ff52de685c5c4bf4451e..23cb89435033e72fa96d6d76947d83a931260a45 100644 (file)
@@ -89,4 +89,16 @@ chown_files (char **files, int bit_flags,
              struct Chown_option const *chopt)
   _GL_ATTRIBUTE_NONNULL ();
 
+static inline void
+emit_from_option_description (bool user)
+{
+  printf (_("\
+      --from=CURRENT_OWNER:CURRENT_GROUP\n\
+                         change the %sgroup of each file only if\n\
+                         its current owner and/or group match those specified\n\
+                         here.  Either may be omitted, in which case a match\n\
+                         is not required for the omitted attribute\n\
+"), user ? "owner and/or " : "");
+}
+
 #endif /* CHOWN_CORE_H */
index 4e0e1c3fc0916913a4b43f2d6c5beb73e2fb0e0b..b2cb973b0d4490e70738bd536b8cc0e68cef3666 100644 (file)
@@ -99,13 +99,7 @@ With --reference, change the owner and group of each FILE to those of RFILE.\n\
                          (useful only on systems that can change the\n\
                          ownership of a symlink)\n\
 "), stdout);
-      fputs (_("\
-      --from=CURRENT_OWNER:CURRENT_GROUP\n\
-                         change the owner and/or group of each file only if\n\
-                         its current owner and/or group match those specified\n\
-                         here.  Either may be omitted, in which case a match\n\
-                         is not required for the omitted attribute\n\
-"), stdout);
+      emit_from_option_description (true);
       fputs (_("\
       --no-preserve-root  do not treat '/' specially (the default)\n\
       --preserve-root    fail to operate recursively on '/'\n\
diff --git a/tests/chgrp/from.sh b/tests/chgrp/from.sh
new file mode 100755 (executable)
index 0000000..2ddb858
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+# make sure chgrp --from=... works
+
+# Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ chgrp
+require_root_
+
+touch f || framework_failure_
+
+chgrp -R --preserve-root 1 f
+
+# Make sure the owner and group are 0 and 1 respectively.
+set _ $(ls -n f); shift; test ":$4" = ':1' || fail=1
+
+# Make sure the correct diagnostic is output
+# Note we output a name even though an id was specified.
+chgrp -v --from=42 43 f > out || fail=1
+printf "group of 'f' retained as $(id -nu 1)\n" > exp
+compare exp out || fail=1
+
+chgrp --from=:1 010 f || fail=1
+
+# And now they should be 10
+set _ $(ls -n f); shift; test ":$4" = ':10' || fail=1
+
+ln -s f slink
+# Applying chgrp to a symlink with --no-dereference
+# should change only the link.
+chgrp --no-dereference 1 slink || fail=1
+# owner/group on the symlink should be set
+set _ $(ls -n slink); shift; test ":$4" = ':1' || fail=1
+# owner/group on the referent should remain unchanged
+set _ $(ls -n f);     shift; test ":$4" = ':10' || fail=1
+
+chgrp --no-dereference --from=:1 010 slink || fail=1
+# owner/group on the symlink should be changed
+set _ $(ls -n slink); shift; test ":$4" = ':10' || fail=1
+
+Exit $fail
index 2db9ef715092a6b3b559e7f47041cd096c08df22..0565ba472999ea949f9ad31682d9321227046d84 100755 (executable)
@@ -30,7 +30,7 @@ set _ $(ls -n f); shift; test "$3:$4" = 0:1 || fail=1
 # Make sure the correct diagnostic is output
 # Note we output a name even though an id was specified.
 chown -v --from=42 43 f > out || fail=1
-printf "ownership of 'f' retained as $(id -nu)\n" > exp
+printf "ownership of 'f' retained as $(id -nu 0)\n" > exp
 compare exp out || fail=1
 
 # Ensure diagnostics work for non existent files.
index bf03238c3f95411698a50009c98d439bcdfc6068..57fe023de199ad89b05251af53cc2ac76ca8371f 100644 (file)
@@ -108,6 +108,7 @@ EXTRA_DIST +=                       \
 
 all_root_tests =                               \
   tests/chown/basic.sh                         \
+  tests/chgrp/from.sh                          \
   tests/cp/cp-a-selinux.sh                     \
   tests/cp/preserve-gid.sh                     \
   tests/cp/special-bits.sh                     \