Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 19 Sep 2001 18:35:52 +0300
From:      Ruslan Ermilov <ru@FreeBSD.ORG>
To:        arch@FreeBSD.ORG
Cc:        freebsd-standards@bostonradio.org
Subject:   Re: Fixing chown(8) and friends to handle symlinks properly
Message-ID:  <20010919183552.G884@sunbay.com>
In-Reply-To: <20010918190222.F4648@sunbay.com>; from ru@FreeBSD.org on Tue, Sep 18, 2001 at 07:02:22PM %2B0300
References:  <20010913202742.C2458@sunbay.com> <20010917191657.A19072@sunbay.com> <200109172143.f8HLhkU41334@khavrinen.lcs.mit.edu> <20010913202742.C2458@sunbay.com> <20010914060800.A27869@nagual.pp.ru> <20010918190222.F4648@sunbay.com>

next in thread | previous in thread | raw e-mail | index | archive | help

--2B/JsCI69OhZNC5r
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Tue, Sep 18, 2001 at 07:02:22PM +0300, Ruslan Ermilov wrote:
> Hi,
> 
> After re-reading the POSIX.1-200x draft and the symlink(7) manpage,
> I have developed another patch (the winner!) that implements what
> I think is required by POSIX, while still preserving the basic
> ideas of symlink(7).
> 
Here's a similar patch for chmod(1), for those still interested.


Cheers,
-- 
Ruslan Ermilov		Oracle Developer/DBA,
ru@sunbay.com		Sunbay Software AG,
ru@FreeBSD.org		FreeBSD committer,
+380.652.512.251	Simferopol, Ukraine

http://www.FreeBSD.org	The Power To Serve
http://www.oracle.com	Enabling The Information Age

--2B/JsCI69OhZNC5r
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=p

Index: chmod.c
===================================================================
RCS file: /home/ncvs/src/bin/chmod/chmod.c,v
retrieving revision 1.22
diff -u -p -u -p -r1.22 chmod.c
--- chmod.c	2001/05/31 11:47:19	1.22
+++ chmod.c	2001/09/19 15:37:29
@@ -32,7 +32,7 @@
  */
 
 #ifndef lint
-static char const copyright[] =
+static const char copyright[] =
 "@(#) Copyright (c) 1989, 1993, 1994\n\
 	The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */
@@ -57,8 +57,7 @@ static const char rcsid[] =
 #include <string.h>
 #include <unistd.h>
 
-int main __P((int, char *[]));
-void usage __P((void));
+void	usage __P((void));
 
 int
 main(argc, argv)
@@ -70,27 +69,25 @@ main(argc, argv)
 	mode_t *set;
 	long val;
 	int oct, omode;
-	int Hflag, Lflag, Pflag, Rflag, ch, fflag, fts_options, hflag, rval;
-	int vflag;
+	int Hflag, Lflag, Rflag, fflag, hflag, vflag;
+	int ch, rval;
 	char *ep, *mode;
 	int newmode;
+	struct stat sb;
 	int (*change_mode) __P((const char *, mode_t));
 
