commit: 69683e73c1845797ec0e57c5e1f2ccfef76965ca
parent 5649c59141e9f117d371187e8819c9e9ebf77b3d
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Sat, 24 May 2025 21:29:39 +0200
cmd/chown: Add support for --reference
Diffstat:
3 files changed, 88 insertions(+), 3 deletions(-)
diff --git a/cmd/chown.1 b/cmd/chown.1
@@ -13,11 +13,21 @@
.Op Fl h | Fl R Op Fl HLP
.Ar owner Ns Op : Ns Ar group
.Ar file...
+.Nm chown
+.Op Fl v
+.Op Fl h | Fl R Op Fl HLP
+.Fl -reference Ar ref_file
+.Ar file...
.Nm chgrp
.Op Fl v
.Op Fl h | Fl R Op Fl HLP
.Ar group
.Ar file...
+.Nm chgrp
+.Op Fl v
+.Op Fl h | Fl R Op Fl HLP
+.Fl -reference Ar ref_file
+.Ar file...
.Sh DESCRIPTION
.Nm chown
sets the user ID given by
@@ -38,7 +48,16 @@ The
and
.Ar group
arguments can be either numeric IDs, or names.
-Ownership refers to both user and group.
+Unless specified, ownership refers to both user and group.
+.Pp
+If
+.Fl -reference
+is used instead
+.Nm chown
+sets both user and group ownership
+and
+.Nm chmod
+sets only group ownership.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl h
@@ -66,6 +85,11 @@ is also specified,
change ownership of symbolic links without dereferencing.
.It Fl R
Recurse into directories.
+.It Fl -reference Ar ref_file
+Copy ownership from
+.Ar ref_file
+into each
+.Ar file .
.It Fl v
Verbose, print a message about each processed file, whether a change was made or not.
.El
@@ -81,6 +105,8 @@ specification.
.Pp
The
.Fl v
-option is an extension, also present in GNU coreutils.
+and
+.Fl -reference
+options are extensions, also present in GNU coreutils.
.Sh AUTHORS
.An Haelwenn (lanodan) Monnier Aq Mt contact+utils@hacktivis.me
diff --git a/cmd/chown.c b/cmd/chown.c
@@ -204,15 +204,19 @@ int
main(int argc, char *argv[])
{
argv0 = static_basename(argv[0]);
+ char *ref_file = NULL;
enum chown_follow_symlinks follow_symlinks = CHOWN_FOLLOW_UNK_SYMLINKS;
#ifdef HAS_GETOPT_LONG
- // Strictly for GNUisms compatibility so no long-only options
// clang-format off
+ enum long_opt_vals {
+ OPT_REFERENCE = 1,
+ };
static struct option opts[] = {
{"no-dereference", no_argument, 0, 'h'},
{"recursive", no_argument, 0, 'R'},
+ {"reference", required_argument, 0, OPT_REFERENCE},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0},
};
@@ -244,6 +248,11 @@ main(int argc, char *argv[])
case 'v':
opt_v = true;
break;
+#ifdef HAS_GETOPT_LONG
+ case OPT_REFERENCE: // GNU & NetBSD
+ ref_file = optarg;
+ break;
+#endif
case ':':
fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
usage();
@@ -263,6 +272,51 @@ main(int argc, char *argv[])
if(follow_symlinks == CHOWN_FOLLOW_UNK_SYMLINKS)
follow_symlinks = opt_R ? CHOWN_FOLLOW_NO_SYMLINK : CHOWN_FOLLOW_ALL_SYMLINKS;
+ if(ref_file != NULL)
+ {
+ if(argc < 1)
+ {
+ fprintf(stderr, "%s: error: Expects >=1 arguments, %d given\n", argv0, argc);
+ usage();
+ return 1;
+ }
+
+ struct stat ref_stat;
+ if(stat(ref_file, &ref_stat) != 0)
+ {
+ fprintf(stderr,
+ "%s: error: Failed to get status from reference file '%s': %s\n",
+ argv0,
+ ref_file,
+ strerror(errno));
+ return 1;
+ }
+
+ if(strcmp(argv0, "chown") == 0)
+ {
+ user = ref_stat.st_uid;
+ group = ref_stat.st_gid;
+ }
+ else if(strcmp(argv0, "chgrp") == 0)
+ {
+ group = ref_stat.st_gid;
+ }
+ else
+ {
+ fprintf(
+ stderr, "%s: error: Unknown utility '%s' expected either chown or chgrp\n", argv0, argv0);
+ return 1;
+ }
+
+ for(int i = 0; i < argc; i++)
+ {
+ int ret = do_fchownat(AT_FDCWD, argv[i], argv[i], follow_symlinks);
+ if(ret != 0) return ret;
+ }
+
+ return 0;
+ }
+
if(argc < 2)
{
fprintf(stderr, "%s: error: Expects >=2 arguments, %d given\n", argv0, argc);
diff --git a/test-cmd/chown.t b/test-cmd/chown.t
@@ -59,5 +59,10 @@
chown: Ownership already set to \d+:\d+ for 'grp_unchanged' (re)
$ rm grp_unchanged
+ $ touch ref_file ref_unchanged
+ $ chown -v --reference ref_file ref_unchanged
+ chown: Ownership already set to \d+:\d+ for 'ref_unchanged' (re)
+ $ rm ref_file ref_unchanged
+
$ find .
.