chmod(1): Bring in some changes from FreeBSD.
authorPeter Avalos <pavalos@dragonflybsd.org>
Thu, 12 May 2016 02:31:08 +0000 (19:31 -0700)
committerPeter Avalos <pavalos@dragonflybsd.org>
Thu, 12 May 2016 03:27:12 +0000 (20:27 -0700)
- Change directory permissions in pre-order.
- Allow "-v -v" to mean very verbose.
- Follow symbolic links named as command line arguments if run w/o -R.

Obtained-from:  FreeBSD

bin/chmod/chmod.1
bin/chmod/chmod.c

index c6f98a0..9e3d9df 100644 (file)
@@ -1,3 +1,4 @@
+.\"-
 .\" Copyright (c) 1989, 1990, 1993, 1994
 .\"    The Regents of the University of California.  All rights reserved.
 .\"
 .\" SUCH DAMAGE.
 .\"
 .\"    @(#)chmod.1     8.4 (Berkeley) 3/31/94
-.\" $FreeBSD: src/bin/chmod/chmod.1,v 1.17.2.16 2003/02/24 03:01:00 trhodes Exp $
-.\" $DragonFly: src/bin/chmod/chmod.1,v 1.3 2006/02/17 19:33:30 swildner Exp $
+.\" $FreeBSD: head/bin/chmod/chmod.1 282208 2015-04-29 00:49:00Z smh $
 .\"
-.Dd March 31, 1994
+.Dd May 11, 2016
 .Dt CHMOD 1
 .Os
 .Sh NAME
@@ -63,9 +63,9 @@ nor modify the exit status to reflect such failures.
 .It Fl H
 If the
 .Fl R
-option is specified, symbolic links on the command line are followed.
-(Symbolic links encountered in the tree traversal are not followed by
-default.)
+option is specified, symbolic links on the command line are followed
+and hence unaffected by the command.
+(Symbolic links encountered during tree traversal are not followed.)
 .It Fl h
 If the file is a symbolic link, change the mode of the link itself
 rather than the file that the link points to.
@@ -79,12 +79,20 @@ If the
 option is specified, no symbolic links are followed.
 This is the default.
 .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.
+Beware of unintentionally matching the
+.Dq Pa ".."
+hard link to the parent directory when using wildcards like
+.Dq Li ".*" .
 .It Fl v
 Cause
 .Nm
 to be verbose, showing filenames as the mode is modified.
+If the
+.Fl v
+flag is specified more than once, the old and new modes of the file
+will also be printed, in both octal and symbolic notation.
 .El
 .Pp
 The
@@ -102,33 +110,6 @@ Only the owner of a file or the super-user is permitted to change
 the mode of a file.
 .Sh EXIT STATUS
 .Ex -std
-.Sh EXAMPLES
-.Bl -tag -width "u=rwx,go=u-w" -compact
-.It Li 644
-make a file readable by anyone and writable by the owner only.
-.Pp
-.It Li go-w
-deny write permission to group and others.
-.Pp
-.It Li =rw,+X
-set the read and write permissions to the usual defaults, but
-retain any execute permissions that are currently set.
-.Pp
-.It Li +X
-make a directory or file searchable/executable by everyone if it is
-already searchable/executable by anyone.
-.Pp
-.It Li 755
-.It Li u=rwx,go=rx
-.It Li u=rwx,go=u-w
-make a file readable/executable by everyone and writable by the owner only.
-.Pp
-.It Li go=
-clear all mode bits for group and others.
-.Pp
-.It Li g=u-w
-set the group bits equal to the user bits, but clear the group write bit.
-.El
 .Sh MODES
 Modes may be absolute or symbolic.
 An absolute mode is an octal number constructed from the sum of
@@ -251,6 +232,8 @@ If no value is supplied for
 each permission bit specified in
 .Ar perm ,
 for which the corresponding bit in the file mode creation mask
+(see
+.Xr umask 2 )
 is clear, is set.
 Otherwise, the mode bits represented by the specified
 .Ar who
@@ -275,7 +258,9 @@ values are cleared.
 .It =
 The mode bits specified by the
 .Ar who
-value are cleared, or, if no who value is specified, the owner, group
+value are cleared, or, if no
+.Ar who
+value is specified, the owner, group
 and other mode bits are cleared.
 Then, if no value is supplied for
 .Ar who ,
@@ -300,6 +285,37 @@ Operations upon the other permissions only (specified by the symbol
 ``o'' by itself), in combination with the
 .Ar perm
 symbols ``s'' or ``t'', are ignored.