-	set = NULL;
-	omode = 0;
-	Hflag = Lflag = Pflag = Rflag = fflag = hflag = vflag = 0;
+	Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
 	while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1)
 		switch (ch) {
 		case 'H':
 			Hflag = 1;
-			Lflag = Pflag = 0;
+			Lflag = 0;
 			break;
 		case 'L':
 			Lflag = 1;
-			Hflag = Pflag = 0;
+			Hflag = 0;
 			break;
 		case 'P':
-			Pflag = 1;
 			Hflag = Lflag = 0;
 			break;
 		case 'R':
@@ -100,14 +97,6 @@ main(argc, argv)
 			fflag = 1;
 			break;
 		case 'h':
-			/*
-			 * In System V (and probably POSIX.2) the -h option
-			 * causes chmod to change the mode of the symbolic
-			 * link.  4.4BSD's symbolic links didn't have modes,
-			 * so it was an undocumented noop.  In FreeBSD 3.0,
-			 * lchmod(2) is introduced and this option does real
-			 * work.
-			 */
 			hflag = 1;
 			break;
 		/*
@@ -136,25 +125,6 @@ done:	argv += optind;
 	if (argc < 2)
 		usage();
 
-	if (Rflag) {
-		fts_options = FTS_PHYSICAL;
-		if (hflag)
-			errx(1,
-		"the -R and -h options may not be specified together.");
-		if (Hflag)
-			fts_options |= FTS_COMFOLLOW;
-		if (Lflag) {
-			fts_options &= ~FTS_PHYSICAL;
-			fts_options |= FTS_LOGICAL;
-		}
-	} else
-		fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
-
-	if (hflag)
-		change_mode = lchmod;
-	else
-		change_mode = chmod;
-
 	mode = *argv;
 	if (*mode >= '0' && *mode <= '7') {
 		errno = 0;
@@ -173,15 +143,17 @@ done:	argv += optind;
 		oct = 0;
 	}
 
-	if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
+	if ((ftsp = fts_open(++argv, FTS_PHYSICAL, 0)) == NULL)
 		err(1, NULL);
+
 	for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+		change_mode = chmod;
 		switch (p->fts_info) {
 		case FTS_D:			/* Change it at FTS_DP. */
 			if (!Rflag)
 				fts_set(ftsp, p, FTS_SKIP);
 			continue;
-		case FTS_DNR:			/* Warn, chmod, continue. */
+		case FTS_DNR:			/* Warn, chmod. */
 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
 			rval = 1;
 			break;
@@ -190,41 +162,80 @@ done:	argv += optind;
 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
 			rval = 1;
 			continue;
-		case FTS_SL:			/* Ignore. */
-		case FTS_SLNONE:
+		case FTS_SL:
 			/*
-			 * 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 not traversing a file tree and -h was not
+			 * specified, follow symlinks.  This is done to
+			 * reinitialize the p->fts_statp with stat(2).
+			 *
+			 * If traversing a file tree, check if we were
+			 * asked to follow symlinks to directories.
 			 */
-			if (!hflag)
+			if ((!Rflag && !hflag) ||
+			    (Rflag && (Lflag || (Hflag && p->fts_level == 0)) &&
+			    stat(p->fts_accpath, &sb) == 0 &&
+			    S_ISDIR(sb.st_mode))) {
+				fts_set(ftsp, p, FTS_FOLLOW);
 				continue;
-			/* else */
-			/* FALLTHROUGH */
+			}
+			if (hflag || Rflag)
+				change_mode = lchmod;
+			break;
+		case FTS_SLNONE:		/* Ignore. */
+			/*
+			 * The only symlinks that end up here are ones that
+			 * don't point to anything.  Note that since we are
+			 * doing a physical walk, we never reach here unless
+			 * we asked to follow explicitly.
+			 *
+			 * When following a symlink to a directory above,
+			 * the directory may be deleted (or renamed) in a
+			 * small window between the calls to fts_set() and
+			 * fts_read(), causing FTS_SLNONE to be returned.
+			 */
+			continue;
 		default:
 			break;
 		}
 		newmode = oct ? omode : getmode(set, p->fts_statp->st_mode);
+#define DEBUG
+#ifdef DEBUG
+		if ((change_mode == chmod ? stat : lstat)(p->fts_accpath, &sb) == -1)
+			abort();
+		if (sb.st_ino != p->fts_statp->st_ino)
+			abort();
+#else
 		if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS))
 			continue;
-		if ((*change_mode)(p->fts_accpath, newmode) && !fflag) {
-			warn("%s", p->fts_path);
-			rval = 1;
+#endif
+		if ((*change_mode)(p->fts_accpath, newmode) == -1) {
+			if (!fflag) {
+				warn("%s", p->fts_path);
+				rval = 1;
+			}
 		} else {
 		    	if (vflag)
-				(void)printf("%s\n", p->fts_accpath);
+#ifdef DEBUG
+				printf("%s %s\n",
+				    (change_mode == chmod) ? "chmod" : "lchmod",
+				    p->fts_path);
+#else
+				printf("%s\n", p->fts_path);
+#endif
 		}
 	}
 	if (errno)
 		err(1, "fts_read");
-	free(set);
+	if (!oct)
+		free(set);
 	exit(rval);
 }
 
 void
 usage()
 {
+
 	(void)fprintf(stderr,
-	    "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n");
+	    "usage: chmod [-fv] [-h | -R [-H | -L | -P]] mode file ...\n");
 	exit(1);
 }
Index: chmod.1
===================================================================
RCS file: /home/ncvs/src/bin/chmod/chmod.1,v
retrieving revision 1.27
diff -u -p -u -p -r1.27 chmod.1
--- chmod.1	2001/08/15 09:09:34	1.27
+++ chmod.1	2001/09/19 15:37:30
@@ -35,7 +35,7 @@
 .\"	@(#)chmod.1	8.4 (Berkeley) 3/31/94
 .\" $FreeBSD: src/bin/chmod/chmod.1,v 1.27 2001/08/15 09:09:34 ru Exp $
 .\"
-.Dd March 31, 1994
+.Dd September 19, 2001
 .Dt CHMOD 1
 .Os
 .Sh NAME
@@ -43,55 +43,84 @@
 .Nd change file modes
 .Sh SYNOPSIS
 .Nm
