>From c7bc43a05e4f021db8047ecd390b4fb75368f558 Mon Sep 17 00:00:00 2001 From: Jeff Liu <jeff....@oracle.com> Date: Fri, 5 Feb 2010 20:45:41 +0800 Subject: [PATCH 1/1] Modify chmod v1 do not touch the inode if the new file permission bits is identical to it was before.
Signed-off-by: Jie Liu <jeff....@oracle.com> --- gnulib | 2 +- src/chmod.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/gnulib b/gnulib index 2eb5a8a..4b93a25 160000 --- a/gnulib +++ b/gnulib @@ -1 +1 @@ -Subproject commit 2eb5a8a0ff8348149a9ca985e2ccbfb03bc8de53 +Subproject commit 4b93a2579fb567b9fbbeb24439770d814dac95cd diff --git a/src/chmod.c b/src/chmod.c index 3dab92e..30802d7 100644 --- a/src/chmod.c +++ b/src/chmod.c @@ -20,6 +20,7 @@ #include <stdio.h> #include <getopt.h> #include <sys/types.h> +#include <unistd.h> #include "system.h" #include "dev-ino.h" @@ -32,6 +33,16 @@ #include "root-dev-ino.h" #include "xfts.h" +/* Some systems only have <sys/vfs.h>, other systems also + * have <sys/statfs.h>, where the former includes the latter. + * So it seems including the former is the best choice. */ +#include "fs.h" +#if HAVE_SYS_VFS_H +# include <sys/vfs.h> +#else +# include <sys/statfs.h> +#endif + /* The official name of this program (e.g., no `g' prefix). */ #define PROGRAM_NAME "chmod" @@ -83,6 +94,9 @@ static enum Verbosity verbosity = V_off; Otherwise NULL. */ static struct dev_ino *root_dev_ino; +/* The effective user ID of the caller process. */ +static uid_t euid; + /* For long options that have no equivalent short option, use a non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum @@ -170,6 +184,25 @@ describe_change (const char *file, mode_t mode, (unsigned long int) (mode & CHMOD_MODE_BITS), &perms[1]); } +/* Return true if the file resides on NFS filesystem. + * limit this optimization to systems that provide statfs. */ + +static bool +may_have_nfsacl(const char *file) +{ +# if HAVE_SYS_VFS_H && HAVE_SYS_STATFS_H && HAVE_STRUCT_STATFS_F_TYPE + struct statfs buf; + + /* If statfs fails, assume we can't use the optimization. */ + if (statfs (file, &buf) < 0) + return true; + + return buf.f_type == S_MAGIC_NFS; +#endif + + return true; +} + /* Change the mode of FILE. Return true if successful. This function is called once for every file system object that fts encounters. */ @@ -257,18 +290,38 @@ process_file (FTS *fts, FTSENT *ent) old_mode = file_stats->st_mode; new_mode = mode_adjust (old_mode, S_ISDIR (old_mode) != 0, umask_value, change, NULL); - - if (! S_ISLNK (old_mode)) + + /* Do not touch the inode if the new file mode is the same as it was before; + * This is an optimization for some cases. However, the new behavior must be + * disabled for filesystems that support NFSv4 ACLs. + * The idea suggested by Paul Eggert refer to FreeBSD chmod(1). + * it uses pathconf(2)/lpathconf(2) to determine whether this is the case. + * but on linux, we lack of them. Instead, calling statfs(2) and compare the + * f_type we can still do optimization at least its not NFS. */ + + if ((old_mode & CHMOD_MODE_BITS) == new_mode && + !may_have_nfsacl (file_full_name)) { - if (chmodat (fts->fts_cwd_fd, file, new_mode) == 0) - chmod_succeeded = true; + if (file_stats->st_uid != euid && euid != 0) + error (0, 0, _("changing permissions of %s: Operation not permitted"), + quote (file_full_name)); else - { - if (! force_silent) - error (0, errno, _("changing permissions of %s"), - quote (file_full_name)); - ok = false; - } + chmod_succeeded = true; + } + else + { + if (! S_ISLNK (old_mode)) + { + if (chmodat (fts->fts_cwd_fd, file, new_mode) == 0) + chmod_succeeded = true; + else + { + if (! force_silent) + error (0, errno, _("changing permissions of %s"), + quote (file_full_name)); + ok = false; + } + } } } @@ -545,6 +598,8 @@ main (int argc, char **argv) root_dev_ino = NULL; } + euid = geteuid (); + ok = process_files (argv + optind, FTS_COMFOLLOW | FTS_PHYSICAL | FTS_DEFER_STAT); -- 1.5.4.3