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 = {