Commit 2d518f84 authored by OGAWA Hirofumi's avatar OGAWA Hirofumi Committed by Linus Torvalds

fat: relax the permission check of fat_setattr()

New chmod() allows only acceptable permission, and if not acceptable, it
returns -EPERM.  Old one allows even if it can't store permission to on
disk inode.  But it seems too strict for users.

E.g.  https://bugzilla.redhat.com/show_bug.cgi?id=449080: With new one,
rsync couldn't create the temporary file.

So, this patch allows like old one, but now it doesn't change the
permission if it can't store, and it returns 0.

Also, this patch fixes missing check.
Signed-off-by: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent c700be3d
...@@ -257,26 +257,34 @@ int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) ...@@ -257,26 +257,34 @@ int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
} }
EXPORT_SYMBOL_GPL(fat_getattr); EXPORT_SYMBOL_GPL(fat_getattr);
static int fat_check_mode(const struct msdos_sb_info *sbi, struct inode *inode, static int fat_sanitize_mode(const struct msdos_sb_info *sbi,
mode_t mode) struct inode *inode, umode_t *mode_ptr)
{ {
mode_t mask, req = mode & ~S_IFMT; mode_t mask, perm;
if (S_ISREG(mode)) /*
* Note, the basic check is already done by a caller of
* (attr->ia_mode & ~MSDOS_VALID_MODE)
*/
if (S_ISREG(inode->i_mode))
mask = sbi->options.fs_fmask; mask = sbi->options.fs_fmask;
else else
mask = sbi->options.fs_dmask; mask = sbi->options.fs_dmask;
perm = *mode_ptr & ~(S_IFMT | mask);
/* /*
* Of the r and x bits, all (subject to umask) must be present. Of the * 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. * w bits, either all (subject to umask) or none must be present.
*/ */
req &= ~mask; if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
if ((req & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
return -EPERM; return -EPERM;
if ((req & S_IWUGO) && ((req & S_IWUGO) != (S_IWUGO & ~mask))) if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
return -EPERM; return -EPERM;
*mode_ptr &= S_IFMT | perm;
return 0; return 0;
} }
...@@ -299,7 +307,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -299,7 +307,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
{ {
struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int mask, error = 0; int error = 0;
unsigned int ia_valid; unsigned int ia_valid;
lock_kernel(); lock_kernel();
...@@ -332,12 +340,13 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -332,12 +340,13 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
error = 0; error = 0;
goto out; goto out;
} }
if (((attr->ia_valid & ATTR_UID) && if (((attr->ia_valid & ATTR_UID) &&
(attr->ia_uid != sbi->options.fs_uid)) || (attr->ia_uid != sbi->options.fs_uid)) ||
((attr->ia_valid & ATTR_GID) && ((attr->ia_valid & ATTR_GID) &&
(attr->ia_gid != sbi->options.fs_gid)) || (attr->ia_gid != sbi->options.fs_gid)) ||
((attr->ia_valid & ATTR_MODE) && ((attr->ia_valid & ATTR_MODE) &&
fat_check_mode(sbi, inode, attr->ia_mode) < 0)) (attr->ia_mode & ~MSDOS_VALID_MODE)))
error = -EPERM; error = -EPERM;
if (error) { if (error) {
...@@ -346,15 +355,16 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -346,15 +355,16 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
goto out; goto out;
} }
error = inode_setattr(inode, attr); /*
if (error) * We don't return -EPERM here. Yes, strange, but this is too
goto out; * old behavior.
*/
if (attr->ia_valid & ATTR_MODE) {
if (fat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0)
attr->ia_valid &= ~ATTR_MODE;
}
if (S_ISDIR(inode->i_mode)) error = inode_setattr(inode, attr);
mask = sbi->options.fs_dmask;
else
mask = sbi->options.fs_fmask;
inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask);
out: out:
unlock_kernel(); unlock_kernel();
return error; return error;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment