From c340dfdee2aa8a2d2cf507ce99416eb7c71e8437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20A=2E=20Fern=C3=A1ndez?= Date: Fri, 10 Sep 2021 15:12:38 -0300 Subject: [PATCH] Fix SETFLAGS/GETFLAGS ioctls for newer kernels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Luflosi is reporting new build issues: https://github.com/linux-apfs/linux-apfs-rw/issues/19 These problems started when I implemented the FS_IOC_SETFLAGS and FS_IOC_GETFLAGS ioctls. These ioctls have become officially supported by the vfs recently, and then some of the helpers I was using got removed. The proper way to implement this in newer kernels is through inode methods ->apfs_fileattr_get() and ->apfs_fileattr_set(). So do that, and also fix a new instance of the typical build issue with new namespace parameters. This time I've taken the trouble to build-test this with a 5.13 kernel, and I even ran xfstests, so there shouldn't be any more problems, at least not with that version. Signed-off-by: Ernesto A. Fernández --- apfs.h | 5 ++++ file.c | 4 +++ inode.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++---------- namei.c | 4 +++ 4 files changed, 86 insertions(+), 14 deletions(-) diff --git a/apfs.h b/apfs.h index 64be131..e5a41a3 100644 --- a/apfs.h +++ b/apfs.h @@ -788,6 +788,11 @@ extern int apfs_getattr(struct user_namespace *mnt_userns, extern int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta); extern int APFS_CRYPTO_ADJ_REFCNT_MAXOPS(void); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) +extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa); +extern int apfs_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa); +#endif + /* key.c */ extern int apfs_filename_cmp(struct super_block *sb, const char *name1, const char *name2); diff --git a/file.c b/file.c index 348b5af..c443436 100644 --- a/file.c +++ b/file.c @@ -131,4 +131,8 @@ const struct inode_operations apfs_file_inode_operations = { .listxattr = apfs_listxattr, .setattr = apfs_setattr, .update_time = apfs_update_time, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + .fileattr_get = apfs_fileattr_get, + .fileattr_set = apfs_fileattr_set, +#endif }; diff --git a/inode.c b/inode.c index 358ef61..a71173f 100644 --- a/inode.c +++ b/inode.c @@ -10,6 +10,10 @@ #include #include "apfs.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) +#include +#endif + #define MAX_PFK_LEN 512 static int apfs_readpage(struct file *file, struct page *page) @@ -1756,20 +1760,6 @@ static unsigned int apfs_getflags(struct inode *inode) return flags; } -/** - * apfs_ioc_getflags - Ioctl handler for FS_IOC_GETFLAGS - * @file: affected file - * @arg: ioctl argument - * - * Returns 0 on success, or a negative error code in case of failure. - */ -static int apfs_ioc_getflags(struct file *file, int __user *arg) -{ - unsigned int flags = apfs_getflags(file_inode(file)); - - return put_user(flags, arg); -} - /** * apfs_setflags - Set an inode's bsd flags * @inode: the vfs inode @@ -1802,6 +1792,22 @@ static void apfs_setflags(struct inode *inode, unsigned int flags) inode_set_flags(inode, i_flags, S_IMMUTABLE | S_APPEND); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) + +/** + * apfs_ioc_getflags - Ioctl handler for FS_IOC_GETFLAGS + * @file: affected file + * @arg: ioctl argument + * + * Returns 0 on success, or a negative error code in case of failure. + */ +static int apfs_ioc_getflags(struct file *file, int __user *arg) +{ + unsigned int flags = apfs_getflags(file_inode(file)); + + return put_user(flags, arg); +} + /** * apfs_do_ioc_setflags - Actual work for apfs_ioc_setflags(), after preparation * @inode: affected vfs inode @@ -1856,7 +1862,11 @@ static int apfs_ioc_setflags(struct file *file, int __user *arg) if (sb->s_flags & SB_RDONLY) return -EROFS; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) if (!inode_owner_or_capable(inode)) +#else + if (!inode_owner_or_capable(&init_user_ns, inode)) +#endif return -EPERM; if (get_user(newflags, arg)) @@ -1877,15 +1887,62 @@ static int apfs_ioc_setflags(struct file *file, int __user *arg) return err; } +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) */ + +int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa) +{ + unsigned int flags = apfs_getflags(d_inode(dentry)); + + fileattr_fill_flags(fa, flags); + return 0; +} + +int apfs_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa) +{ + struct inode *inode = d_inode(dentry); + struct super_block *sb = inode->i_sb; + struct apfs_max_ops maxops; + int err; + + if (sb->s_flags & SB_RDONLY) + return -EROFS; + + if (fa->flags & ~(FS_APPEND_FL | FS_IMMUTABLE_FL | FS_NODUMP_FL)) + return -EOPNOTSUPP; + if (fileattr_has_fsx(fa)) + return -EOPNOTSUPP; + + lockdep_assert_held_write(&inode->i_rwsem); + + maxops.cat = APFS_UPDATE_INODE_MAXOPS(); + maxops.blks = 0; + err = apfs_transaction_start(sb, maxops); + if (err) + return err; + + apfs_inode_join_transaction(sb, inode); + apfs_setflags(inode, fa->flags); + inode->i_ctime = current_time(inode); + + err = apfs_transaction_commit(sb); + if (err) + apfs_transaction_abort(sb); + return err; +} + +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) */ + long apfs_dir_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; switch (cmd) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) case FS_IOC_GETFLAGS: return apfs_ioc_getflags(file, argp); case FS_IOC_SETFLAGS: return apfs_ioc_setflags(file, argp); +#endif case APFS_IOC_SET_DFLT_PFK: return apfs_ioc_set_dflt_pfk(file, argp); case APFS_IOC_SET_DIR_CLASS: @@ -1902,10 +1959,12 @@ long apfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) void __user *argp = (void __user *)arg; switch (cmd) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) case FS_IOC_GETFLAGS: return apfs_ioc_getflags(file, argp); case FS_IOC_SETFLAGS: return apfs_ioc_setflags(file, argp); +#endif case APFS_IOC_SET_PFK: return apfs_ioc_set_pfk(file, argp); case APFS_IOC_GET_CLASS: diff --git a/namei.c b/namei.c index 158f206..96f2175 100644 --- a/namei.c +++ b/namei.c @@ -56,6 +56,10 @@ const struct inode_operations apfs_dir_inode_operations = { .listxattr = apfs_listxattr, .setattr = apfs_setattr, .update_time = apfs_update_time, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + .fileattr_get = apfs_fileattr_get, + .fileattr_set = apfs_fileattr_set, +#endif }; const struct inode_operations apfs_special_inode_operations = {