From da091b3ab33a31f1354b8ab42915faf15c3c4745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Wed, 27 Sep 2023 20:32:06 +0100 Subject: [PATCH] chgrp: add --from parameter similar to chown * 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 | 3 +++ doc/coreutils.texi | 15 ++++++++---- po/POTFILES.in | 1 + src/chgrp.c | 22 +++++++++++++++++- src/chown-core.h | 12 ++++++++++ src/chown.c | 8 +------ tests/chgrp/from.sh | 54 ++++++++++++++++++++++++++++++++++++++++++++ tests/chown/basic.sh | 2 +- tests/local.mk | 1 + 9 files changed, 104 insertions(+), 14 deletions(-) create mode 100755 tests/chgrp/from.sh diff --git a/NEWS b/NEWS index 0823f0d9f..a4fd77073 100644 --- 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 diff --git a/doc/coreutils.texi b/doc/coreutils.texi index d445ea228..1f8b356d1 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -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 diff --git a/po/POTFILES.in b/po/POTFILES.in index 3029d1e3b..751af7cc6 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 diff --git a/src/chgrp.c b/src/chgrp.c index 6ec3b4d99..51fc10782 100644 --- a/src/chgrp.c +++ b/src/chgrp.c @@ -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); } diff --git a/src/chown-core.h b/src/chown-core.h index 8afdc0b1e..23cb89435 100644 --- a/src/chown-core.h +++ b/src/chown-core.h @@ -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 */ diff --git a/src/chown.c b/src/chown.c index 4e0e1c3fc..b2cb973b0 100644 --- a/src/chown.c +++ b/src/chown.c @@ -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 index 000000000..2ddb85889 --- /dev/null +++ b/tests/chgrp/from.sh @@ -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 . + +. "${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 diff --git a/tests/chown/basic.sh b/tests/chown/basic.sh index 2db9ef715..0565ba472 100755 --- a/tests/chown/basic.sh +++ b/tests/chown/basic.sh @@ -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. diff --git a/tests/local.mk b/tests/local.mk index bf03238c3..57fe023de 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -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 \ -- 2.41.0