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;

Reply via email to