Standardise chmod, chflags, chown and chgrp recursive symlink processing

chmod, chflags, chgrp, chmod and chown now affect symlinks in -R mode as
defined in symlink(7); previously symlinks were silently ignored.

Differential Revision:	https://reviews.freebsd.org/D2316
Reviewed by:	jilles
MFC after:	1 month
Relnotes:	yes
Sponsored by:	Multiplay
This commit is contained in:
smh 2015-04-29 00:49:00 +00:00
parent 0991d74d31
commit 091e83ecee
8 changed files with 162 additions and 140 deletions

View File

@ -31,6 +31,10 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 11.x IS SLOW:
disable the most expensive debugging functionality run disable the most expensive debugging functionality run
"ln -s 'abort:false,junk:false' /etc/malloc.conf".) "ln -s 'abort:false,junk:false' /etc/malloc.conf".)
20150523:
chmod, chflags, chown and chgrp now affect symlinks in -R mode as
defined in symlink(7); previously symlinks were silently ignored.
20150415: 20150415:
The const qualifier has been removed from iconv(3) to comply with The const qualifier has been removed from iconv(3) to comply with
POSIX. The ports tree is aware of this from r384038 onwards. POSIX. The ports tree is aware of this from r384038 onwards.

View File

@ -32,7 +32,7 @@
.\" @(#)chflags.1 8.4 (Berkeley) 5/2/95 .\" @(#)chflags.1 8.4 (Berkeley) 5/2/95
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd April 8, 2013 .Dd April 20, 2015
.Dt CHFLAGS 1 .Dt CHFLAGS 1
.Os .Os
.Sh NAME .Sh NAME
@ -66,8 +66,9 @@ nor modify the exit status to reflect such failures.
.It Fl H .It Fl H
If the If the
.Fl R .Fl R
option is specified, symbolic links on the command line are followed. option is specified, symbolic links on the command line are followed
(Symbolic links encountered in the tree traversal are not followed.) and hence unaffected by the command.
(Symbolic links encountered during traversal are not followed.)
.It Fl h .It Fl h
If the If the
.Ar file .Ar file
@ -83,8 +84,12 @@ If the
option is specified, no symbolic links are followed. option is specified, no symbolic links are followed.
This is the default. This is the default.
.It Fl R .It Fl R
Change the file flags for the file hierarchies rooted Change the file flags of the file hierarchies rooted in the files,
in the files instead of just the files themselves. instead of just the files themselves.
Beware of unintentionally matching the
.Dq Pa ".."
hard link to the parent directory when using wildcards like
.Dq Li ".*" .
.It Fl v .It Fl v
Cause Cause
.Nm .Nm

View File

