Commit 5010fe9f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'vfs-fix-ioctl-checking-3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull common SETFLAGS/FSSETXATTR parameter checking from Darrick Wong:
 "Here's a patch series that sets up common parameter checking functions
  for the FS_IOC_SETFLAGS and FS_IOC_FSSETXATTR ioctl implementations.

  The goal here is to reduce the amount of behaviorial variance between
  the filesystems where those ioctls originated (ext2 and XFS,
  respectively) and everybody else.

   - Standardize parameter checking for the SETFLAGS and FSSETXATTR
     ioctls (which were the file attribute setters for ext4 and xfs and
     have now been hoisted to the vfs)

   - Only allow the DAX flag to be set on files and directories"

* tag 'vfs-fix-ioctl-checking-3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  vfs: only allow FSSETXATTR to set DAX flag on files and dirs
  vfs: teach vfs_ioc_fssetxattr_check to check extent size hints
  vfs: teach vfs_ioc_fssetxattr_check to check project id info
  vfs: create a generic checking function for FS_IOC_FSSETXATTR
  vfs: create a generic checking and prep function for FS_IOC_SETFLAGS
parents 8487d822 dbc77f31
...@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) ...@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode); struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root; struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
unsigned int fsflags; unsigned int fsflags, old_fsflags;
int ret; int ret;
const char *comp = NULL; const char *comp = NULL;
u32 binode_flags = binode->flags; u32 binode_flags = binode->flags;
...@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) ...@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode); inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags); fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) & old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
(FS_APPEND_FL | FS_IMMUTABLE_FL)) { ret = vfs_ioc_setflags_prepare(inode, old_fsflags, fsflags);
if (!capable(CAP_LINUX_IMMUTABLE)) { if (ret)
ret = -EPERM;
goto out_unlock; goto out_unlock;
}
}
if (fsflags & FS_SYNC_FL) if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC; binode_flags |= BTRFS_INODE_SYNC;
...@@ -376,9 +373,7 @@ static int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg) ...@@ -376,9 +373,7 @@ static int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(file_inode(file)); struct btrfs_inode *binode = BTRFS_I(file_inode(file));
struct fsxattr fa; struct fsxattr fa;
memset(&fa, 0, sizeof(fa)); simple_fill_fsxattr(&fa, btrfs_inode_flags_to_xflags(binode->flags));
fa.fsx_xflags = btrfs_inode_flags_to_xflags(binode->flags);
if (copy_to_user(arg, &fa, sizeof(fa))) if (copy_to_user(arg, &fa, sizeof(fa)))
return -EFAULT; return -EFAULT;
...@@ -391,7 +386,7 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg) ...@@ -391,7 +386,7 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode); struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root; struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
struct fsxattr fa; struct fsxattr fa, old_fa;
unsigned old_flags; unsigned old_flags;
unsigned old_i_flags; unsigned old_i_flags;
int ret = 0; int ret = 0;
...@@ -402,7 +397,6 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg) ...@@ -402,7 +397,6 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
if (btrfs_root_readonly(root)) if (btrfs_root_readonly(root))
return -EROFS; return -EROFS;
memset(&fa, 0, sizeof(fa));
if (copy_from_user(&fa, arg, sizeof(fa))) if (copy_from_user(&fa, arg, sizeof(fa)))
return -EFAULT; return -EFAULT;
...@@ -422,13 +416,11 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg) ...@@ -422,13 +416,11 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
old_flags = binode->flags; old_flags = binode->flags;
old_i_flags = inode->i_flags; old_i_flags = inode->i_flags;
/* We need the capabilities to change append-only or immutable inode */ simple_fill_fsxattr(&old_fa,
if (((old_flags & (BTRFS_INODE_APPEND | BTRFS_INODE_IMMUTABLE)) || btrfs_inode_flags_to_xflags(binode->flags));
(fa.fsx_xflags & (FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE))) && ret = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa);
!capable(CAP_LINUX_IMMUTABLE)) { if (ret)
ret = -EPERM;
goto out_unlock; goto out_unlock;
}
if (fa.fsx_xflags & FS_XFLAG_SYNC) if (fa.fsx_xflags & FS_XFLAG_SYNC)
binode->flags |= BTRFS_INODE_SYNC; binode->flags |= BTRFS_INODE_SYNC;
......
...@@ -107,16 +107,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, ...@@ -107,16 +107,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size; return size;
} }
static int static inline unsigned int efivarfs_getflags(struct inode *inode)
efivarfs_ioc_getxflags(struct file *file, void __user *arg)
{ {
struct inode *inode = file->f_mapping->host;
unsigned int i_flags; unsigned int i_flags;
unsigned int flags = 0; unsigned int flags = 0;
i_flags = inode->i_flags; i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE) if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL; flags |= FS_IMMUTABLE_FL;
return flags;
}
static int
efivarfs_ioc_getxflags(struct file *file, void __user *arg)
{
struct inode *inode = file->f_mapping->host;
unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags))) if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT; return -EFAULT;
...@@ -129,6 +135,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg) ...@@ -129,6 +135,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
unsigned int flags; unsigned int flags;
unsigned int i_flags = 0; unsigned int i_flags = 0;
unsigned int oldflags = efivarfs_getflags(inode);
int error; int error;
if (!inode_owner_or_capable(inode)) if (!inode_owner_or_capable(inode))
...@@ -140,9 +147,6 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg) ...@@ -140,9 +147,6 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL) if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (!capable(CAP_LINUX_IMMUTABLE))
return -EPERM;
if (flags & FS_IMMUTABLE_FL) if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE; i_flags |= S_IMMUTABLE;
...@@ -152,12 +156,16 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg) ...@@ -152,12 +156,16 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
return error; return error;
inode_lock(inode); inode_lock(inode);
error = vfs_ioc_setflags_prepare(inode, oldflags, flags);
if (error)
goto out;
inode_set_flags(inode, i_flags, S_IMMUTABLE); inode_set_flags(inode, i_flags, S_IMMUTABLE);
out:
inode_unlock(inode); inode_unlock(inode);
mnt_drop_write_file(file); mnt_drop_write_file(file);
return error;
return 0;
} }
static long static long
......
...@@ -60,19 +60,11 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -60,19 +60,11 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
} }
oldflags = ei->i_flags; oldflags = ei->i_flags;
/* ret = vfs_ioc_setflags_prepare(inode, oldflags, flags);
* The IMMUTABLE and APPEND_ONLY flags can only be changed by if (ret) {
* the relevant capability.
*
* This test looks nicer. Thanks to Pauline Middelink
*/
if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
if (!capable(CAP_LINUX_IMMUTABLE)) {
inode_unlock(inode); inode_unlock(inode);
ret = -EPERM;
goto setflags_out; goto setflags_out;
} }
}
flags = flags & EXT2_FL_USER_MODIFIABLE; flags = flags & EXT2_FL_USER_MODIFIABLE;
flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
......
...@@ -312,16 +312,9 @@ static int ext4_ioctl_setflags(struct inode *inode, ...@@ -312,16 +312,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */ /* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL; jflag = flags & EXT4_JOURNAL_DATA_FL;
/* err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
* The IMMUTABLE and APPEND_ONLY flags can only be changed by if (err)
* the relevant capability.
*
* This test looks nicer. Thanks to Pauline Middelink
*/
if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
if (!capable(CAP_LINUX_IMMUTABLE))
goto flags_out; goto flags_out;
}
/* /*
* The JOURNAL_DATA flag can only be changed by * The JOURNAL_DATA flag can only be changed by
...@@ -741,28 +734,15 @@ static long ext4_ioctl_group_add(struct file *file, ...@@ -741,28 +734,15 @@ static long ext4_ioctl_group_add(struct file *file,
return err; return err;
} }
static int ext4_ioctl_check_project(struct inode *inode, struct fsxattr *fa) static void ext4_fill_fsxattr(struct inode *inode, struct fsxattr *fa)
{ {
/* struct ext4_inode_info *ei = EXT4_I(inode);
* Project Quota ID state is only allowed to change from within the init
* namespace. Enforce that restriction only if we are trying to change
* the quota ID state. Everything else is allowed in user namespaces.
*/
if (current_user_ns() == &init_user_ns)
return 0;
if (__kprojid_val(EXT4_I(inode)->i_projid) != fa->fsx_projid)
return -EINVAL;
if (ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT)) { simple_fill_fsxattr(fa, ext4_iflags_to_xflags(ei->i_flags &
if (!(fa->fsx_xflags & FS_XFLAG_PROJINHERIT)) EXT4_FL_USER_VISIBLE));
return -EINVAL;
} else {
if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
return -EINVAL;
}
return 0; if (ext4_has_feature_project(inode->i_sb))
fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid);
} }
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
...@@ -1139,13 +1119,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -1139,13 +1119,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{ {
struct fsxattr fa; struct fsxattr fa;
memset(&fa, 0, sizeof(struct fsxattr)); ext4_fill_fsxattr(inode, &fa);
fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE);
if (ext4_has_feature_project(inode->i_sb)) {
fa.fsx_projid = (__u32)from_kprojid(&init_user_ns,
EXT4_I(inode)->i_projid);
}
if (copy_to_user((struct fsxattr __user *)arg, if (copy_to_user((struct fsxattr __user *)arg,
&fa, sizeof(fa))) &fa, sizeof(fa)))
...@@ -1154,7 +1128,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -1154,7 +1128,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
} }
case EXT4_IOC_FSSETXATTR: case EXT4_IOC_FSSETXATTR:
{ {
struct fsxattr fa; struct fsxattr fa, old_fa;
int err; int err;
if (copy_from_user(&fa, (struct fsxattr __user *)arg, if (copy_from_user(&fa, (struct fsxattr __user *)arg,
...@@ -1177,7 +1151,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -1177,7 +1151,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return err; return err;
inode_lock(inode); inode_lock(inode);
err = ext4_ioctl_check_project(inode, &fa); ext4_fill_fsxattr(inode, &old_fa);
err = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa);
if (err) if (err)
goto out; goto out;
flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) | flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) |
......
...@@ -136,27 +136,36 @@ static struct { ...@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA}, {FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
}; };
static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
{
int i;
u32 fsflags = 0;
if (S_ISDIR(inode->i_mode))
gfsflags &= ~GFS2_DIF_JDATA;
else
gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
if (gfsflags & fsflag_gfs2flag[i].gfsflag)
fsflags |= fsflag_gfs2flag[i].fsflag;
return fsflags;
}
static int gfs2_get_flags(struct file *filp, u32 __user *ptr) static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh; struct gfs2_holder gh;
int i, error; int error;
u32 gfsflags, fsflags = 0; u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh); gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh); error = gfs2_glock_nq(&gh);
if (error) if (error)
goto out_uninit; goto out_uninit;
gfsflags = ip->i_diskflags; fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (S_ISDIR(inode->i_mode))
gfsflags &= ~GFS2_DIF_JDATA;
else
gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
if (gfsflags & fsflag_gfs2flag[i].gfsflag)
fsflags |= fsflag_gfs2flag[i].fsflag;
if (put_user(fsflags, ptr)) if (put_user(fsflags, ptr))
error = -EFAULT; error = -EFAULT;
...@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode) ...@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer * @filp: file pointer
* @reqflags: The flags to set * @reqflags: The flags to set
* @mask: Indicates which flags are valid * @mask: Indicates which flags are valid
* @fsflags: The FS_* inode flags passed in
* *
*/ */
static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
const u32 fsflags)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
...@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) ...@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh; struct buffer_head *bh;
struct gfs2_holder gh; struct gfs2_holder gh;
int error; int error;
u32 new_flags, flags; u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp); error = mnt_want_write_file(filp);
if (error) if (error)
...@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) ...@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error) if (error)
goto out_drop_write; goto out_drop_write;
oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
error = vfs_ioc_setflags_prepare(inode, oldflags, fsflags);
if (error)
goto out;
error = -EACCES; error = -EACCES;
if (!inode_owner_or_capable(inode)) if (!inode_owner_or_capable(inode))
goto out; goto out;
...@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr) ...@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA); mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
} }
return do_gfs2_set_flags(filp, gfsflags, mask); return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
} }
static int gfs2_getlabel(struct file *filp, char __user *label) static int gfs2_getlabel(struct file *filp, char __user *label)
......
...@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags) ...@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0; return 0;
} }
static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) static inline unsigned int hfsplus_getflags(struct inode *inode)
{ {
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode); struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0; unsigned int flags = 0;
...@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) ...@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL; flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP) if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL; flags |= FS_NODUMP_FL;
return flags;
}
static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
{
struct inode *inode = file_inode(file);
unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags); return put_user(flags, user_flags);
} }
...@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) ...@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode); struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0; unsigned int flags, new_fl = 0;
unsigned int oldflags = hfsplus_getflags(inode);
int err = 0; int err = 0;
err = mnt_want_write_file(file); err = mnt_want_write_file(file);
...@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) ...@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode); inode_lock(inode);
if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) || err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
inode->i_flags & (S_IMMUTABLE|S_APPEND)) { if (err)
if (!capable(CAP_LINUX_IMMUTABLE)) {
err = -EPERM;
goto out_unlock_inode; goto out_unlock_inode;
}
}
/* don't silently ignore unsupported ext2 flags */ /* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) { if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
......
...@@ -2190,3 +2190,89 @@ struct timespec64 current_time(struct inode *inode) ...@@ -2190,3 +2190,89 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran); return timespec64_trunc(now, inode->i_sb->s_time_gran);
} }
EXPORT_SYMBOL(current_time); EXPORT_SYMBOL(current_time);
/*
* Generic function to check FS_IOC_SETFLAGS values and reject any invalid
* configurations.
*
* Note: the caller should be holding i_mutex, or else be sure that they have
* exclusive access to the inode structure.
*/
int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
unsigned int flags)
{
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
* the relevant capability.
*
* This test looks nicer. Thanks to Pauline Middelink
*/
if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
!capable(CAP_LINUX_IMMUTABLE))
return -EPERM;
return 0;
}
EXPORT_SYMBOL(vfs_ioc_setflags_prepare);
/*
* Generic function to check FS_IOC_FSSETXATTR values and reject any invalid
* configurations.
*
* Note: the caller should be holding i_mutex, or else be sure that they have
* exclusive access to the inode structure.
*/
int vfs_ioc_fssetxattr_check(struct inode *inode, const struct fsxattr *old_fa,
struct fsxattr *fa)
{
/*
* Can't modify an immutable/append-only file unless we have
* appropriate permission.
*/
if ((old_fa->fsx_xflags ^ fa->fsx_xflags) &
(FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND) &&
!capable(CAP_LINUX_IMMUTABLE))
return -EPERM;
/*
* Project Quota ID state is only allowed to change from within the init
* namespace. Enforce that restriction only if we are trying to change
* the quota ID state. Everything else is allowed in user namespaces.
*/
if (current_user_ns() != &init_user_ns) {
if (old_fa->fsx_projid != fa->fsx_projid)
return -EINVAL;
if ((old_fa->fsx_xflags ^ fa->fsx_xflags) &
FS_XFLAG_PROJINHERIT)
return -EINVAL;
}
/* Check extent size hints. */
if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
return -EINVAL;
if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
!S_ISDIR(inode->i_mode))
return -EINVAL;
if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
return -EINVAL;
/*
* It is only valid to set the DAX flag on regular files and
* directories on filesystems.
*/
if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
return -EINVAL;
/* Extent size hints of zero turn off the flags. */
if (fa->fsx_extsize == 0)
fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
if (fa->fsx_cowextsize == 0)
fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
return 0;
}
EXPORT_SYMBOL(vfs_ioc_fssetxattr_check);
...@@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */ /* Lock against other parallel changes of flags */
inode_lock(inode); inode_lock(inode);
oldflags = jfs_inode->mode2; oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
0);
/* err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
* The IMMUTABLE and APPEND_ONLY flags can only be changed by if (err) {
* the relevant capability.
*/
if ((oldflags & JFS_IMMUTABLE_FL) ||
((flags ^ oldflags) &
(JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
if (!capable(CAP_LINUX_IMMUTABLE)) {
inode_unlock(inode); inode_unlock(inode);
err = -EPERM;
goto setflags_out; goto setflags_out;
} }
}
flags = flags & JFS_FL_USER_MODIFIABLE; flags = flags & JFS_FL_USER_MODIFIABLE;
flags |= oldflags & ~JFS_FL_USER_MODIFIABLE; flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
jfs_inode->mode2 = flags; jfs_inode->mode2 = flags;
jfs_set_inode_flags(inode); jfs_set_inode_flags(inode);
......
...@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp, ...@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags; oldflags = NILFS_I(inode)->i_flags;
/* ret = vfs_ioc_setflags_prepare(inode, oldflags, flags);
* The IMMUTABLE and APPEND_ONLY flags can only be changed by the if (ret)
* relevant capability.
*/
ret = -EPERM;
if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
!capable(CAP_LINUX_IMMUTABLE))
goto out; goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0); ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
......
...@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags, ...@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask; flags = flags & mask;
flags |= oldflags & ~mask; flags |= oldflags & ~mask;
/* status = vfs_ioc_setflags_prepare(inode, oldflags, flags);
* The IMMUTABLE and APPEND_ONLY flags can only be changed by if (status)
* the relevant capability.
*/
status = -EPERM;
if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
(OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
if (!capable(CAP_LINUX_IMMUTABLE))
goto bail_unlock; goto bail_unlock;
}
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) { if (IS_ERR(handle)) {
......
...@@ -357,11 +357,28 @@ static ssize_t orangefs_file_write_iter(struct kiocb *iocb, ...@@ -357,11 +357,28 @@ static ssize_t orangefs_file_write_iter(struct kiocb *iocb,
return ret; return ret;
} }
static int orangefs_getflags(struct inode *inode, unsigned long *uval)
{
__u64 val = 0;
int ret;
ret = orangefs_inode_getxattr(inode,
"user.pvfs2.meta_hint",
&val, sizeof(val));
if (ret < 0 && ret != -ENODATA)
return ret;
else if (ret == -ENODATA)
val = 0;
*uval = val;
return 0;
}
/* /*
* Perform a miscellaneous operation on a file. * Perform a miscellaneous operation on a file.
*/ */
static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{ {
struct inode *inode = file_inode(file);
int ret = -ENOTTY; int ret = -ENOTTY;
__u64 val = 0; __u64 val = 0;
unsigned long uval; unsigned long uval;
...@@ -375,20 +392,16 @@ static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long ar ...@@ -375,20 +392,16 @@ static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long ar
* and append flags * and append flags
*/ */
if (cmd == FS_IOC_GETFLAGS) { if (cmd == FS_IOC_GETFLAGS) {
val = 0; ret = orangefs_getflags(inode, &uval);
ret = orangefs_inode_getxattr(file_inode(file), if (ret)
"user.pvfs2.meta_hint",
&val, sizeof(val));
if (ret < 0 && ret != -ENODATA)
return ret; return ret;
else if (ret == -ENODATA)
val = 0;
uval = val;
gossip_debug(GOSSIP_FILE_DEBUG, gossip_debug(GOSSIP_FILE_DEBUG,
"orangefs_ioctl: FS_IOC_GETFLAGS: %llu\n", "orangefs_ioctl: FS_IOC_GETFLAGS: %llu\n",
(unsigned long long)uval); (unsigned long long)uval);
return put_user(uval, (int __user *)arg); return put_user(uval, (int __user *)arg);
} else if (cmd == FS_IOC_SETFLAGS) { } else if (cmd == FS_IOC_SETFLAGS) {
unsigned long old_uval;
ret = 0; ret = 0;
if (get_user(uval, (int __user *)arg)) if (get_user(uval, (int __user *)arg))
return -EFAULT; return -EFAULT;
...@@ -404,11 +417,17 @@ static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long ar ...@@ -404,11 +417,17 @@ static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long ar
gossip_err("orangefs_ioctl: the FS_IOC_SETFLAGS only supports setting one of FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NOATIME_FL\n"); gossip_err("orangefs_ioctl: the FS_IOC_SETFLAGS only supports setting one of FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NOATIME_FL\n");
return -EINVAL; return -EINVAL;
} }
ret = orangefs_getflags(inode, &old_uval);
if (ret)
return ret;
ret = vfs_ioc_setflags_prepare(inode, old_uval, uval);
if (ret)
return ret;
val = uval; val = uval;
gossip_debug(GOSSIP_FILE_DEBUG, gossip_debug(GOSSIP_FILE_DEBUG,
"orangefs_ioctl: FS_IOC_SETFLAGS: %llu\n", "orangefs_ioctl: FS_IOC_SETFLAGS: %llu\n",
(unsigned long long)val); (unsigned long long)val);
ret = orangefs_inode_setxattr(file_inode(file), ret = orangefs_inode_setxattr(inode,
"user.pvfs2.meta_hint", "user.pvfs2.meta_hint",
&val, sizeof(val), 0); &val, sizeof(val), 0);
} }
......
...@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM; err = -EPERM;
goto setflags_out; goto setflags_out;
} }
if (((flags ^ REISERFS_I(inode)-> err = vfs_ioc_setflags_prepare(inode,
i_attrs) & (REISERFS_IMMUTABLE_FL | REISERFS_I(inode)->i_attrs,
REISERFS_APPEND_FL)) flags);
&& !capable(CAP_LINUX_IMMUTABLE)) { if (err)
err = -EPERM;
goto setflags_out; goto setflags_out;
}
if ((flags & REISERFS_NOTAIL_FL) && if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) { S_ISREG(inode->i_mode)) {
int result; int result;
......
...@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags) ...@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err) if (err)
return err; return err;
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
* the relevant capability.
*/
mutex_lock(&ui->ui_mutex); mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags); oldflags = ubifs2ioctl(ui->flags);
if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
if (!capable(CAP_LINUX_IMMUTABLE)) { if (err)
err = -EPERM;
goto out_unlock; goto out_unlock;
}
}
ui->flags = ioctl2ubifs(flags); ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode); ubifs_set_inode_flags(inode);
......
...@@ -879,37 +879,44 @@ xfs_di2lxflags( ...@@ -879,37 +879,44 @@ xfs_di2lxflags(
return flags; return flags;
} }
STATIC int static void
xfs_ioc_fsgetxattr( xfs_fill_fsxattr(
xfs_inode_t *ip, struct xfs_inode *ip,
int attr, bool attr,
void __user *arg) struct fsxattr *fa)
{ {
struct fsxattr fa; simple_fill_fsxattr(fa, xfs_ip2xflags(ip));
fa->fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
memset(&fa, 0, sizeof(struct fsxattr)); fa->fsx_cowextsize = ip->i_d.di_cowextsize <<
xfs_ilock(ip, XFS_ILOCK_SHARED);
fa.fsx_xflags = xfs_ip2xflags(ip);
fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
fa.fsx_cowextsize = ip->i_d.di_cowextsize <<
ip->i_mount->m_sb.sb_blocklog; ip->i_mount->m_sb.sb_blocklog;
fa.fsx_projid = xfs_get_projid(ip); fa->fsx_projid = xfs_get_projid(ip);
if (attr) { if (attr) {
if (ip->i_afp) { if (ip->i_afp) {
if (ip->i_afp->if_flags & XFS_IFEXTENTS) if (ip->i_afp->if_flags & XFS_IFEXTENTS)
fa.fsx_nextents = xfs_iext_count(ip->i_afp); fa->fsx_nextents = xfs_iext_count(ip->i_afp);
else else
fa.fsx_nextents = ip->i_d.di_anextents; fa->fsx_nextents = ip->i_d.di_anextents;
} else } else
fa.fsx_nextents = 0; fa->fsx_nextents = 0;
} else { } else {
if (ip->i_df.if_flags & XFS_IFEXTENTS) if (ip->i_df.if_flags & XFS_IFEXTENTS)
fa.fsx_nextents = xfs_iext_count(&ip->i_df); fa->fsx_nextents = xfs_iext_count(&ip->i_df);
else else
fa.fsx_nextents = ip->i_d.di_nextents; fa->fsx_nextents = ip->i_d.di_nextents;
} }
}
STATIC int
xfs_ioc_fsgetxattr(
xfs_inode_t *ip,
int attr,
void __user *arg)
{
struct fsxattr fa;
xfs_ilock(ip, XFS_ILOCK_SHARED);
xfs_fill_fsxattr(ip, attr, &fa);
xfs_iunlock(ip, XFS_ILOCK_SHARED); xfs_iunlock(ip, XFS_ILOCK_SHARED);
if (copy_to_user(arg, &fa, sizeof(fa))) if (copy_to_user(arg, &fa, sizeof(fa)))
...@@ -1035,15 +1042,6 @@ xfs_ioctl_setattr_xflags( ...@@ -1035,15 +1042,6 @@ xfs_ioctl_setattr_xflags(
if ((fa->fsx_xflags & FS_XFLAG_DAX) && xfs_is_reflink_inode(ip)) if ((fa->fsx_xflags & FS_XFLAG_DAX) && xfs_is_reflink_inode(ip))
return -EINVAL; return -EINVAL;
/*
* Can't modify an immutable/append-only file unless
* we have appropriate permission.
*/
if (((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND)) ||
(fa->fsx_xflags & (FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND))) &&
!capable(CAP_LINUX_IMMUTABLE))
return -EPERM;
/* diflags2 only valid for v3 inodes. */ /* diflags2 only valid for v3 inodes. */
di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags); di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
if (di_flags2 && ip->i_d.di_version < 3) if (di_flags2 && ip->i_d.di_version < 3)
...@@ -1202,21 +1200,15 @@ xfs_ioctl_setattr_check_extsize( ...@@ -1202,21 +1200,15 @@ xfs_ioctl_setattr_check_extsize(
struct fsxattr *fa) struct fsxattr *fa)
{ {
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
xfs_extlen_t size;
if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(VFS_I(ip)->i_mode)) xfs_fsblock_t extsize_fsb;
return -EINVAL;
if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
!S_ISDIR(VFS_I(ip)->i_mode))
return -EINVAL;
if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_d.di_nextents && if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_d.di_nextents &&
((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
return -EINVAL; return -EINVAL;
if (fa->fsx_extsize != 0) { if (fa->fsx_extsize == 0)
xfs_extlen_t size; return 0;
xfs_fsblock_t extsize_fsb;
extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize); extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
if (extsize_fsb > MAXEXTLEN) if (extsize_fsb > MAXEXTLEN)
...@@ -1233,8 +1225,6 @@ xfs_ioctl_setattr_check_extsize( ...@@ -1233,8 +1225,6 @@ xfs_ioctl_setattr_check_extsize(
if (fa->fsx_extsize % size) if (fa->fsx_extsize % size)
return -EINVAL; return -EINVAL;
} else
fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
return 0; return 0;
} }
...@@ -1260,6 +1250,8 @@ xfs_ioctl_setattr_check_cowextsize( ...@@ -1260,6 +1250,8 @@ xfs_ioctl_setattr_check_cowextsize(
struct fsxattr *fa) struct fsxattr *fa)
{ {
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
xfs_extlen_t size;
xfs_fsblock_t cowextsize_fsb;
if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE)) if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
return 0; return 0;
...@@ -1268,12 +1260,8 @@ xfs_ioctl_setattr_check_cowextsize( ...@@ -1268,12 +1260,8 @@ xfs_ioctl_setattr_check_cowextsize(
ip->i_d.di_version != 3) ip->i_d.di_version != 3)
return -EINVAL; return -EINVAL;
if (!S_ISREG(VFS_I(ip)->i_mode) && !S_ISDIR(VFS_I(ip)->i_mode)) if (fa->fsx_cowextsize == 0)
return -EINVAL; return 0;
if (fa->fsx_cowextsize != 0) {
xfs_extlen_t size;
xfs_fsblock_t cowextsize_fsb;
cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize); cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
if (cowextsize_fsb > MAXEXTLEN) if (cowextsize_fsb > MAXEXTLEN)
...@@ -1285,8 +1273,6 @@ xfs_ioctl_setattr_check_cowextsize( ...@@ -1285,8 +1273,6 @@ xfs_ioctl_setattr_check_cowextsize(
if (fa->fsx_cowextsize % size) if (fa->fsx_cowextsize % size)
return -EINVAL; return -EINVAL;
} else
fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
return 0; return 0;
} }
...@@ -1300,21 +1286,6 @@ xfs_ioctl_setattr_check_projid( ...@@ -1300,21 +1286,6 @@ xfs_ioctl_setattr_check_projid(
if (fa->fsx_projid > (uint16_t)-1 && if (fa->fsx_projid > (uint16_t)-1 &&
!xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb)) !xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
return -EINVAL; return -EINVAL;
/*
* Project Quota ID state is only allowed to change from within the init
* namespace. Enforce that restriction only if we are trying to change
* the quota ID state. Everything else is allowed in user namespaces.
*/
if (current_user_ns() == &init_user_ns)
return 0;
if (xfs_get_projid(ip) != fa->fsx_projid)
return -EINVAL;
if ((fa->fsx_xflags & FS_XFLAG_PROJINHERIT) !=
(ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
return -EINVAL;
return 0; return 0;
} }
...@@ -1323,6 +1294,7 @@ xfs_ioctl_setattr( ...@@ -1323,6 +1294,7 @@ xfs_ioctl_setattr(
xfs_inode_t *ip, xfs_inode_t *ip,
struct fsxattr *fa) struct fsxattr *fa)
{ {
struct fsxattr old_fa;
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp; struct xfs_trans *tp;
struct xfs_dquot *udqp = NULL; struct xfs_dquot *udqp = NULL;
...@@ -1370,7 +1342,6 @@ xfs_ioctl_setattr( ...@@ -1370,7 +1342,6 @@ xfs_ioctl_setattr(
goto error_free_dquots; goto error_free_dquots;
} }
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
xfs_get_projid(ip) != fa->fsx_projid) { xfs_get_projid(ip) != fa->fsx_projid) {
code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp, code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp,
...@@ -1379,6 +1350,11 @@ xfs_ioctl_setattr( ...@@ -1379,6 +1350,11 @@ xfs_ioctl_setattr(
goto error_trans_cancel; goto error_trans_cancel;
} }
xfs_fill_fsxattr(ip, false, &old_fa);
code = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, fa);
if (code)
goto error_trans_cancel;
code = xfs_ioctl_setattr_check_extsize(ip, fa); code = xfs_ioctl_setattr_check_extsize(ip, fa);
if (code) if (code)
goto error_trans_cancel; goto error_trans_cancel;
...@@ -1489,6 +1465,7 @@ xfs_ioc_setxflags( ...@@ -1489,6 +1465,7 @@ xfs_ioc_setxflags(
{ {
struct xfs_trans *tp; struct xfs_trans *tp;
struct fsxattr fa; struct fsxattr fa;
struct fsxattr old_fa;
unsigned int flags; unsigned int flags;
int join_flags = 0; int join_flags = 0;
int error; int error;
...@@ -1524,6 +1501,13 @@ xfs_ioc_setxflags( ...@@ -1524,6 +1501,13 @@ xfs_ioc_setxflags(
goto out_drop_write; goto out_drop_write;
} }
xfs_fill_fsxattr(ip, false, &old_fa);
error = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, &fa);
if (error) {
xfs_trans_cancel(tp);
goto out_drop_write;
}
error = xfs_ioctl_setattr_xflags(tp, ip, &fa); error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
if (error) { if (error) {
xfs_trans_cancel(tp); xfs_trans_cancel(tp);
......
...@@ -3556,4 +3556,16 @@ static inline struct sock *io_uring_get_socket(struct file *file) ...@@ -3556,4 +3556,16 @@ static inline struct sock *io_uring_get_socket(struct file *file)
} }
#endif #endif
int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
unsigned int flags);
int vfs_ioc_fssetxattr_check(struct inode *inode, const struct fsxattr *old_fa,
struct fsxattr *fa);
static inline void simple_fill_fsxattr(struct fsxattr *fa, __u32 xflags)
{
memset(fa, 0, sizeof(*fa));
fa->fsx_xflags = xflags;
}
#endif /* _LINUX_FS_H */ #endif /* _LINUX_FS_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment