+ /* 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;
+ }
+
+ /* The root directory has no attributes */
+ if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
+ err = -EINVAL;
+ goto up;
+ }
+
+ if (sbi->options.sys_immutable) {
+ if ((attr | oldattr) & ATTR_SYS) {
+ if (!capable(CAP_LINUX_IMMUTABLE)) {
+ err = -EPERM;
+ goto up;
+ }
+ }
+ }
+
+ /* This MUST be done before doing anything irreversible... */
+ err = notify_change(filp->f_path.dentry, &ia);
+ if (err)
+ goto up;
+
+ if (sbi->options.sys_immutable) {
+ if (attr & ATTR_SYS)
+ inode->i_flags |= S_IMMUTABLE;
+ else
+ inode->i_flags &= S_IMMUTABLE;
+ }
+
+ MSDOS_I(inode)->i_attrs = attr & ATTR_UNUSED;
+ mark_inode_dirty(inode);
+ up:
+ mutex_unlock(&inode->i_mutex);
+ return err;
+ }
+ default:
+ return -ENOTTY; /* Inappropriate ioctl for device */
+ }
+}
+
+static int fat_file_release(struct inode *inode, struct file *filp)
+{
+ if ((filp->f_mode & FMODE_WRITE) &&
+ MSDOS_SB(inode->i_sb)->options.flush) {
+ fat_flush_inodes(inode->i_sb, inode, NULL);
+ congestion_wait(WRITE, HZ/10);
+ }
+ return 0;
+}
+
+const struct file_operations fat_file_operations = {