Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=19c561a60ffe52df88dd63de0bff480ca094efe4
Commit:     19c561a60ffe52df88dd63de0bff480ca094efe4
Parent:     20292bc2c3feaee7f2e93911ffcb692732293894
Author:     Jan Engelhardt <[EMAIL PROTECTED]>
AuthorDate: Wed Feb 6 01:36:08 2008 -0800
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Wed Feb 6 10:40:59 2008 -0800

    fs/fat/: refine chmod checks
    
    Prohibit mode changes in non-quiet mode that cannot be stored reliably with
    the on-disk format.
    
    Suppose a vfat filesystem is mounted with umask=0 and [not-quiet].  Then
    all files will have mode 0777.  Trying to change the owner will fail,
    because fat does not know about owners or groups.  chmod 0770, on the other
    hand, will succeed, even though fat does not know about the permission
    triplet [user/group/other].
    
    So this patch changes fat's not-quiet behavior so that only UNIX modes are
    accepted that can be mapped lossless between the fat disk format and the
    local system.  There is only one attribute, and that is the readonly
    attribute, which is mapped to the UNIX write permission bit(s).  chmod 0555
    is therefore valid (taking away the +w bits <=> setting the readonly
    attribute).  Since chmod 0775 and chmod 0755 is an ambiguous case as to
    whether to set or clear the readonly bit, these modes are also denied.
    
    In quiet mode, chmod and chown will continue to "succeed" as they did
    before, meaning that a subsequent stat() will temporarily return the new
    mode as long as the inode is not reread from disk, and chown will silently
    do nothing, not even return the new uid/gid in stat().
    
    Signed-off-by: Jan Engelhardt <[EMAIL PROTECTED]>
    Cc: OGAWA Hirofumi <[EMAIL PROTECTED]>
    Signed-off-by: Andrew Morton <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 fs/fat/file.c |   47 ++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 44 insertions(+), 3 deletions(-)

diff --git a/fs/fat/file.c b/fs/fat/file.c
index 69a83b5..c614175 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -155,6 +155,42 @@ out:
        return err;
 }
 
+static int check_mode(const struct msdos_sb_info *sbi, mode_t mode)
+{
+       mode_t req = mode & ~S_IFMT;
+
+       /*
+        * Of the r and x bits, all (subject to umask) must be present. Of the
+        * w bits, either all (subject to umask) or none must be present.
+        */
+
+       if (S_ISREG(mode)) {
+               req &= ~sbi->options.fs_fmask;
+
+               if ((req & (S_IRUGO | S_IXUGO)) !=
+                   ((S_IRUGO | S_IXUGO) & ~sbi->options.fs_fmask))
+                       return -EPERM;
+
+               if ((req & S_IWUGO) != 0 &&
+                   (req & S_IWUGO) != (S_IWUGO & ~sbi->options.fs_fmask))
+                       return -EPERM;
+       } else if (S_ISDIR(mode)) {
+               req &= ~sbi->options.fs_dmask;
+
+               if ((req & (S_IRUGO | S_IXUGO)) !=
+                   ((S_IRUGO | S_IXUGO) & ~sbi->options.fs_dmask))
+                       return -EPERM;
+
+               if ((req & S_IWUGO) != 0 &&
+                   (req & S_IWUGO) != (S_IWUGO & ~sbi->options.fs_dmask))
+                       return -EPERM;
+       } else {
+               return -EPERM;
+       }
+
+       return 0;
+}
+
 int fat_notify_change(struct dentry *dentry, struct iattr *attr)
 {
        struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
@@ -186,9 +222,7 @@ int fat_notify_change(struct dentry *dentry, struct iattr 
*attr)
        if (((attr->ia_valid & ATTR_UID) &&
             (attr->ia_uid != sbi->options.fs_uid)) ||
            ((attr->ia_valid & ATTR_GID) &&
-            (attr->ia_gid != sbi->options.fs_gid)) ||
-           ((attr->ia_valid & ATTR_MODE) &&
-            (attr->ia_mode & ~MSDOS_VALID_MODE)))
+            (attr->ia_gid != sbi->options.fs_gid)))
                error = -EPERM;
 
        if (error) {
@@ -196,6 +230,13 @@ int fat_notify_change(struct dentry *dentry, struct iattr 
*attr)
                        error = 0;
                goto out;
        }
+
+       if (attr->ia_valid & ATTR_MODE) {
+               error = check_mode(sbi, attr->ia_mode);
+               if (error != 0 && !sbi->options.quiet)
+                       goto out;
+       }
+
        error = inode_setattr(inode, attr);
        if (error)
                goto out;
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to