@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <fts.h> #include <fts.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -65,7 +66,6 @@ main(int argc, char *argv[])
int Hflag, Lflag, Rflag, fflag, hflag, vflag; int Hflag, Lflag, Rflag, fflag, hflag, vflag;
int ch, fts_options, oct, rval; int ch, fts_options, oct, rval;
char *flags, *ep; char *flags, *ep;
int (*change_flags)(const char *, unsigned long);
Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) while ((ch = getopt(argc, argv, "HLPRfhv")) != -1)
@ -104,20 +104,23 @@ main(int argc, char *argv[])
usage(); usage();
if (Rflag) { if (Rflag) {
fts_options = FTS_PHYSICAL;
if (hflag) if (hflag)
errx(1, "the -R and -h options " errx(1, "the -R and -h options may not be "
"may not be specified together"); "specified together.");
if (Hflag)
fts_options |= FTS_COMFOLLOW;
if (Lflag) { if (Lflag) {
fts_options &= ~FTS_PHYSICAL; fts_options = FTS_LOGICAL;
fts_options |= FTS_LOGICAL; } else {
} fts_options = FTS_PHYSICAL;
} else
fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
change_flags = hflag ? lchflags : chflags; if (Hflag) {
fts_options |= FTS_COMFOLLOW;
}
}
} else if (hflag) {
fts_options = FTS_PHYSICAL;
} else {
fts_options = FTS_LOGICAL;
}
flags = *argv; flags = *argv;
if (*flags >= '0' && *flags <= '7') { if (*flags >= '0' && *flags <= '7') {
@ -142,12 +145,21 @@ main(int argc, char *argv[])
err(1, NULL); err(1, NULL);
for (rval = 0; (p = fts_read(ftsp)) != NULL;) { for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
int atflag;
if ((fts_options & FTS_LOGICAL) ||
((fts_options & FTS_COMFOLLOW) &&
p->fts_level == FTS_ROOTLEVEL))
atflag = 0;
else
atflag = AT_SYMLINK_NOFOLLOW;
switch (p->fts_info) { switch (p->fts_info) {
case FTS_D: /* Change it at FTS_DP if we're recursive. */ case FTS_D: /* Change it at FTS_DP if we're recursive. */
if (!Rflag) if (!Rflag)
fts_set(ftsp, p, FTS_SKIP); fts_set(ftsp, p, FTS_SKIP);
continue; continue;
case FTS_DNR: /* Warn, chflag, continue. */ case FTS_DNR: /* Warn, chflags. */
warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
rval = 1; rval = 1;
break; break;
@ -156,16 +168,6 @@ main(int argc, char *argv[])
warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
rval = 1; rval = 1;
continue; continue;
case FTS_SL: /* Ignore. */
case FTS_SLNONE:
/*
* The only symlinks that end up here are ones that
* don't point to anything and ones that we found
* doing a physical walk.
*/
if (!hflag)
continue;
/* FALLTHROUGH */
default: default:
break; break;
} }
@ -175,7 +177,8 @@ main(int argc, char *argv[])
newflags = (p->fts_statp->st_flags | set) & clear; newflags = (p->fts_statp->st_flags | set) & clear;
if (newflags == p->fts_statp->st_flags) if (newflags == p->fts_statp->st_flags)
continue; continue;
if ((*change_flags)(p->fts_accpath, newflags) && !fflag) { if (chflagsat(AT_FDCWD, p->fts_accpath, newflags,
atflag) == -1 && !fflag) {
warn("%s", p->fts_path); warn("%s", p->fts_path);
rval = 1; rval = 1;
} else if (vflag) { } else if (vflag) {

View File

@ -32,7 +32,7 @@
.\" @(#)chmod.1 8.4 (Berkeley) 3/31/94 .\" @(#)chmod.1 8.4 (Berkeley) 3/31/94
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd January 26, 2009 .Dd April 20, 2015
.Dt CHMOD 1 .Dt CHMOD 1
.Os .Os
.Sh NAME .Sh NAME
@ -63,9 +63,9 @@ nor modify the exit status to reflect such failures.
.It Fl H .It Fl H
If the If the
.Fl R .Fl R
option is specified, symbolic links on the command line are followed. option is specified, symbolic links on the command line are followed
(Symbolic links encountered in the tree traversal are not followed by and hence unaffected by the command.
default.) (Symbolic links encountered during tree traversal are not followed.)
.It Fl h .It Fl h
If the file is a symbolic link, change the mode of the link itself If the file is a symbolic link, change the mode of the link itself
rather than the file that the link points to. rather than the file that the link points to.
@ -79,8 +79,12 @@ If the
option is specified, no symbolic links are followed. option is specified, no symbolic links are followed.
This is the default. This is the default.
.It Fl R .It Fl R
Change the modes of the file hierarchies rooted in the files Change the modes of the file hierarchies rooted in the files,
instead of just the files themselves. instead of just the files themselves.
Beware of unintentionally matching the
.Dq Pa ".."
hard link to the parent directory when using wildcards like
.Dq Li ".*" .
.It Fl v .It Fl v
Cause Cause
.Nm .Nm

View File

@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <fts.h> #include <fts.h>
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
@ -62,7 +63,7 @@ main(int argc, char *argv[])
FTS *ftsp; FTS *ftsp;
FTSENT *p; FTSENT *p;
mode_t *set; mode_t *set;
int Hflag, Lflag, Rflag, ch, error, fflag, fts_options, hflag, rval; int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval;
int vflag; int vflag;
char *mode; char *mode;
mode_t newmode; mode_t newmode;
@ -126,18 +127,23 @@ done: argv += optind;
usage(); usage();
if (Rflag) { if (Rflag) {
fts_options = FTS_PHYSICAL;
if (hflag) if (hflag)
errx(1, errx(1, "the -R and -h options may not be "
"the -R and -h options may not be specified together."); "specified together.");
if (Hflag)
fts_options |= FTS_COMFOLLOW;
if (Lflag) { if (Lflag) {
fts_options &= ~FTS_PHYSICAL; fts_options = FTS_LOGICAL;
fts_options |= FTS_LOGICAL; } else {
fts_options = FTS_PHYSICAL;
if (Hflag) {
fts_options |= FTS_COMFOLLOW;
}
}
} else if (hflag) {
fts_options = FTS_PHYSICAL;
} else {
fts_options = FTS_LOGICAL;
} }
} else
fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
mode = *argv; mode = *argv;
if ((set = setmode(mode)) == NULL) if ((set = setmode(mode)) == NULL)
@ -146,12 +152,21 @@ done: argv += optind;
if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
err(1, "fts_open"); err(1, "fts_open");
for (rval = 0; (p = fts_read(ftsp)) != NULL;) { for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
int atflag;
if ((fts_options & FTS_LOGICAL) ||
((fts_options & FTS_COMFOLLOW) &&
p->fts_level == FTS_ROOTLEVEL))
atflag = 0;
else
atflag = AT_SYMLINK_NOFOLLOW;
switch (p->fts_info) { switch (p->fts_info) {
case FTS_D: /* Change it at FTS_DP. */ case FTS_D: /* Change it at FTS_DP. */
if (!Rflag) if (!Rflag)
fts_set(ftsp, p, FTS_SKIP); fts_set(ftsp, p, FTS_SKIP);
continue; continue;
case FTS_DNR: /* Warn, chmod, continue. */ case FTS_DNR: /* Warn, chmod. */
warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
rval = 1; rval = 1;
break; break;
@ -160,16 +175,6 @@ done: argv += optind;
warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
rval = 1; rval = 1;
continue; continue;
case FTS_SL: /* Ignore. */
case FTS_SLNONE:
/*
* The only symlinks that end up here are ones that
* don't point to anything and ones that we found
* doing a physical walk.
*/
if (!hflag)
continue;
/* FALLTHROUGH */
default: default:
break; break;
} }
@ -182,17 +187,11 @@ done: argv += optind;
if (may_have_nfs4acl(p, hflag) == 0 && if (may_have_nfs4acl(p, hflag) == 0 &&
(newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) (newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS))
continue; continue;
if (hflag) if (fchmodat(AT_FDCWD, p->fts_accpath, newmode, atflag) == -1
error = lchmod(p->fts_accpath, newmode); && !fflag) {
else
error = chmod(p->fts_accpath, newmode);
if (error) {
if (!fflag) {
warn("%s", p->fts_path); warn("%s", p->fts_path);
rval = 1; rval = 1;
} } else if (vflag) {
} else {
if (vflag) {
(void)printf("%s", p->fts_path); (void)printf("%s", p->fts_path);
if (vflag > 1) { if (vflag > 1) {
@ -209,7 +208,6 @@ done: argv += optind;
(void)printf("\n"); (void)printf("\n");
} }
} }
}
if (errno) if (errno)
err(1, "fts_read"); err(1, "fts_read");
exit(rval); exit(rval);

View File

@ -31,7 +31,7 @@
.\" @(#)chgrp.1 8.3 (Berkeley) 3/31/94 .\" @(#)chgrp.1 8.3 (Berkeley) 3/31/94
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd February 21, 2010 .Dd April 20, 2015
.Dt CHGRP 1 .Dt CHGRP 1
.Os .Os
.Sh NAME .Sh NAME
@ -60,8 +60,9 @@ The following options are available:
.It Fl H .It Fl H
If the If the
.Fl R .Fl R
option is specified, symbolic links on the command line are followed. option is specified, symbolic links on the command line are followed
(Symbolic links encountered in the tree traversal are not followed.) and hence unaffected by the command.
(Symbolic links encountered during traversal are not followed.)
.It Fl L .It Fl L
If the If the
.Fl R .Fl R
@ -72,8 +73,12 @@ If the
option is specified, no symbolic links are followed. option is specified, no symbolic links are followed.
This is the default. This is the default.
.It Fl R .It Fl R
Change the group ID for the file hierarchies rooted Change the group ID of the file hierarchies rooted in the files,
in the files instead of just the files themselves. instead of just the files themselves.
Beware of unintentionally matching the
.Dq Pa ".."
hard link to the parent directory when using wildcards like
.Dq Li ".*" .
.It Fl f .It Fl f
The force option ignores errors, except for usage errors and does not The force option ignores errors, except for usage errors and does not
query about strange modes (unless the user does not have proper permissions). query about strange modes (unless the user does not have proper permissions).

View File

@ -28,7 +28,7 @@
.\" @(#)chown.8 8.3 (Berkeley) 3/31/94 .\" @(#)chown.8 8.3 (Berkeley) 3/31/94
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd February 21, 2010 .Dd April 20, 2015
.Dt CHOWN 8 .Dt CHOWN 8
.Os .Os
.Sh NAME .Sh NAME
@ -64,8 +64,9 @@ The options are as follows:
.It Fl H .It Fl H
If the If the
.Fl R .Fl R
option is specified, symbolic links on the command line are followed. option is specified, symbolic links on the command line are followed
(Symbolic links encountered in the tree traversal are not followed.) and hence unaffected by the command.
(Symbolic links encountered during traversal are not followed.)
.It Fl L .It Fl L
If the If the
.Fl R .Fl R
@ -76,8 +77,8 @@ If the
option is specified, no symbolic links are followed. option is specified, no symbolic links are followed.
This is the default. This is the default.
.It Fl R .It Fl R
Change the user ID and/or the group ID of the specified directory trees Change the user ID and/or the group ID of the file hierarchies rooted
(recursively, including their contents) and files. in the files, instead of just the files themselves.
Beware of unintentionally matching the Beware of unintentionally matching the
.Dq Pa ".." .Dq Pa ".."
hard link to the parent directory when using wildcards like hard link to the parent directory when using wildcards like

View File

@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <fts.h> #include <fts.h>
#include <grp.h> #include <grp.h>
#include <libgen.h> #include <libgen.h>
@ -119,18 +120,24 @@ main(int argc, char **argv)
usage(); usage();
if (Rflag) { if (Rflag) {
fts_options = FTS_PHYSICAL;
if (hflag && (Hflag || Lflag)) if (hflag && (Hflag || Lflag))
errx(1, "the -R%c and -h options may not be " errx(1, "the -R%c and -h options may not be "
"specified together", Hflag ? 'H' : 'L'); "specified together", Hflag ? 'H' : 'L');
if (Hflag) if (Lflag) {
fts_options = FTS_LOGICAL;
} else {
fts_options = FTS_PHYSICAL;
if (Hflag) {
fts_options |= FTS_COMFOLLOW; fts_options |= FTS_COMFOLLOW;
else if (Lflag) {
fts_options &= ~FTS_PHYSICAL;
fts_options |= FTS_LOGICAL;
} }
} else }
fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; } else if (hflag) {
fts_options = FTS_PHYSICAL;
} else {
fts_options = FTS_LOGICAL;
}
if (xflag) if (xflag)
fts_options |= FTS_XDEV; fts_options |= FTS_XDEV;
@ -156,6 +163,15 @@ main(int argc, char **argv)
err(1, NULL); err(1, NULL);
for (rval = 0; (p = fts_read(ftsp)) != NULL;) { for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
int atflag;
if ((fts_options & FTS_LOGICAL) ||
((fts_options & FTS_COMFOLLOW) &&
p->fts_level == FTS_ROOTLEVEL))
atflag = 0;
else
atflag = AT_SYMLINK_NOFOLLOW;
switch (p->fts_info) { switch (p->fts_info) {
case FTS_D: /* Change it at FTS_DP. */ case FTS_D: /* Change it at FTS_DP. */
if (!Rflag) if (!Rflag)
@ -170,30 +186,17 @@ main(int argc, char **argv)
warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
rval = 1; rval = 1;
continue; continue;
case FTS_SL:
case FTS_SLNONE:
/*
* The only symlinks that end up here are ones that
* don't point to anything and ones that we found
* doing a physical walk.
*/
if (hflag)
break;
else
continue;
default: default:
break; break;
} }
if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) &&
(gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) (gid == (gid_t)-1 || gid == p->fts_statp->st_gid))
continue; continue;
if ((hflag ? lchown : chown)(p->fts_accpath, uid, gid) == -1) { if (fchownat(AT_FDCWD, p->fts_accpath, uid, gid, atflag)
if (!fflag) { == -1 && !fflag) {
chownerr(p->fts_path); chownerr(p->fts_path);
rval = 1; rval = 1;
} } else if (vflag) {
} else {
if (vflag) {
printf("%s", p->fts_path); printf("%s", p->fts_path);
if (vflag > 1) { if (vflag > 1) {
if (ischown) { if (ischown) {
@ -223,7 +226,6 @@ main(int argc, char **argv)
printf("\n"); printf("\n");
} }
} }
}
if (errno) if (errno)
err(1, "fts_read"); err(1, "fts_read");
exit(rval); exit(rval);