** 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
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
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.
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
Do not print error messages about files whose group cannot be
changed.
+@chownFromOption{chgrp}
+
@item --dereference
@opindex --dereference
@cindex symbolic links, changing owner
src/chgrp.c
src/chmod.c
src/chown-core.c
+src/chown-core.h
src/chown.c
src/chroot.c
src/cksum.c
#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"
enum
{
DEREFERENCE_OPTION = CHAR_MAX + 1,
+ FROM_OPTION,
NO_PRESERVE_ROOT,
PRESERVE_ROOT,
REFERENCE_FILE_OPTION
{"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},
(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\
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, "");
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;
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);
}
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 */
(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\
--- /dev/null
+#!/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
# 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.
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 \