ext4: convert to fileattr
authorMiklos Szeredi <mszeredi@redhat.com>
Wed, 7 Apr 2021 12:36:43 +0000 (14:36 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Mon, 12 Apr 2021 13:04:29 +0000 (15:04 +0200)
Use the fileattr API to let the VFS handle locking, permission checking and
conversion.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Cc: "Theodore Ts'o" <tytso@mit.edu>
fs/ext4/ext4.h
fs/ext4/file.c
fs/ext4/ioctl.c
fs/ext4/namei.c

index 826a56e..18f021c 100644 (file)
@@ -472,15 +472,6 @@ struct flex_groups {
                                         EXT4_VERITY_FL | \
                                         EXT4_INLINE_DATA_FL)
 
-/* Flags we can manipulate with through FS_IOC_FSSETXATTR */
-#define EXT4_FL_XFLAG_VISIBLE          (EXT4_SYNC_FL | \
-                                        EXT4_IMMUTABLE_FL | \
-                                        EXT4_APPEND_FL | \
-                                        EXT4_NODUMP_FL | \
-                                        EXT4_NOATIME_FL | \
-                                        EXT4_PROJINHERIT_FL | \
-                                        EXT4_DAX_FL)
-
 /* Flags that should be inherited by new inodes from their parent. */
 #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\
                           EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\
@@ -2928,6 +2919,9 @@ extern int ext4_ind_remove_space(handle_t *handle, struct inode *inode,
 /* ioctl.c */
 extern long ext4_ioctl(struct file *, unsigned int, unsigned long);
 extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long);
+int ext4_fileattr_set(struct user_namespace *mnt_userns,
+                     struct dentry *dentry, struct fileattr *fa);
+int ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa);
 extern void ext4_reset_inode_seed(struct inode *inode);
 
 /* migrate.c */
index 194f5d0..5332dd3 100644 (file)
@@ -919,5 +919,7 @@ const struct inode_operations ext4_file_inode_operations = {
        .get_acl        = ext4_get_acl,
        .set_acl        = ext4_set_acl,
        .fiemap         = ext4_fiemap,
+       .fileattr_get   = ext4_fileattr_get,
+       .fileattr_set   = ext4_fileattr_set,
 };
 
index a2cf350..e9b0a1f 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/uaccess.h>
 #include <linux/delay.h>
 #include <linux/iversion.h>
+#include <linux/fileattr.h>
 #include "ext4_jbd2.h"
 #include "ext4.h"
 #include <linux/fsmap.h>
@@ -344,11 +345,6 @@ static int ext4_ioctl_setflags(struct inode *inode,
                goto flags_out;
 
        oldflags = ei->i_flags;
-
-       err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
-       if (err)
-               goto flags_out;
-
        /*
         * The JOURNAL_DATA flag can only be changed by
         * the relevant capability.
@@ -459,9 +455,8 @@ flags_out:
 }
 
 #ifdef CONFIG_QUOTA
-static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
+static int ext4_ioctl_setproject(struct inode *inode, __u32 projid)
 {
-       struct inode *inode = file_inode(filp);
        struct super_block *sb = inode->i_sb;
        struct ext4_inode_info *ei = EXT4_I(inode);
        int err, rc;
@@ -545,7 +540,7 @@ out_stop:
        return err;
 }
 #else
-static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
+static int ext4_ioctl_setproject(struct inode *inode, __u32 projid)
 {
        if (projid != EXT4_DEF_PROJID)
                return -EOPNOTSUPP;
@@ -553,56 +548,6 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
 }
 #endif
 
-/* Transfer internal flags to xflags */
-static inline __u32 ext4_iflags_to_xflags(unsigned long iflags)
-{
-       __u32 xflags = 0;
-
-       if (iflags & EXT4_SYNC_FL)
-               xflags |= FS_XFLAG_SYNC;
-       if (iflags & EXT4_IMMUTABLE_FL)
-               xflags |= FS_XFLAG_IMMUTABLE;
-       if (iflags & EXT4_APPEND_FL)
-               xflags |= FS_XFLAG_APPEND;
-       if (iflags & EXT4_NODUMP_FL)
-               xflags |= FS_XFLAG_NODUMP;
-       if (iflags & EXT4_NOATIME_FL)
-               xflags |= FS_XFLAG_NOATIME;
-       if (iflags & EXT4_PROJINHERIT_FL)
-               xflags |= FS_XFLAG_PROJINHERIT;
-       if (iflags & EXT4_DAX_FL)
-               xflags |= FS_XFLAG_DAX;
-       return xflags;
-}
-
-#define EXT4_SUPPORTED_FS_XFLAGS (FS_XFLAG_SYNC | FS_XFLAG_IMMUTABLE | \
-                                 FS_XFLAG_APPEND | FS_XFLAG_NODUMP | \
-                                 FS_XFLAG_NOATIME | FS_XFLAG_PROJINHERIT | \
-                                 FS_XFLAG_DAX)
-
-/* Transfer xflags flags to internal */
-static inline unsigned long ext4_xflags_to_iflags(__u32 xflags)
-{
-       unsigned long iflags = 0;
-
-       if (xflags & FS_XFLAG_SYNC)
-               iflags |= EXT4_SYNC_FL;
-       if (xflags & FS_XFLAG_IMMUTABLE)
-               iflags |= EXT4_IMMUTABLE_FL;
-       if (xflags & FS_XFLAG_APPEND)
-               iflags |= EXT4_APPEND_FL;
-       if (xflags & FS_XFLAG_NODUMP)
-               iflags |= EXT4_NODUMP_FL;
-       if (xflags & FS_XFLAG_NOATIME)
-               iflags |= EXT4_NOATIME_FL;
-       if (xflags & FS_XFLAG_PROJINHERIT)
-               iflags |= EXT4_PROJINHERIT_FL;
-       if (xflags & FS_XFLAG_DAX)
-               iflags |= EXT4_DAX_FL;
-
-       return iflags;
-}
-
 static int ext4_shutdown(struct super_block *sb, unsigned long arg)
 {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
@@ -770,15 +715,52 @@ group_add_out:
        return err;
 }
 
-static void ext4_fill_fsxattr(struct inode *inode, struct fsxattr *fa)
+int ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 {
+       struct inode *inode = d_inode(dentry);
        struct ext4_inode_info *ei = EXT4_I(inode);
+       u32 flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
 
-       simple_fill_fsxattr(fa, ext4_iflags_to_xflags(ei->i_flags &
-                                                     EXT4_FL_USER_VISIBLE));
+       if (S_ISREG(inode->i_mode))
+               flags &= ~FS_PROJINHERIT_FL;
 
+       fileattr_fill_flags(fa, flags);
        if (ext4_has_feature_project(inode->i_sb))
                fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid);
+
+       return 0;
+}
+
+int ext4_fileattr_set(struct user_namespace *mnt_userns,
+                     struct dentry *dentry, struct fileattr *fa)
+{
+       struct inode *inode = d_inode(dentry);
+       u32 flags = fa->flags;
+       int err = -EOPNOTSUPP;
+
+       ext4_fc_start_update(inode);
+       if (flags & ~EXT4_FL_USER_VISIBLE)
+               goto out;
+
+       /*
+        * chattr(1) grabs flags via GETFLAGS, modifies the result and
+        * passes that to SETFLAGS. So we cannot easily make SETFLAGS
+        * more restrictive than just silently masking off visible but
+        * not settable flags as we always did.
+        */
+       flags &= EXT4_FL_USER_MODIFIABLE;
+       if (ext4_mask_flags(inode->i_mode, flags) != flags)
+               goto out;
+       err = ext4_ioctl_check_immutable(inode, fa->fsx_projid, flags);
+       if (err)
+               goto out;
+       err = ext4_ioctl_setflags(inode, flags);
+       if (err)
+               goto out;
+       err = ext4_ioctl_setproject(inode, fa->fsx_projid);
+out:
+       ext4_fc_stop_update(inode);
+       return err;
 }
 
 /* So that the fiemap access checks can't overflow on 32 bit machines. */
@@ -816,55 +798,13 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        struct inode *inode = file_inode(filp);
        struct super_block *sb = inode->i_sb;
-       struct ext4_inode_info *ei = EXT4_I(inode);
        struct user_namespace *mnt_userns = file_mnt_user_ns(filp);
