#include <linux/module.h>
#include <linux/mount.h>
#include <linux/time.h>
-#include <linux/msdos_fs.h>
-#include <linux/smp_lock.h>
#include <linux/buffer_head.h>
#include <linux/writeback.h>
#include <linux/backing-dev.h>
#include <linux/blkdev.h>
+#include <linux/fsnotify.h>
+#include <linux/security.h>
+#include "fat.h"
int fat_generic_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
u32 attr;
- if (inode->i_ino == MSDOS_ROOT_INO)
- attr = ATTR_DIR;
- else
- attr = fat_attr(inode);
+ mutex_lock(&inode->i_mutex);
+ attr = fat_make_attrs(inode);
+ mutex_unlock(&inode->i_mutex);
return put_user(attr, user_attr);
}
/* Merge in ATTR_VOLUME and ATTR_DIR */
attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
(is_dir ? ATTR_DIR : 0);
- oldattr = fat_attr(inode);
+ oldattr = fat_make_attrs(inode);
/* Equivalent to a chmod() */
ia.ia_valid = ATTR_MODE | ATTR_CTIME;
- if (is_dir) {
- ia.ia_mode = MSDOS_MKMODE(attr,
- S_IRWXUGO & ~sbi->options.fs_dmask)
- | S_IFDIR;
- } else {
- ia.ia_mode = MSDOS_MKMODE(attr,
- (S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO))
- & ~sbi->options.fs_fmask)
- | S_IFREG;
+ ia.ia_ctime = current_fs_time(inode->i_sb);
+ if (is_dir)
+ ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO);
+ else {
+ ia.ia_mode = fat_make_mode(sbi, attr,
+ S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO));
}
/* The root directory has no attributes */
}
}
+ /*
+ * The security check is questionable... We single
+ * out the RO attribute for checking by the security
+ * module, just because it maps to a file mode.
+ */
+ err = security_inode_setattr(filp->f_path.dentry, &ia);
+ if (err)
+ goto up;
+
/* This MUST be done before doing anything irreversible... */
- err = notify_change(filp->f_path.dentry, &ia);
+ err = fat_setattr(filp->f_path.dentry, &ia);
if (err)
goto up;
+ fsnotify_change(filp->f_path.dentry, ia.ia_valid);
if (sbi->options.sys_immutable) {
if (attr & ATTR_SYS)
inode->i_flags |= S_IMMUTABLE;
inode->i_flags &= S_IMMUTABLE;
}
- MSDOS_I(inode)->i_attrs = attr & ATTR_UNUSED;
+ fat_save_attrs(inode, attr);
mark_inode_dirty(inode);
up:
mnt_drop_write(filp->f_path.mnt);
fatent_brelse(&fatent);
return 0;
} else if (ret == FAT_ENT_FREE) {
- fat_fs_panic(sb,
+ fat_fs_error(sb,
"%s: invalid cluster chain (i_pos %lld)",
- __FUNCTION__, MSDOS_I(inode)->i_pos);
+ __func__, MSDOS_I(inode)->i_pos);
ret = -EIO;
} else if (ret > 0) {
err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait);
nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits;
- lock_kernel();
fat_free(inode, nr_clusters);
- unlock_kernel();
fat_flush_inodes(inode->i_sb, inode, NULL);
}
}
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 & ~FAT_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.
+ *
+ * If fat_mode_can_hold_ro(inode) is false, can't change w bits.
*/
- req &= ~mask;
- if ((req & (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_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
return -EPERM;
+ if (fat_mode_can_hold_ro(inode)) {
+ if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
+ return -EPERM;
+ } else {
+ if ((perm & S_IWUGO) != (S_IWUGO & ~mask))
+ return -EPERM;
+ }
+
+ *mode_ptr &= S_IFMT | perm;
return 0;
}
+static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
+{
+ mode_t allow_utime = sbi->options.allow_utime;
+
+ if (current_fsuid() != inode->i_uid) {
+ if (in_group_p(inode->i_gid))
+ allow_utime >>= 3;
+ if (allow_utime & MAY_WRITE)
+ return 1;
+ }
+
+ /* use a default check */
+ return 0;
+}
+
+#define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
+/* valid file mode bits */
+#define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO)
+
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();
+ unsigned int ia_valid;
+ int error;
/*
* Expand the file. Since inode_setattr() updates ->i_size
}
}
+ /* Check for setting the inode time. */
+ ia_valid = attr->ia_valid;
+ if (ia_valid & TIMES_SET_FLAGS) {
+ if (fat_allow_set_time(sbi, inode))
+ attr->ia_valid &= ~TIMES_SET_FLAGS;
+ }
+
error = inode_change_ok(inode, attr);
+ attr->ia_valid = ia_valid;
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)) ||
((attr->ia_valid & ATTR_MODE) &&
- fat_check_mode(sbi, inode, attr->ia_mode) < 0))
+ (attr->ia_mode & ~FAT_VALID_MODE)))
error = -EPERM;
if (error) {
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);
+ if (attr->ia_valid)
+ error = inode_setattr(inode, attr);
out:
- unlock_kernel();
return error;
}
EXPORT_SYMBOL_GPL(fat_setattr);