It seems that chown(1) will write to a file even if it already has the
desired ownership. The below patch causes it to skip the write when
there would be no change. The best I could tell, the fts_read(3) and
fchownat(3) logic agree on whether to follow symlinks in all cases, so
there's no need to execute a useless write when certain options are
specified.
I get a speedup of 5-10% on SSDs, and probably more on a slow storage
HD. This is when all the files are already owned by the specified user.
I think this is a common case, as chown is often used to "comb out"
offending files, ensure that a server can access everything in /var/www,
etc.
The APIs involved are not simple and there's potential for subtle
breakage, so this only an initial patch. I'm interested to hear what
more experienced people think.
If it's worthwhile, a similar approach can probably be applied to
chmod(1) et al. as well.
Thanks for your time,
Michael
Index: chmod.c
===================================================================
RCS file: /cvs/src/bin/chmod/chmod.c,v
retrieving revision 1.39
diff -u -p -r1.39 chmod.c
--- chmod.c 31 Dec 2015 23:38:16 -0000 1.39
+++ chmod.c 14 Mar 2016 04:01:39 -0000
@@ -63,8 +63,8 @@ main(int argc, char *argv[])
int oct;
mode_t omode;
int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, atflags;
- uid_t uid;
- gid_t gid;
+ uid_t uid, orig_uid;
+ gid_t gid, orig_gid;
u_int32_t fclear, fset;
char *ep, *mode, *cp, *flags;
@@ -213,7 +213,12 @@ done:
if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
err(1, NULL);
+
for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+
+ orig_uid = p->fts_statp->st_uid;
+ orig_gid = p->fts_statp->st_gid;
+
switch (p->fts_info) {
case FTS_D:
if (!Rflag)
@@ -265,6 +270,16 @@ done:
|| fflag)
continue;
} else if (!ischflags) {
+ /*
+ * Don't modify the file if it already has the
+ * specified ownership. fts_read(3) and
+ * fchownat(3) both handle the symlink logic, so
+ * we know we're deciding based on the right
+ * file.
+ */
+ if ((gid == -1 || gid == orig_gid) &&
+ (uid == -1 || uid == orig_uid))
+ continue;
if (!fchownat(AT_FDCWD, p->fts_accpath, uid, gid,
atflags) || fflag)
continue;