-       unsigned int flags;
 
        ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
 
        switch (cmd) {
        case FS_IOC_GETFSMAP:
                return ext4_ioc_getfsmap(sb, (void __user *)arg);
-       case FS_IOC_GETFLAGS:
-               flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
-               if (S_ISREG(inode->i_mode))
-                       flags &= ~EXT4_PROJINHERIT_FL;
-               return put_user(flags, (int __user *) arg);
-       case FS_IOC_SETFLAGS: {
-               int err;
-
-               if (!inode_owner_or_capable(mnt_userns, inode))
-                       return -EACCES;
-
-               if (get_user(flags, (int __user *) arg))
-                       return -EFAULT;
-
-               if (flags & ~EXT4_FL_USER_VISIBLE)
-                       return -EOPNOTSUPP;
-               /*
-                * chattr(1) grabs flags via GETFLAGS, modifies the result and
-                * passes that to SETFLAGS. So we cannot easily make SETFLAGS
-                * more restrictive than just silently masking off visible but
-                * not settable flags as we always did.
-                */
-               flags &= EXT4_FL_USER_MODIFIABLE;
-               if (ext4_mask_flags(inode->i_mode, flags) != flags)
-                       return -EOPNOTSUPP;
-
-               err = mnt_want_write_file(filp);
-               if (err)
-                       return err;
-
-               inode_lock(inode);
-               err = ext4_ioctl_check_immutable(inode,
-                               from_kprojid(&init_user_ns, ei->i_projid),
-                               flags);
-               if (!err)
-                       err = ext4_ioctl_setflags(inode, flags);
-               inode_unlock(inode);
-               mnt_drop_write_file(filp);
-               return err;
-       }
        case EXT4_IOC_GETVERSION:
        case EXT4_IOC_GETVERSION_OLD:
                return put_user(inode->i_generation, (int __user *) arg);
@@ -1246,60 +1186,6 @@ resizefs_out:
        case EXT4_IOC_GET_ES_CACHE:
                return ext4_ioctl_get_es_cache(filp, arg);
 
-       case FS_IOC_FSGETXATTR:
-       {
-               struct fsxattr fa;
-
-               ext4_fill_fsxattr(inode, &fa);
-
-               if (copy_to_user((struct fsxattr __user *)arg,
-                                &fa, sizeof(fa)))
-                       return -EFAULT;
-               return 0;
-       }
-       case FS_IOC_FSSETXATTR:
-       {
-               struct fsxattr fa, old_fa;
-               int err;
-
-               if (copy_from_user(&fa, (struct fsxattr __user *)arg,
-                                  sizeof(fa)))
-                       return -EFAULT;
-
-               /* Make sure caller has proper permission */
-               if (!inode_owner_or_capable(mnt_userns, inode))
-                       return -EACCES;
-
-               if (fa.fsx_xflags & ~EXT4_SUPPORTED_FS_XFLAGS)
-                       return -EOPNOTSUPP;
-
-               flags = ext4_xflags_to_iflags(fa.fsx_xflags);
-               if (ext4_mask_flags(inode->i_mode, flags) != flags)
-                       return -EOPNOTSUPP;
-
-               err = mnt_want_write_file(filp);
-               if (err)
-                       return err;
-
-               inode_lock(inode);
-               ext4_fill_fsxattr(inode, &old_fa);
-               err = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa);
-               if (err)
-                       goto out;
-               flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) |
-                        (flags & EXT4_FL_XFLAG_VISIBLE);
-               err = ext4_ioctl_check_immutable(inode, fa.fsx_projid, flags);
-               if (err)
-                       goto out;
-               err = ext4_ioctl_setflags(inode, flags);
-               if (err)
-                       goto out;
-               err = ext4_ioctl_setproject(filp, fa.fsx_projid);
-out:
-               inode_unlock(inode);
-               mnt_drop_write_file(filp);
-               return err;
-       }
        case EXT4_IOC_SHUTDOWN:
                return ext4_shutdown(sb, arg);
 
@@ -1340,12 +1226,6 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        /* These are just misnamed, they actually get/put from/to user an int */
        switch (cmd) {
-       case FS_IOC32_GETFLAGS:
-               cmd = FS_IOC_GETFLAGS;
-               break;
-       case FS_IOC32_SETFLAGS:
-               cmd = FS_IOC_SETFLAGS;
-               break;
        case EXT4_IOC32_GETVERSION:
                cmd = EXT4_IOC_GETVERSION;
                break;
@@ -1405,8 +1285,6 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case EXT4_IOC_CLEAR_ES_CACHE:
        case EXT4_IOC_GETSTATE:
        case EXT4_IOC_GET_ES_CACHE:
-       case FS_IOC_FSGETXATTR:
-       case FS_IOC_FSSETXATTR:
                break;
        default:
                return -ENOIOCTLCMD;
index 883e2a7..a37a19f 100644 (file)
@@ -4172,6 +4172,8 @@ const struct inode_operations ext4_dir_inode_operations = {
        .get_acl        = ext4_get_acl,
        .set_acl        = ext4_set_acl,
        .fiemap         = ext4_fiemap,
+       .fileattr_get   = ext4_fileattr_get,
+       .fileattr_set   = ext4_fileattr_set,
 };
 
 const struct inode_operations ext4_special_inode_operations = {