-.Op Fl fhv
-.Op Fl R Op Fl H | L | P
+.Op Fl fv
+.Op Fl h | R Op Fl H | L | P
 .Ar mode
 .Ar
 .Sh DESCRIPTION
 The
 .Nm
-utility modifies the file mode bits of the listed files
-as specified by the
-.Ar mode
-operand.
+utility changes the file mode bits of the specified files.
 .Pp
-The options are as follows:
-.Bl -tag -width Ds
+The following options are available:
+.Bl -tag -width indent
 .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
+and a symbolic link
+referencing a file of type directory
+is specified on the command line,
+change the mode
+of the directory referenced by the link
+and all files in the subtree below it.
+Otherwise,
+change the mode
+of the link itself
+rather than the file that the link points to.
 .It Fl L
 If the
 .Fl R
-option is specified, all symbolic links are followed.
+option is specified
+and a symbolic link
+referencing a file of type directory
+is specified on the command line
+or encountered during the tree traversal,
+change the mode
+of the directory referenced by the link
+and all files in the subtree below it.
+Otherwise,
+change the mode
+of the link itself
+rather than the file that the link points to.
 .It Fl P
 If the
 .Fl R
-option is specified, no symbolic links are followed.
-This is the default.
+option is specified
+and a symbolic link
+is specified on the command line
+or encountered during the tree traversal,
+change the mode
+of the link itself
+rather than the file that the link points to.
 .It Fl R
-Change the modes of the file hierarchies rooted in the files
-instead of just the files themselves.
+If
+.Ar file
+argument designates a directory,
+change the mode
+of the directory
+and all files in the subtree below it.
 .It Fl f
-Do not display a diagnostic message if
-.Nm
-could not modify the mode for
-.Va file .
+Do not report any failure to
+change the mode
+of the file,
+nor modify the exit status to reflect such failures.
 .It Fl h
-If the file is a symbolic link, change the mode of the link itself
+If the
+.Fl R
+option is not specified
+and
+.Ar file
+argument is a symbolic link,
+change the mode
+of the link itself
 rather than the file that the link points to.
 .It Fl v
-Cause
-.Nm
-to be verbose, showing files as the mode is modified.
+Be verbose, showing files as the mode is modified.
 .El
 .Pp
 The
-.Fl H ,
-.Fl L
+.Fl H , L
 and
 .Fl P
 options are ignored unless the
@@ -99,37 +128,45 @@ options are ignored unless the
 option is specified.
 In addition, these options override each other and the
 command's actions are determined by the last one specified.
+If none are specified, the
+default is
+.Fl P .
 .Pp
 Only the owner of a file or the super-user is permitted to change
 the mode of a file.
 .Sh DIAGNOSTICS
 .Ex -std
 .Sh MODES
-Modes may be absolute or symbolic.
+The
+.Ar mode
+argument may be either absolute or symbolic.
 An absolute mode is an octal number constructed from the sum of
 one or more of the following values:
 .Pp
-.Bl -tag -width 6n -compact -offset indent
+.Bl -tag -width indent -compact -offset indent
 .It Li 4000
-(the set-user-ID-on-execution bit) Executable files with this bit set
-will run with effective uid set to the uid of the file owner.
-Directories with the set-user-id bit set will force all files and
+(the set-user-ID-on-execution bit)
+Executable files with this bit set
+will run with effective user ID set to the user ID of the file owner.
+Directories with the set-user-ID bit set will force all files and
 sub-directories created in them to be owned by the directory owner
-and not by the uid of the creating process, if the underlying file
+and not by the user ID of the creating process, if the underlying file
 system supports this feature: see
 .Xr chmod 2
 and the
-.Ar suiddir
+.Cm suiddir
 option to
 .Xr mount 8 .
 .It Li 2000
-(the set-group-ID-on-execution bit)  Executable files with this bit set
-will run with effective gid set to the gid of the file owner.
+(the set-group-ID-on-execution bit)
+Executable files with this bit set
+will run with effective group ID set to the group ID of the file owner.
 .It Li 1000
 (the sticky bit)
 When set on a directory, unprivileged users can delete and rename
 only those files in the directory that are owned by them, regardless of
