Fix SETFLAGS/GETFLAGS ioctls for newer kernels

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 <ernesto@corellium.com>
This commit is contained in:
Ernesto A. Fernández
2021-09-10 15:12:38 -03:00
parent fd5deb160b
commit c340dfdee2
4 changed files with 86 additions and 14 deletions
+5
View File
@@ -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);
+4
View File
@@ -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
};
+73 -14
View File
@@ -10,6 +10,10 @@
#include <linux/blk_types.h>
#include "apfs.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
#include <linux/fileattr.h>
#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:
+4
View File
@@ -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 = {