Date: Thu, 13 Sep 2001 20:27:42 +0300 From: Ruslan Ermilov <ru@FreeBSD.org> To: arch@FreeBSD.org, freebsd-standards@bostonradio.org Cc: Bruce Evans <bde@FreeBSD.org>, Garrett Wollman <wollman@FreeBSD.org> Subject: Fixing chown(8) and friends to handle symlinks properly Message-ID: <20010913202742.C2458@sunbay.com>
next in thread | raw e-mail | index | archive | help
--2fHTh5uZTiUOsy+g
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
[Bcc'ed to -current in a hope for a more wider auditory]
The attached patch fixes the handling of symlinks in chown(8)
and chgrp(1). It is currently broken in many different ways.
Basically, it is broken because -h is not supported with -RH
or -RL. The actual problem lies in the depths of the fts(3)
implementation, in particular, how the `fts_stat' is getting
initialized. The trick in supporting roughly any combination
of command line flags ([-h] [-R [-H | -L | -P]]) was to avoid
looking into the `fts_stat' (as we don't know whether it is
from lstat(2) or stat(2)), and just call chown(2) or lchown(2)
as appropriate. Although it may seem as a big performance
degradation, in fact it isn't, as kernel doesn't modify the
inode if nothing is to be changed.
This work is based on the latest POSIX drafts, and the
superior NetBSD implementation (which I had to fix in a
number of cases anyway, in particular, handling of dead
symlinks in the -h case).
The new algorithm of handling a symlink encountered during
a file tree traversal works as follows:
+-------------+-------------------------------------------------+
| Options | Action |
+-------------+-------------------------------------------------+
| -- | chown |
| -h | lchown |
| | Notes: only FTS_SL symlinks may end up here |
+-------------+-------------------------------------------------+
| -RP | SKIP |
| -RP -h | lchown |
| | Notes: only FTS_SL symlinks may end here |
+-------------+-------------------------------------------------+
| -RH | SKIP |
| -RH -h | lchown |
| | Notes: both FTS_SL and FTS_SLNONE symlinks |
| | may end up here. FTS_SLNONE's are deadlinks |
| | specified as command line arguments |
+-------------+-------------------------------------------------+
| -RL | SKIP |
| -RL -h | lchown |
| | Notes: only FTS_SLNONE symlinks may end up here |
+-------------+-------------------------------------------------+
Or, in a more compact form:
: if symlink {
: if -R
: -h ? lchmod : SKIP
: else
: -h ? lchmod : chmod
: } else
: chmod
For the testing purposes, I'd suggest creating the following
structure, which should be self-explaining:
afile
alink -> afile
deadlink -> nofile
dir
adir -> dir
dir/afile
dir/alink -> afile
dir/deadlink -> nofile
PLEASE TEST THIS PATCH THROUGHLY!
NOTE: This implementation is still not quite POSIX compliant, as
the latter says to follow a symlink (in the -R case) only if it
points to an object of type directory. Our fts(3) routines are
unable of doing this.
If this patch is accepted, I'm going to revisit and fix the rest
of the fts(3) utilities that are listed on the symlink(7) manpage.
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
--2fHTh5uZTiUOsy+g
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=p
Index: chown.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/chown/chown.c,v
retrieving revision 1.19
diff -u -p -r1.19 chown.c
--- chown.c 2001/09/13 14:55:59 1.19
+++ chown.c 2001/09/13 16:54:28
@@ -80,6 +80,7 @@ main(argc, argv)
int Hflag, Lflag, Rflag, fflag, hflag, vflag;
int ch, fts_options, rval;
char *cp;
+ int (*change_owner) __P((const char *, uid_t, gid_t));
cp = strrchr(argv[0], '/');
cp = (cp != NULL) ? cp + 1 : argv[0];
@@ -121,19 +122,15 @@ main(argc, argv)
if (argc < 2)
usage();
+ fts_options = FTS_PHYSICAL;
if (Rflag) {
- fts_options = FTS_PHYSICAL;
- if (hflag && (Hflag || Lflag))
- errx(1, "the -R%c and -h options may not be "
- "specified together", Hflag ? 'H' : 'L');
if (Hflag)
fts_options |= FTS_COMFOLLOW;
else if (Lflag) {
fts_options &= ~FTS_PHYSICAL;
fts_options |= FTS_LOGICAL;
}
- } else
- fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
+ }
uid = (uid_t)-1;
gid = (gid_t)-1;
@@ -156,6 +153,7 @@ main(argc, argv)
err(1, NULL);
for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+ change_owner = chown;
switch (p->fts_info) {
case FTS_D: /* Change it at FTS_DP. */
if (!Rflag)
@@ -170,31 +168,48 @@ main(argc, argv)
warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
rval = 1;
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.
+ * don't point to anything. Note that if we are
+ * doing a physical walk, we never reach here unless
+ * we asked to follow explicitly.
*/
- if (hflag)
- break;
- else
+ /* FALLTHROUGH */
+ case FTS_SL:
+ /*
+ * All symlinks we found while doing a physical
+ * walk end up here.
+ */
+ if (Rflag && !hflag)
continue;
+ /*
+ * Note that if we follow a symlink, fts_info is
+ * not FTS_SL but FTS_F or whatever. And we should
+ * use lchown(2) only for FTS_SL and should use
+ * chown(2) for others.
+ */
+ if (hflag)
+ change_owner = lchown;
+ break;
default:
break;
}
- if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) &&
- (gid == (gid_t)-1 || gid == p->fts_statp->st_gid))
- continue;
- if ((hflag ? lchown : chown)(p->fts_accpath, uid, gid) == -1) {
+ if ((*change_owner)(p->fts_accpath, uid, gid) == -1) {
if (!fflag) {
chownerr(p->fts_path);
rval = 1;
}
} else {
if (vflag)
+#define DEBUG
+#ifdef DEBUG
+ printf("%s %s\n",
+ (change_owner == chown) ? "chown" : "lchown",
+ p->fts_path);
+#else
printf("%s\n", p->fts_path);
+#endif
}
}
if (errno)
--2fHTh5uZTiUOsy+g--
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?20010913202742.C2458>