+.Pp
+The ``w'' permission on directories will permit file creation, relocation,
+and copy into that directory.
+Files created within the directory itself will inherit its group ID.
+.Sh EXAMPLES
+.Bl -tag -width "u=rwx,go=u-w" -compact
+.It Li 644
+make a file readable by anyone and writable by the owner only.
+.Pp
+.It Li go-w
+deny write permission to group and others.
+.Pp
+.It Li =rw,+X
+set the read and write permissions to the usual defaults, but
+retain any execute permissions that are currently set.
+.Pp
+.It Li +X
+make a directory or file searchable/executable by everyone if it is
+already searchable/executable by anyone.
+.Pp
+.It Li 755
+.It Li u=rwx,go=rx
+.It Li u=rwx,go=u-w
+make a file readable/executable by everyone and writable by the owner only.
+.Pp
+.It Li go=
+clear all mode bits for group and others.
+.Pp
+.It Li g=u-w
+set the group bits equal to the user bits, but clear the group write bit.
+.El
 .Sh COMPATIBILITY
 The
 .Fl v
@@ -332,6 +348,6 @@ A
 command appeared in
 .At v1 .
 .Sh BUGS
-There's no
+There is no
 .Ar perm
-option for the naughty bits.
+option for the naughty bits of a horse.
index f0227e7..8c0dc71 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*-
  * Copyright (c) 1989, 1993, 1994
  *     The Regents of the University of California.  All rights reserved.
  *
  *
  * @(#) Copyright (c) 1989, 1993, 1994 The Regents of the University of California.  All rights reserved.
  * @(#)chmod.c 8.8 (Berkeley) 4/1/94
- * $FreeBSD: src/bin/chmod/chmod.c,v 1.16.2.6 2002/10/18 01:36:38 trhodes Exp $
- * $DragonFly: src/bin/chmod/chmod.c,v 1.9 2008/09/02 22:13:11 swildner Exp $
+ * $FreeBSD: head/bin/chmod/chmod.c 283997 2015-06-04 19:18:58Z pluknet $
  */
 
-#include <sys/types.h>
+#include <sys/param.h>
 #include <sys/stat.h>
 
 #include <err.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <fts.h>
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
-static void usage (void);
+static void usage(void);
 
 int
-main(int argc, char **argv)
+main(int argc, char *argv[])
 {
        FTS *ftsp;
        FTSENT *p;
-       mode_t newmode;
        void *set;
        int Hflag, Lflag, Rflag, ch, fflag;
        int fts_options, hflag, rval, vflag;
        char *mode;
-       int (*change_mode) (const char *, mode_t);
+       mode_t newmode;
 
        Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
        while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1)
@@ -103,8 +102,9 @@ main(int argc, char **argv)
                                --optind;
                        goto done;
                case 'v':
-                       vflag = 1;
+                       vflag++;
                        break;
+               case '?':
                default:
                        usage();
                }
@@ -114,24 +114,25 @@ done:     argv += optind;
        if (argc < 2)
                usage();
 
-       fts_options = FTS_PHYSICAL;
        if (Rflag) {
                if (hflag)
-                       errx(1,
-               "the -R and -h options may not be specified together.");
-               if (Hflag)
-                       fts_options |= FTS_COMFOLLOW;
+                       errx(1, "the -R and -h options may not be "
+                           "specified together.");
                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;
        }
 
-       if (hflag)
-               change_mode = lchmod;
-       else
-               change_mode = chmod;
-
        mode = *argv;
        errno = 0;
        if ((set = setmode(mode)) == NULL) {
@@ -143,50 +144,62 @@ done:     argv += optind;
        }
 
        if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
-               err(1, NULL);
+               err(1, "fts_open");
        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) {
-               case FTS_D:                     /* Change it at FTS_DP. */
+               case FTS_D:
                        if (!Rflag)
                                fts_set(ftsp, p, FTS_SKIP);
-                       continue;
-               case FTS_DNR:                   /* Warn, chmod, continue. */
+                       break;
+               case FTS_DNR:                   /* Warn, chmod. */
                        warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
                        rval = 1;
                        break;
+               case FTS_DP:                    /* Already changed at FTS_D. */
+                       continue;
                case FTS_ERR:                   /* Warn, continue. */
                case FTS_NS:
                        warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
                        rval = 1;
                        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;
-                       /* else */
-                       /* FALLTHROUGH */
                default:
                        break;
                }
                newmode = getmode(set, p->fts_statp->st_mode);
                if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS))
                        continue;
-               if ((*change_mode)(p->fts_accpath, newmode) && !fflag) {
+               if (fchmodat(AT_FDCWD, p->fts_accpath, newmode, atflag) == -1
+                   && !fflag) {
                        warn("%s", p->fts_path);
                        rval = 1;
-               } else {
-                       if (vflag)
-                               printf("%s\n", p->fts_accpath);
+               } else if (vflag) {
+                       printf("%s", p->fts_path);
+
+                       if (vflag > 1) {
+                               char m1[12], m2[12];
+
+                               strmode(p->fts_statp->st_mode, m1);
+                               strmode((p->fts_statp->st_mode &
+                                   S_IFMT) | newmode, m2);
+                               printf(": 0%o [%s] -> 0%o [%s]",
+                                   p->fts_statp->st_mode, m1,
+                                   (p->fts_statp->st_mode & S_IFMT) |
+                                   newmode, m2);
+                       }
+                       printf("\n");
                }
        }
        if (errno)
                err(1, "fts_read");
-       free(set);
        exit(rval);
 }