-the permissions on the directory.  Under
+the permissions on the directory.
+Under
 .Fx ,
 the sticky bit is
 ignored for executable files and may only be set for directories (see
@@ -139,27 +176,30 @@ Allow read by owner.
 .It Li 0200
 Allow write by owner.
 .It Li 0100
-For files, allow execution by owner.  For directories, allow the owner to
+For files, allow execution by owner.
+For directories, allow the owner to
 search in the directory.
 .It Li 0040
 Allow read by group members.
 .It Li 0020
 Allow write by group members.
 .It Li 0010
-For files, allow execution by group members.  For directories, allow
-group members to search in the directory.
+For files, allow execution by group members.
+For directories, allow group members to
+search in the directory.
 .It Li 0004
 Allow read by others.
 .It Li 0002
 Allow write by others.
 .It Li 0001
-For files, allow execution by others.  For directories allow others to
+For files, allow execution by others.
+For directories allow others to
 search in the directory.
 .El
 .Pp
 For example, the absolute mode that permits read, write and execute by
 the owner, read and execute by group members, read and execute by
-others, and no set-uid or set-gid behaviour is 755
+others, and no set-user-ID or set-group-ID behaviour is 755
 (400+200+100+040+010+004+001).
 .Pp
 The symbolic mode is described by the following grammar:
@@ -174,40 +214,52 @@ perm         ::= r | s | t | w | x | X |
 .Pp
 The
 .Ar who
-symbols ``u'', ``g'', and ``o'' specify the user, group, and other parts
+symbols
+.Dq Li u ,
+.Dq Li g ,
+and
+.Dq Li o
+specify the user, group, and other parts
 of the mode bits, respectively.
 The
 .Ar who
-symbol ``a'' is equivalent to ``ugo''.
+symbol
+.Dq Li a
+is equivalent to
+.Dq Li ugo .
 .Pp
 The
 .Ar perm
 symbols represent the portions of the mode bits as follows:
 .Pp
-.Bl -tag -width Ds -compact -offset indent
-.It r
+.Bl -tag -width indent -compact -offset indent
+.It Li r
 The read bits.
-.It s
+.It Li s
 The set-user-ID-on-execution and set-group-ID-on-execution bits.
-.It t
+.It Li t
 The sticky bit.
-.It w
+.It Li w
 The write bits.
-.It x
+.It Li x
 The execute/search bits.
-.It X
+.It Li X
 The execute/search bits if the file is a directory or any of the
 execute/search bits are set in the original (unmodified) mode.
 Operations with the
 .Ar perm
-symbol ``X'' are only meaningful in conjunction with the
+symbol
+.Dq Li X
+are only meaningful in conjunction with the
 .Ar op
-symbol ``+'', and are ignored in all other cases.
-.It u
+symbol
+.Dq Li + ,
+and are ignored in all other cases.
+.It Li u
 The user permission bits in the mode of the original file.
-.It g
+.It Li g
 The group permission bits in the mode of the original file.
-.It o
+.It Li o
 The other permission bits in the mode of the original file.
 .El
 .Pp
@@ -215,10 +267,12 @@ The
 .Ar op
 symbols represent the operation performed, as follows:
 .Bl -tag -width 4n
-.It +
+.It Li +
 If no value is supplied for
 .Ar perm ,
-the ``+'' operation has no effect.
+the
+.Dq Li +
+operation has no effect.
 If no value is supplied for
 .Ar who ,
 each permission bit specified in
@@ -230,10 +284,12 @@ Otherwise, the mode bits represented by 
 and
 .Ar perm
 values are set.
-.It \&\-
+.It Li \-
 If no value is supplied for
 .Ar perm ,
-the ``\-'' operation has no effect.
+the
+.Dq Li \-
+operation has no effect.
 If no value is supplied for
 .Ar who ,
 each permission bit specified in
@@ -245,10 +301,12 @@ Otherwise, the mode bits represented by 
 and
 .Ar perm
 values are cleared.
-.It =
+.It Li =
 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 ,
@@ -270,11 +328,16 @@ bits, and each operation is applied to t
 order specified.
 .Pp
 Operations upon the other permissions only (specified by the symbol
-``o'' by itself), in combination with the
+.Dq Li o
+by itself), in combination with the
 .Ar perm
-symbols ``s'' or ``t'', are ignored.
+symbols
+.Dq Li s
+or
+.Dq Li t ,
+are ignored.
 .Sh EXAMPLES
-.Bl -tag -width "u=rwx,go=u-w" -compact
+.Bl -tag -width ".Li u=rwx,go=u-w" -compact
 .It Li 644
 make a file readable by anyone and writable by the owner only.
 .Pp
@@ -301,10 +364,13 @@ clear all mode bits for group and others
 set the group bits equal to the user bits, but clear the group write bit.
 .El
 .Sh BUGS
-There's no
+There is no
 .Ar perm
 option for the naughty bits.
 .Sh COMPATIBILITY
+On previous versions of this system, symbolic links did not have
+modes.
+.Pp
 The
 .Fl v
 option is non-standard and its use in scripts is not recommended.
@@ -328,7 +394,7 @@ utility is expected to be
 compatible with the exception of the
 .Ar perm
 symbol
-.Dq t
+.Dq Li t
 which is not included in that standard.
 .Sh HISTORY
 A

--2B/JsCI69OhZNC5r--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010919183552.G884>