diff --git a/fs/fat/file.c b/fs/fat/file.c index 2a3bed967041..b61a98f5398a 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -157,104 +157,6 @@ 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); - struct inode *inode = dentry->d_inode; - int mask, error = 0; - - lock_kernel(); - - /* - * Expand the file. Since inode_setattr() updates ->i_size - * before calling the ->truncate(), but FAT needs to fill the - * hole before it. - */ - if (attr->ia_valid & ATTR_SIZE) { - if (attr->ia_size > inode->i_size) { - error = fat_cont_expand(inode, attr->ia_size); - if (error || attr->ia_valid == ATTR_SIZE) - goto out; - attr->ia_valid &= ~ATTR_SIZE; - } - } - - error = inode_change_ok(inode, attr); - if (error) { - if (sbi->options.quiet) - error = 0; - goto out; - } - 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))) - error = -EPERM; - - if (error) { - if (sbi->options.quiet) - 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; - - if (S_ISDIR(inode->i_mode)) - mask = sbi->options.fs_dmask; - else - mask = sbi->options.fs_fmask; - inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask); -out: - unlock_kernel(); - return error; -} - -EXPORT_SYMBOL_GPL(fat_notify_change); - /* Free all clusters after the skip'th cluster. */ static int fat_free(struct inode *inode, int skip) { @@ -355,8 +257,91 @@ int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) } EXPORT_SYMBOL_GPL(fat_getattr); +static int fat_check_mode(const struct msdos_sb_info *sbi, mode_t mode) +{ + mode_t mask, req = mode & ~S_IFMT; + + if (S_ISREG(mode)) + mask = sbi->options.fs_fmask; + else + mask = sbi->options.fs_dmask; + + /* + * 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. + */ + req &= ~mask; + if ((req & (S_IRUGO | S_IXUGO)) != ((S_IRUGO | S_IXUGO) & ~mask)) + return -EPERM; + if ((req & S_IWUGO) && ((req & S_IWUGO) != (S_IWUGO & ~mask))) + return -EPERM; + + return 0; +} + +int fat_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); + struct inode *inode = dentry->d_inode; + int mask, error = 0; + + lock_kernel(); + + /* + * Expand the file. Since inode_setattr() updates ->i_size + * before calling the ->truncate(), but FAT needs to fill the + * hole before it. + */ + if (attr->ia_valid & ATTR_SIZE) { + if (attr->ia_size > inode->i_size) { + error = fat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + goto out; + attr->ia_valid &= ~ATTR_SIZE; + } + } + + error = inode_change_ok(inode, attr); + if (error) { + if (sbi->options.quiet) + error = 0; + goto out; + } + 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))) + error = -EPERM; + + if (error) { + if (sbi->options.quiet) + error = 0; + goto out; + } + + if (attr->ia_valid & ATTR_MODE) { + error = fat_check_mode(sbi, attr->ia_mode); + if (error != 0 && !sbi->options.quiet) + goto out; + } + + error = inode_setattr(inode, attr); + if (error) + goto out; + + if (S_ISDIR(inode->i_mode)) + mask = sbi->options.fs_dmask; + else + mask = sbi->options.fs_fmask; + inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask); +out: + unlock_kernel(); + return error; +} +EXPORT_SYMBOL_GPL(fat_setattr); + const struct inode_operations fat_file_inode_operations = { .truncate = fat_truncate, - .setattr = fat_notify_change, + .setattr = fat_setattr, .getattr = fat_getattr, }; diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 30f7d0ae2215..2d4358c59f68 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -653,7 +653,7 @@ static const struct inode_operations msdos_dir_inode_operations = { .mkdir = msdos_mkdir, .rmdir = msdos_rmdir, .rename = msdos_rename, - .setattr = fat_notify_change, + .setattr = fat_setattr, .getattr = fat_getattr, }; diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index cd450bea9f1a..efc70576e4b4 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -1003,7 +1003,7 @@ static const struct inode_operations vfat_dir_inode_operations = { .mkdir = vfat_mkdir, .rmdir = vfat_rmdir, .rename = vfat_rename, - .setattr = fat_notify_change, + .setattr = fat_setattr, .getattr = fat_getattr, }; diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index f950921523f5..c4bed98bc00b 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -401,7 +401,7 @@ extern int fat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern const struct file_operations fat_file_operations; extern const struct inode_operations fat_file_inode_operations; -extern int fat_notify_change(struct dentry * dentry, struct iattr * attr); +extern int fat_setattr(struct dentry * dentry, struct iattr * attr); extern void fat_truncate(struct inode *inode); extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);