Skip to content

Commit

Permalink
fat: relax the permission check of fat_setattr()
Browse files Browse the repository at this point in the history
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: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
OGAWA Hirofumi authored and Linus Torvalds committed Jun 13, 2008
1 parent c700be3 commit 2d518f8
Showing 1 changed file with 27 additions and 17 deletions.
44 changes: 27 additions & 17 deletions fs/fat/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,26 +257,34 @@ 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, struct inode *inode,
mode_t mode)
static int fat_sanitize_mode(const struct msdos_sb_info *sbi,
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;
else
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
* w bits, either all (subject to umask) or none must be present.
*/
req &= ~mask;
if ((req & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
return -EPERM;
if ((req & S_IWUGO) && ((req & S_IWUGO) != (S_IWUGO & ~mask)))
if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
return -EPERM;

*mode_ptr &= S_IFMT | perm;

return 0;
}

Expand All @@ -299,7 +307,7 @@ 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;
int error = 0;
unsigned int ia_valid;

lock_kernel();
Expand Down Expand Up @@ -332,12 +340,13 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
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)) ||
((attr->ia_valid & ATTR_MODE) &&
fat_check_mode(sbi, inode, attr->ia_mode) < 0))
(attr->ia_mode & ~MSDOS_VALID_MODE)))
error = -EPERM;

if (error) {
Expand All @@ -346,15 +355,16 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
goto out;
}

error = inode_setattr(inode, attr);
if (error)
goto out;
/*
* We don't return -EPERM here. Yes, strange, but this is too
* 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))
mask = sbi->options.fs_dmask;
else
mask = sbi->options.fs_fmask;
inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask);
error = inode_setattr(inode, attr);
out:
unlock_kernel();
return error;
Expand Down

0 comments on commit 2d518f8

Please sign in to comment.