mirror of
https://github.com/t2linux/kernel.git
synced 2026-04-30 13:48:59 -07:00
Merge branch 'miklos.fileattr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull fileattr conversion updates from Miklos Szeredi via Al Viro: "This splits the handling of FS_IOC_[GS]ETFLAGS from ->ioctl() into a separate method. The interface is reasonably uniform across the filesystems that support it and gives nice boilerplate removal" * 'miklos.fileattr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (23 commits) ovl: remove unneeded ioctls fuse: convert to fileattr fuse: add internal open/release helpers fuse: unsigned open flags fuse: move ioctl to separate source file vfs: remove unused ioctl helpers ubifs: convert to fileattr reiserfs: convert to fileattr ocfs2: convert to fileattr nilfs2: convert to fileattr jfs: convert to fileattr hfsplus: convert to fileattr efivars: convert to fileattr xfs: convert to fileattr orangefs: convert to fileattr gfs2: convert to fileattr f2fs: convert to fileattr ext4: convert to fileattr ext2: convert to fileattr btrfs: convert to fileattr ...
This commit is contained in:
@@ -80,13 +80,16 @@ prototypes::
|
||||
struct file *, unsigned open_flag,
|
||||
umode_t create_mode);
|
||||
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
|
||||
int (*fileattr_set)(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa);
|
||||
int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa);
|
||||
|
||||
locking rules:
|
||||
all may block
|
||||
|
||||
============ =============================================
|
||||
============= =============================================
|
||||
ops i_rwsem(inode)
|
||||
============ =============================================
|
||||
============= =============================================
|
||||
lookup: shared
|
||||
create: exclusive
|
||||
link: exclusive (both)
|
||||
@@ -107,7 +110,9 @@ fiemap: no
|
||||
update_time: no
|
||||
atomic_open: shared (exclusive if O_CREAT is set in open flags)
|
||||
tmpfile: no
|
||||
============ =============================================
|
||||
fileattr_get: no or exclusive
|
||||
fileattr_set: exclusive
|
||||
============= =============================================
|
||||
|
||||
|
||||
Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_rwsem
|
||||
|
||||
@@ -441,6 +441,9 @@ As of kernel 2.6.22, the following members are defined:
|
||||
unsigned open_flag, umode_t create_mode);
|
||||
int (*tmpfile) (struct user_namespace *, struct inode *, struct dentry *, umode_t);
|
||||
int (*set_acl)(struct user_namespace *, struct inode *, struct posix_acl *, int);
|
||||
int (*fileattr_set)(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa);
|
||||
int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa);
|
||||
};
|
||||
|
||||
Again, all methods are called without any locks being held, unless
|
||||
@@ -588,6 +591,18 @@ otherwise noted.
|
||||
atomically creating, opening and unlinking a file in given
|
||||
directory.
|
||||
|
||||
``fileattr_get``
|
||||
called on ioctl(FS_IOC_GETFLAGS) and ioctl(FS_IOC_FSGETXATTR) to
|
||||
retrieve miscellaneous file flags and attributes. Also called
|
||||
before the relevant SET operation to check what is being changed
|
||||
(in this case with i_rwsem locked exclusive). If unset, then
|
||||
fall back to f_op->ioctl().
|
||||
|
||||
``fileattr_set``
|
||||
called on ioctl(FS_IOC_SETFLAGS) and ioctl(FS_IOC_FSSETXATTR) to
|
||||
change miscellaneous file flags and attributes. Callers hold
|
||||
i_rwsem exclusive. If unset, then fall back to f_op->ioctl().
|
||||
|
||||
|
||||
The Address Space Object
|
||||
========================
|
||||
|
||||
@@ -3207,6 +3207,9 @@ void btrfs_update_inode_bytes(struct btrfs_inode *inode,
|
||||
/* ioctl.c */
|
||||
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
long btrfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
int btrfs_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa);
|
||||
int btrfs_ioctl_get_supported_features(void __user *arg);
|
||||
void btrfs_sync_inode_flags_to_i_flags(struct inode *inode);
|
||||
int __pure btrfs_is_empty_uuid(u8 *uuid);
|
||||
|
||||
@@ -10620,6 +10620,8 @@ static const struct inode_operations btrfs_dir_inode_operations = {
|
||||
.set_acl = btrfs_set_acl,
|
||||
.update_time = btrfs_update_time,
|
||||
.tmpfile = btrfs_tmpfile,
|
||||
.fileattr_get = btrfs_fileattr_get,
|
||||
.fileattr_set = btrfs_fileattr_set,
|
||||
};
|
||||
|
||||
static const struct file_operations btrfs_dir_file_operations = {
|
||||
@@ -10673,6 +10675,8 @@ static const struct inode_operations btrfs_file_inode_operations = {
|
||||
.get_acl = btrfs_get_acl,
|
||||
.set_acl = btrfs_set_acl,
|
||||
.update_time = btrfs_update_time,
|
||||
.fileattr_get = btrfs_fileattr_get,
|
||||
.fileattr_set = btrfs_fileattr_set,
|
||||
};
|
||||
static const struct inode_operations btrfs_special_inode_operations = {
|
||||
.getattr = btrfs_getattr,
|
||||
|
||||
+35
-193
@@ -26,6 +26,7 @@
|
||||
#include <linux/btrfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/iversion.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include "ctree.h"
|
||||
#include "disk-io.h"
|
||||
#include "export.h"
|
||||
@@ -153,16 +154,6 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode)
|
||||
new_fl);
|
||||
}
|
||||
|
||||
static int btrfs_ioctl_getflags(struct file *file, void __user *arg)
|
||||
{
|
||||
struct btrfs_inode *binode = BTRFS_I(file_inode(file));
|
||||
unsigned int flags = btrfs_inode_flags_to_fsflags(binode->flags);
|
||||
|
||||
if (copy_to_user(arg, &flags, sizeof(flags)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if @flags are a supported and valid set of FS_*_FL flags and that
|
||||
* the old and new flags are not conflicting
|
||||
@@ -201,9 +192,22 @@ static int check_fsflags_compatible(struct btrfs_fs_info *fs_info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
/*
|
||||
* Set flags/xflags from the internal inode flags. The remaining items of
|
||||
* fsxattr are zeroed.
|
||||
*/
|
||||
int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct btrfs_inode *binode = BTRFS_I(d_inode(dentry));
|
||||
|
||||
fileattr_fill_flags(fa, btrfs_inode_flags_to_fsflags(binode->flags));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
||||
struct btrfs_inode *binode = BTRFS_I(inode);
|
||||
struct btrfs_root *root = binode->root;
|
||||
@@ -213,34 +217,21 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
const char *comp = NULL;
|
||||
u32 binode_flags;
|
||||
|
||||
if (!inode_owner_or_capable(&init_user_ns, inode))
|
||||
return -EPERM;
|
||||
|
||||
if (btrfs_root_readonly(root))
|
||||
return -EROFS;
|
||||
|
||||
if (copy_from_user(&fsflags, arg, sizeof(fsflags)))
|
||||
return -EFAULT;
|
||||
if (fileattr_has_fsx(fa))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = mnt_want_write_file(file);
|
||||
fsflags = btrfs_mask_fsflags_for_type(inode, fa->flags);
|
||||
old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
|
||||
ret = check_fsflags(old_fsflags, fsflags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
btrfs_inode_lock(inode, 0);
|
||||
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
|
||||
old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
|
||||
|
||||
ret = vfs_ioc_setflags_prepare(inode, old_fsflags, fsflags);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
ret = check_fsflags(old_fsflags, fsflags);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
ret = check_fsflags_compatible(fs_info, fsflags);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
return ret;
|
||||
|
||||
binode_flags = binode->flags;
|
||||
if (fsflags & FS_SYNC_FL)
|
||||
@@ -263,6 +254,14 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
binode_flags |= BTRFS_INODE_NOATIME;
|
||||
else
|
||||
binode_flags &= ~BTRFS_INODE_NOATIME;
|
||||
|
||||
/* If coming from FS_IOC_FSSETXATTR then skip unconverted flags */
|
||||
if (!fa->flags_valid) {
|
||||
/* 1 item for the inode */
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
goto update_flags;
|
||||
}
|
||||
|
||||
if (fsflags & FS_DIRSYNC_FL)
|
||||
binode_flags |= BTRFS_INODE_DIRSYNC;
|
||||
else
|
||||
@@ -303,10 +302,8 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
binode_flags |= BTRFS_INODE_NOCOMPRESS;
|
||||
} else if (fsflags & FS_COMPR_FL) {
|
||||
|
||||
if (IS_SWAPFILE(inode)) {
|
||||
ret = -ETXTBSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (IS_SWAPFILE(inode))
|
||||
return -ETXTBSY;
|
||||
|
||||
binode_flags |= BTRFS_INODE_COMPRESS;
|
||||
binode_flags &= ~BTRFS_INODE_NOCOMPRESS;
|
||||
@@ -323,10 +320,8 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
* 2 for properties
|
||||
*/
|
||||
trans = btrfs_start_transaction(root, 3);
|
||||
if (IS_ERR(trans)) {
|
||||
ret = PTR_ERR(trans);
|
||||
goto out_unlock;
|
||||
}
|
||||
if (IS_ERR(trans))
|
||||
return PTR_ERR(trans);
|
||||
|
||||
if (comp) {
|
||||
ret = btrfs_set_prop(trans, inode, "btrfs.compression", comp,
|
||||
@@ -344,6 +339,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
}
|
||||
}
|
||||
|
||||
update_flags:
|
||||
binode->flags = binode_flags;
|
||||
btrfs_sync_inode_flags_to_i_flags(inode);
|
||||
inode_inc_iversion(inode);
|
||||
@@ -352,44 +348,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
|
||||
out_end_trans:
|
||||
btrfs_end_transaction(trans);
|
||||
out_unlock:
|
||||
btrfs_inode_unlock(inode, 0);
|
||||
mnt_drop_write_file(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate btrfs internal inode flags to xflags as expected by the
|
||||
* FS_IOC_FSGETXATT ioctl. Filter only the supported ones, unknown flags are
|
||||
* silently dropped.
|
||||
*/
|
||||
static unsigned int btrfs_inode_flags_to_xflags(unsigned int flags)
|
||||
{
|
||||
unsigned int xflags = 0;
|
||||
|
||||
if (flags & BTRFS_INODE_APPEND)
|
||||
xflags |= FS_XFLAG_APPEND;
|
||||
if (flags & BTRFS_INODE_IMMUTABLE)
|
||||
xflags |= FS_XFLAG_IMMUTABLE;
|
||||
if (flags & BTRFS_INODE_NOATIME)
|
||||
xflags |= FS_XFLAG_NOATIME;
|
||||
if (flags & BTRFS_INODE_NODUMP)
|
||||
xflags |= FS_XFLAG_NODUMP;
|
||||
if (flags & BTRFS_INODE_SYNC)
|
||||
xflags |= FS_XFLAG_SYNC;
|
||||
|
||||
return xflags;
|
||||
}
|
||||
|
||||
/* Check if @flags are a supported and valid set of FS_XFLAGS_* flags */
|
||||
static int check_xflags(unsigned int flags)
|
||||
{
|
||||
if (flags & ~(FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE | FS_XFLAG_NOATIME |
|
||||
FS_XFLAG_NODUMP | FS_XFLAG_SYNC))
|
||||
return -EOPNOTSUPP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool btrfs_exclop_start(struct btrfs_fs_info *fs_info,
|
||||
enum btrfs_exclusive_operation type)
|
||||
{
|
||||
@@ -402,111 +363,6 @@ void btrfs_exclop_finish(struct btrfs_fs_info *fs_info)
|
||||
sysfs_notify(&fs_info->fs_devices->fsid_kobj, NULL, "exclusive_operation");
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the xflags from the internal inode flags. The remaining items of fsxattr
|
||||
* are zeroed.
|
||||
*/
|
||||
static int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg)
|
||||
{
|
||||
struct btrfs_inode *binode = BTRFS_I(file_inode(file));
|
||||
struct fsxattr fa;
|
||||
|
||||
simple_fill_fsxattr(&fa, btrfs_inode_flags_to_xflags(binode->flags));
|
||||
if (copy_to_user(arg, &fa, sizeof(fa)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct btrfs_inode *binode = BTRFS_I(inode);
|
||||
struct btrfs_root *root = binode->root;
|
||||
struct btrfs_trans_handle *trans;
|
||||
struct fsxattr fa, old_fa;
|
||||
unsigned old_flags;
|
||||
unsigned old_i_flags;
|
||||
int ret = 0;
|
||||
|
||||
if (!inode_owner_or_capable(&init_user_ns, inode))
|
||||
return -EPERM;
|
||||
|
||||
if (btrfs_root_readonly(root))
|
||||
return -EROFS;
|
||||
|
||||
if (copy_from_user(&fa, arg, sizeof(fa)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = check_xflags(fa.fsx_xflags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (fa.fsx_extsize != 0 || fa.fsx_projid != 0 || fa.fsx_cowextsize != 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = mnt_want_write_file(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
btrfs_inode_lock(inode, 0);
|
||||
|
||||
old_flags = binode->flags;
|
||||
old_i_flags = inode->i_flags;
|
||||
|
||||
simple_fill_fsxattr(&old_fa,
|
||||
btrfs_inode_flags_to_xflags(binode->flags));
|
||||
ret = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
if (fa.fsx_xflags & FS_XFLAG_SYNC)
|
||||
binode->flags |= BTRFS_INODE_SYNC;
|
||||
else
|
||||
binode->flags &= ~BTRFS_INODE_SYNC;
|
||||
if (fa.fsx_xflags & FS_XFLAG_IMMUTABLE)
|
||||
binode->flags |= BTRFS_INODE_IMMUTABLE;
|
||||
else
|
||||
binode->flags &= ~BTRFS_INODE_IMMUTABLE;
|
||||
if (fa.fsx_xflags & FS_XFLAG_APPEND)
|
||||
binode->flags |= BTRFS_INODE_APPEND;
|
||||
else
|
||||
binode->flags &= ~BTRFS_INODE_APPEND;
|
||||
if (fa.fsx_xflags & FS_XFLAG_NODUMP)
|
||||
binode->flags |= BTRFS_INODE_NODUMP;
|
||||
else
|
||||
binode->flags &= ~BTRFS_INODE_NODUMP;
|
||||
if (fa.fsx_xflags & FS_XFLAG_NOATIME)
|
||||
binode->flags |= BTRFS_INODE_NOATIME;
|
||||
else
|
||||
binode->flags &= ~BTRFS_INODE_NOATIME;
|
||||
|
||||
/* 1 item for the inode */
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
if (IS_ERR(trans)) {
|
||||
ret = PTR_ERR(trans);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
btrfs_sync_inode_flags_to_i_flags(inode);
|
||||
inode_inc_iversion(inode);
|
||||
inode->i_ctime = current_time(inode);
|
||||
ret = btrfs_update_inode(trans, root, BTRFS_I(inode));
|
||||
|
||||
btrfs_end_transaction(trans);
|
||||
|
||||
out_unlock:
|
||||
if (ret) {
|
||||
binode->flags = old_flags;
|
||||
inode->i_flags = old_i_flags;
|
||||
}
|
||||
|
||||
btrfs_inode_unlock(inode, 0);
|
||||
mnt_drop_write_file(file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btrfs_ioctl_getversion(struct file *file, int __user *arg)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
@@ -4932,10 +4788,6 @@ long btrfs_ioctl(struct file *file, unsigned int
|
||||
void __user *argp = (void __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case FS_IOC_GETFLAGS:
|
||||
return btrfs_ioctl_getflags(file, argp);
|
||||
case FS_IOC_SETFLAGS:
|
||||
return btrfs_ioctl_setflags(file, argp);
|
||||
case FS_IOC_GETVERSION:
|
||||
return btrfs_ioctl_getversion(file, argp);
|
||||
case FS_IOC_GETFSLABEL:
|
||||
@@ -5061,10 +4913,6 @@ long btrfs_ioctl(struct file *file, unsigned int
|
||||
return btrfs_ioctl_get_features(fs_info, argp);
|
||||
case BTRFS_IOC_SET_FEATURES:
|
||||
return btrfs_ioctl_set_features(file, argp);
|
||||
case FS_IOC_FSGETXATTR:
|
||||
return btrfs_ioctl_fsgetxattr(file, argp);
|
||||
case FS_IOC_FSSETXATTR:
|
||||
return btrfs_ioctl_fssetxattr(file, argp);
|
||||
case BTRFS_IOC_GET_SUBVOL_INFO:
|
||||
return btrfs_ioctl_get_subvol_info(file, argp);
|
||||
case BTRFS_IOC_GET_SUBVOL_ROOTREF:
|
||||
@@ -5084,12 +4932,6 @@ long btrfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
* handling is necessary.
|
||||
*/
|
||||
switch (cmd) {
|
||||
case FS_IOC32_GETFLAGS:
|
||||
cmd = FS_IOC_GETFLAGS;
|
||||
break;
|
||||
case FS_IOC32_SETFLAGS:
|
||||
cmd = FS_IOC_SETFLAGS;
|
||||
break;
|
||||
case FS_IOC32_GETVERSION:
|
||||
cmd = FS_IOC_GETVERSION;
|
||||
break;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/fs_stack.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
@@ -1118,6 +1119,23 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
return vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
|
||||
}
|
||||
|
||||
static int ecryptfs_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
int rc;
|
||||
|
||||
rc = vfs_fileattr_set(&init_user_ns, lower_dentry, fa);
|
||||
fsstack_copy_attr_all(d_inode(dentry), d_inode(lower_dentry));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
const struct inode_operations ecryptfs_symlink_iops = {
|
||||
.get_link = ecryptfs_get_link,
|
||||
.permission = ecryptfs_permission,
|
||||
@@ -1139,6 +1157,8 @@ const struct inode_operations ecryptfs_dir_iops = {
|
||||
.permission = ecryptfs_permission,
|
||||
.setattr = ecryptfs_setattr,
|
||||
.listxattr = ecryptfs_listxattr,
|
||||
.fileattr_get = ecryptfs_fileattr_get,
|
||||
.fileattr_set = ecryptfs_fileattr_set,
|
||||
};
|
||||
|
||||
const struct inode_operations ecryptfs_main_iops = {
|
||||
@@ -1146,6 +1166,8 @@ const struct inode_operations ecryptfs_main_iops = {
|
||||
.setattr = ecryptfs_setattr,
|
||||
.getattr = ecryptfs_getattr,
|
||||
.listxattr = ecryptfs_listxattr,
|
||||
.fileattr_get = ecryptfs_fileattr_get,
|
||||
.fileattr_set = ecryptfs_fileattr_set,
|
||||
};
|
||||
|
||||
static int ecryptfs_xattr_get(const struct xattr_handler *handler,
|
||||
|
||||
@@ -106,86 +106,9 @@ out_free:
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline unsigned int efivarfs_getflags(struct inode *inode)
|
||||
{
|
||||
unsigned int i_flags;
|
||||
unsigned int flags = 0;
|
||||
|
||||
i_flags = inode->i_flags;
|
||||
if (i_flags & S_IMMUTABLE)
|
||||
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)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
efivarfs_ioc_setxflags(struct file *file, void __user *arg)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
unsigned int flags;
|
||||
unsigned int i_flags = 0;
|
||||
unsigned int oldflags = efivarfs_getflags(inode);
|
||||
int error;
|
||||
|
||||
if (!inode_owner_or_capable(&init_user_ns, inode))
|
||||
return -EACCES;
|
||||
|
||||
if (copy_from_user(&flags, arg, sizeof(flags)))
|
||||
return -EFAULT;
|
||||
|
||||
if (flags & ~FS_IMMUTABLE_FL)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (flags & FS_IMMUTABLE_FL)
|
||||
i_flags |= S_IMMUTABLE;
|
||||
|
||||
|
||||
error = mnt_want_write_file(file);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
error = vfs_ioc_setflags_prepare(inode, oldflags, flags);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
inode_set_flags(inode, i_flags, S_IMMUTABLE);
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
mnt_drop_write_file(file);
|
||||
return error;
|
||||
}
|
||||
|
||||
static long
|
||||
efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p)
|
||||
{
|
||||
void __user *arg = (void __user *)p;
|
||||
|
||||
switch (cmd) {
|
||||
case FS_IOC_GETFLAGS:
|
||||
return efivarfs_ioc_getxflags(file, arg);
|
||||
case FS_IOC_SETFLAGS:
|
||||
return efivarfs_ioc_setxflags(file, arg);
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
const struct file_operations efivarfs_file_operations = {
|
||||
.open = simple_open,
|
||||
.read = efivarfs_file_read,
|
||||
.write = efivarfs_file_write,
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = efivarfs_file_ioctl,
|
||||
};
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
#include <linux/kmemleak.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
static const struct inode_operations efivarfs_file_inode_operations;
|
||||
|
||||
struct inode *efivarfs_get_inode(struct super_block *sb,
|
||||
const struct inode *dir, int mode,
|
||||
dev_t dev, bool is_removable)
|
||||
@@ -26,6 +29,7 @@ struct inode *efivarfs_get_inode(struct super_block *sb,
|
||||
inode->i_flags = is_removable ? 0 : S_IMMUTABLE;
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
inode->i_op = &efivarfs_file_inode_operations;
|
||||
inode->i_fop = &efivarfs_file_operations;
|
||||
break;
|
||||
case S_IFDIR:
|
||||
@@ -138,3 +142,43 @@ const struct inode_operations efivarfs_dir_inode_operations = {
|
||||
.unlink = efivarfs_unlink,
|
||||
.create = efivarfs_create,
|
||||
};
|
||||
|
||||
static int
|
||||
efivarfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
unsigned int i_flags;
|
||||
unsigned int flags = 0;
|
||||
|
||||
i_flags = d_inode(dentry)->i_flags;
|
||||
if (i_flags & S_IMMUTABLE)
|
||||
flags |= FS_IMMUTABLE_FL;
|
||||
|
||||
fileattr_fill_flags(fa, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
efivarfs_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
unsigned int i_flags = 0;
|
||||
|
||||
if (fileattr_has_fsx(fa))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (fa->flags & ~FS_IMMUTABLE_FL)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (fa->flags & FS_IMMUTABLE_FL)
|
||||
i_flags |= S_IMMUTABLE;
|
||||
|
||||
inode_set_flags(d_inode(dentry), i_flags, S_IMMUTABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct inode_operations efivarfs_file_inode_operations = {
|
||||
.fileattr_get = efivarfs_fileattr_get,
|
||||
.fileattr_set = efivarfs_fileattr_set,
|
||||
};
|
||||
|
||||
+3
-4
@@ -283,8 +283,6 @@ static inline __u32 ext2_mask_flags(umode_t mode, __u32 flags)
|
||||
/*
|
||||
* ioctl commands
|
||||
*/
|
||||
#define EXT2_IOC_GETFLAGS FS_IOC_GETFLAGS
|
||||
#define EXT2_IOC_SETFLAGS FS_IOC_SETFLAGS
|
||||
#define EXT2_IOC_GETVERSION FS_IOC_GETVERSION
|
||||
#define EXT2_IOC_SETVERSION FS_IOC_SETVERSION
|
||||
#define EXT2_IOC_GETRSVSZ _IOR('f', 5, long)
|
||||
@@ -293,8 +291,6 @@ static inline __u32 ext2_mask_flags(umode_t mode, __u32 flags)
|
||||
/*
|
||||
* ioctl commands in 32 bit emulation
|
||||
*/
|
||||
#define EXT2_IOC32_GETFLAGS FS_IOC32_GETFLAGS
|
||||
#define EXT2_IOC32_SETFLAGS FS_IOC32_SETFLAGS
|
||||
#define EXT2_IOC32_GETVERSION FS_IOC32_GETVERSION
|
||||
#define EXT2_IOC32_SETVERSION FS_IOC32_SETVERSION
|
||||
|
||||
@@ -772,6 +768,9 @@ extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 start, u64 len);
|
||||
|
||||
/* ioctl.c */
|
||||
extern int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
extern int ext2_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa);
|
||||
extern long ext2_ioctl(struct file *, unsigned int, unsigned long);
|
||||
extern long ext2_compat_ioctl(struct file *, unsigned int, unsigned long);
|
||||
|
||||
|
||||
@@ -204,4 +204,6 @@ const struct inode_operations ext2_file_inode_operations = {
|
||||
.get_acl = ext2_get_acl,
|
||||
.set_acl = ext2_set_acl,
|
||||
.fiemap = ext2_fiemap,
|
||||
.fileattr_get = ext2_fileattr_get,
|
||||
.fileattr_set = ext2_fileattr_set,
|
||||
};
|
||||
|
||||
+33
-57
@@ -16,69 +16,51 @@
|
||||
#include <linux/mount.h>
|
||||
#include <asm/current.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
struct ext2_inode_info *ei = EXT2_I(d_inode(dentry));
|
||||
|
||||
fileattr_fill_flags(fa, ei->i_flags & EXT2_FL_USER_VISIBLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ext2_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct ext2_inode_info *ei = EXT2_I(inode);
|
||||
|
||||
if (fileattr_has_fsx(fa))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Is it quota file? Do not allow user to mess with it */
|
||||
if (IS_NOQUOTA(inode))
|
||||
return -EPERM;
|
||||
|
||||
ei->i_flags = (ei->i_flags & ~EXT2_FL_USER_MODIFIABLE) |
|
||||
(fa->flags & EXT2_FL_USER_MODIFIABLE);
|
||||
|
||||
ext2_set_inode_flags(inode);
|
||||
inode->i_ctime = current_time(inode);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct ext2_inode_info *ei = EXT2_I(inode);
|
||||
unsigned int flags;
|
||||
unsigned short rsv_window_size;
|
||||
int ret;
|
||||
|
||||
ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
|
||||
|
||||
switch (cmd) {
|
||||
case EXT2_IOC_GETFLAGS:
|
||||
flags = ei->i_flags & EXT2_FL_USER_VISIBLE;
|
||||
return put_user(flags, (int __user *) arg);
|
||||
case EXT2_IOC_SETFLAGS: {
|
||||
unsigned int oldflags;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!inode_owner_or_capable(&init_user_ns, inode)) {
|
||||
ret = -EACCES;
|
||||
goto setflags_out;
|
||||
}
|
||||
|
||||
if (get_user(flags, (int __user *) arg)) {
|
||||
ret = -EFAULT;
|
||||
goto setflags_out;
|
||||
}
|
||||
|
||||
flags = ext2_mask_flags(inode->i_mode, flags);
|
||||
|
||||
inode_lock(inode);
|
||||
/* Is it quota file? Do not allow user to mess with it */
|
||||
if (IS_NOQUOTA(inode)) {
|
||||
inode_unlock(inode);
|
||||
ret = -EPERM;
|
||||
goto setflags_out;
|
||||
}
|
||||
oldflags = ei->i_flags;
|
||||
|
||||
ret = vfs_ioc_setflags_prepare(inode, oldflags, flags);
|
||||
if (ret) {
|
||||
inode_unlock(inode);
|
||||
goto setflags_out;
|
||||
}
|
||||
|
||||
flags = flags & EXT2_FL_USER_MODIFIABLE;
|
||||
flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
|
||||
ei->i_flags = flags;
|
||||
|
||||
ext2_set_inode_flags(inode);
|
||||
inode->i_ctime = current_time(inode);
|
||||
inode_unlock(inode);
|
||||
|
||||
mark_inode_dirty(inode);
|
||||
setflags_out:
|
||||
mnt_drop_write_file(filp);
|
||||
return ret;
|
||||
}
|
||||
case EXT2_IOC_GETVERSION:
|
||||
return put_user(inode->i_generation, (int __user *) arg);
|
||||
case EXT2_IOC_SETVERSION: {
|
||||
@@ -163,12 +145,6 @@ long ext2_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 EXT2_IOC32_GETFLAGS:
|
||||
cmd = EXT2_IOC_GETFLAGS;
|
||||
break;
|
||||
case EXT2_IOC32_SETFLAGS:
|
||||
cmd = EXT2_IOC_SETFLAGS;
|
||||
break;
|
||||
case EXT2_IOC32_GETVERSION:
|
||||
cmd = EXT2_IOC_GETVERSION;
|
||||
break;
|
||||
|
||||
@@ -427,6 +427,8 @@ const struct inode_operations ext2_dir_inode_operations = {
|
||||
.get_acl = ext2_get_acl,
|
||||
.set_acl = ext2_set_acl,
|
||||
.tmpfile = ext2_tmpfile,
|
||||
.fileattr_get = ext2_fileattr_get,
|
||||
.fileattr_set = ext2_fileattr_set,
|
||||
};
|
||||
|
||||
const struct inode_operations ext2_special_inode_operations = {
|
||||
|
||||
+3
-9
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
+43
-165
@@ -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;
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -3194,6 +3194,9 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
int f2fs_truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
|
||||
void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count);
|
||||
int f2fs_precache_extents(struct inode *inode);
|
||||
int f2fs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
int f2fs_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa);
|
||||
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
|
||||
long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
int f2fs_transfer_project_quota(struct inode *inode, kprojid_t kprojid);
|
||||
|
||||
+38
-178
@@ -22,6 +22,7 @@
|
||||
#include <linux/file.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@@ -990,6 +991,8 @@ const struct inode_operations f2fs_file_inode_operations = {
|
||||
.set_acl = f2fs_set_acl,
|
||||
.listxattr = f2fs_listxattr,
|
||||
.fiemap = f2fs_fiemap,
|
||||
.fileattr_get = f2fs_fileattr_get,
|
||||
.fileattr_set = f2fs_fileattr_set,
|
||||
};
|
||||
|
||||
static int fill_zero(struct inode *inode, pgoff_t index,
|
||||
@@ -1871,13 +1874,16 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FS_IOC_GETFLAGS and FS_IOC_SETFLAGS support */
|
||||
/* FS_IOC_[GS]ETFLAGS and FS_IOC_FS[GS]ETXATTR support */
|
||||
|
||||
/*
|
||||
* To make a new on-disk f2fs i_flag gettable via FS_IOC_GETFLAGS, add an entry
|
||||
* for it to f2fs_fsflags_map[], and add its FS_*_FL equivalent to
|
||||
* F2FS_GETTABLE_FS_FL. To also make it settable via FS_IOC_SETFLAGS, also add
|
||||
* its FS_*_FL equivalent to F2FS_SETTABLE_FS_FL.
|
||||
*
|
||||
* Translating flags to fsx_flags value used by FS_IOC_FSGETXATTR and
|
||||
* FS_IOC_FSSETXATTR is done by the VFS.
|
||||
*/
|
||||
|
||||
static const struct {
|
||||
@@ -1952,67 +1958,6 @@ static inline u32 f2fs_fsflags_to_iflags(u32 fsflags)
|
||||
return iflags;
|
||||
}
|
||||
|
||||
static int f2fs_ioc_getflags(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
u32 fsflags = f2fs_iflags_to_fsflags(fi->i_flags);
|
||||
|
||||
if (IS_ENCRYPTED(inode))
|
||||
fsflags |= FS_ENCRYPT_FL;
|
||||
if (IS_VERITY(inode))
|
||||
fsflags |= FS_VERITY_FL;
|
||||
if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode))
|
||||
fsflags |= FS_INLINE_DATA_FL;
|
||||
if (is_inode_flag_set(inode, FI_PIN_FILE))
|
||||
fsflags |= FS_NOCOW_FL;
|
||||
|
||||
fsflags &= F2FS_GETTABLE_FS_FL;
|
||||
|
||||
return put_user(fsflags, (int __user *)arg);
|
||||
}
|
||||
|
||||
static int f2fs_ioc_setflags(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
u32 fsflags, old_fsflags;
|
||||
u32 iflags;
|
||||
int ret;
|
||||
|
||||
if (!inode_owner_or_capable(&init_user_ns, inode))
|
||||
return -EACCES;
|
||||
|
||||
if (get_user(fsflags, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
if (fsflags & ~F2FS_GETTABLE_FS_FL)
|
||||
return -EOPNOTSUPP;
|
||||
fsflags &= F2FS_SETTABLE_FS_FL;
|
||||
|
||||
iflags = f2fs_fsflags_to_iflags(fsflags);
|
||||
if (f2fs_mask_flags(inode->i_mode, iflags) != iflags)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
old_fsflags = f2fs_iflags_to_fsflags(fi->i_flags);
|
||||
ret = vfs_ioc_setflags_prepare(inode, old_fsflags, fsflags);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = f2fs_setflags_common(inode, iflags,
|
||||
f2fs_fsflags_to_iflags(F2FS_SETTABLE_FS_FL));
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
mnt_drop_write_file(filp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int f2fs_ioc_getversion(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
@@ -3019,9 +2964,8 @@ int f2fs_transfer_project_quota(struct inode *inode, kprojid_t kprojid)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_ioc_setproject(struct file *filp, __u32 projid)
|
||||
static int f2fs_ioc_setproject(struct inode *inode, __u32 projid)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct page *ipage;
|
||||
@@ -3082,7 +3026,7 @@ int f2fs_transfer_project_quota(struct inode *inode, kprojid_t kprojid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f2fs_ioc_setproject(struct file *filp, __u32 projid)
|
||||
static int f2fs_ioc_setproject(struct inode *inode, __u32 projid)
|
||||
{
|
||||
if (projid != F2FS_DEF_PROJID)
|
||||
return -EOPNOTSUPP;
|
||||
@@ -3090,123 +3034,55 @@ static int f2fs_ioc_setproject(struct file *filp, __u32 projid)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* FS_IOC_FSGETXATTR and FS_IOC_FSSETXATTR support */
|
||||
|
||||
/*
|
||||
* To make a new on-disk f2fs i_flag gettable via FS_IOC_FSGETXATTR and settable
|
||||
* via FS_IOC_FSSETXATTR, add an entry for it to f2fs_xflags_map[], and add its
|
||||
* FS_XFLAG_* equivalent to F2FS_SUPPORTED_XFLAGS.
|
||||
*/
|
||||
|
||||
static const struct {
|
||||
u32 iflag;
|
||||
u32 xflag;
|
||||
} f2fs_xflags_map[] = {
|
||||
{ F2FS_SYNC_FL, FS_XFLAG_SYNC },
|
||||
{ F2FS_IMMUTABLE_FL, FS_XFLAG_IMMUTABLE },
|
||||
{ F2FS_APPEND_FL, FS_XFLAG_APPEND },
|
||||
{ F2FS_NODUMP_FL, FS_XFLAG_NODUMP },
|
||||
{ F2FS_NOATIME_FL, FS_XFLAG_NOATIME },
|
||||
{ F2FS_PROJINHERIT_FL, FS_XFLAG_PROJINHERIT },
|
||||
};
|
||||
|
||||
#define F2FS_SUPPORTED_XFLAGS ( \
|
||||
FS_XFLAG_SYNC | \
|
||||
FS_XFLAG_IMMUTABLE | \
|
||||
FS_XFLAG_APPEND | \
|
||||
FS_XFLAG_NODUMP | \
|
||||
FS_XFLAG_NOATIME | \
|
||||
FS_XFLAG_PROJINHERIT)
|
||||
|
||||
/* Convert f2fs on-disk i_flags to FS_IOC_FS{GET,SET}XATTR flags */
|
||||
static inline u32 f2fs_iflags_to_xflags(u32 iflags)
|
||||
{
|
||||
u32 xflags = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(f2fs_xflags_map); i++)
|
||||
if (iflags & f2fs_xflags_map[i].iflag)
|
||||
xflags |= f2fs_xflags_map[i].xflag;
|
||||
|
||||
return xflags;
|
||||
}
|
||||
|
||||
/* Convert FS_IOC_FS{GET,SET}XATTR flags to f2fs on-disk i_flags */
|
||||
static inline u32 f2fs_xflags_to_iflags(u32 xflags)
|
||||
{
|
||||
u32 iflags = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(f2fs_xflags_map); i++)
|
||||
if (xflags & f2fs_xflags_map[i].xflag)
|
||||
iflags |= f2fs_xflags_map[i].iflag;
|
||||
|
||||
return iflags;
|
||||
}
|
||||
|
||||
static void f2fs_fill_fsxattr(struct inode *inode, struct fsxattr *fa)
|
||||
int f2fs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
u32 fsflags = f2fs_iflags_to_fsflags(fi->i_flags);
|
||||
|
||||
simple_fill_fsxattr(fa, f2fs_iflags_to_xflags(fi->i_flags));
|
||||
if (IS_ENCRYPTED(inode))
|
||||
fsflags |= FS_ENCRYPT_FL;
|
||||
if (IS_VERITY(inode))
|
||||
fsflags |= FS_VERITY_FL;
|
||||
if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode))
|
||||
fsflags |= FS_INLINE_DATA_FL;
|
||||
if (is_inode_flag_set(inode, FI_PIN_FILE))
|
||||
fsflags |= FS_NOCOW_FL;
|
||||
|
||||
fileattr_fill_flags(fa, fsflags & F2FS_GETTABLE_FS_FL);
|
||||
|
||||
if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)))
|
||||
fa->fsx_projid = from_kprojid(&init_user_ns, fi->i_projid);
|
||||
}
|
||||
|
||||
static int f2fs_ioc_fsgetxattr(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct fsxattr fa;
|
||||
|
||||
f2fs_fill_fsxattr(inode, &fa);
|
||||
|
||||
if (copy_to_user((struct fsxattr __user *)arg, &fa, sizeof(fa)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
|
||||
int f2fs_fileattr_set(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct fileattr *fa)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct fsxattr fa, old_fa;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
u32 fsflags = fa->flags, mask = F2FS_SETTABLE_FS_FL;
|
||||
u32 iflags;
|
||||
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(&init_user_ns, inode))
|
||||
return -EACCES;
|
||||
|
||||
if (fa.fsx_xflags & ~F2FS_SUPPORTED_XFLAGS)
|
||||
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
|
||||
return -EIO;
|
||||
if (!f2fs_is_checkpoint_ready(F2FS_I_SB(inode)))
|
||||
return -ENOSPC;
|
||||
if (fsflags & ~F2FS_GETTABLE_FS_FL)
|
||||
return -EOPNOTSUPP;
|
||||
fsflags &= F2FS_SETTABLE_FS_FL;
|
||||
if (!fa->flags_valid)
|
||||
mask &= FS_COMMON_FL;
|
||||
|
||||
iflags = f2fs_xflags_to_iflags(fa.fsx_xflags);
|
||||
iflags = f2fs_fsflags_to_iflags(fsflags);
|
||||
if (f2fs_mask_flags(inode->i_mode, iflags) != iflags)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = mnt_want_write_file(filp);
|
||||
if (err)
|
||||
return err;
|
||||
err = f2fs_setflags_common(inode, iflags, f2fs_fsflags_to_iflags(mask));
|
||||
if (!err)
|
||||
err = f2fs_ioc_setproject(inode, fa->fsx_projid);
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
f2fs_fill_fsxattr(inode, &old_fa);
|
||||
err = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = f2fs_setflags_common(inode, iflags,
|
||||
f2fs_xflags_to_iflags(F2FS_SUPPORTED_XFLAGS));
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = f2fs_ioc_setproject(filp, fa.fsx_projid);
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
mnt_drop_write_file(filp);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4233,10 +4109,6 @@ out:
|
||||
static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case FS_IOC_GETFLAGS:
|
||||
return f2fs_ioc_getflags(filp, arg);
|
||||
case FS_IOC_SETFLAGS:
|
||||
return f2fs_ioc_setflags(filp, arg);
|
||||
case FS_IOC_GETVERSION:
|
||||
return f2fs_ioc_getversion(filp, arg);
|
||||
case F2FS_IOC_START_ATOMIC_WRITE:
|
||||
@@ -4285,10 +4157,6 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
return f2fs_ioc_flush_device(filp, arg);
|
||||
case F2FS_IOC_GET_FEATURES:
|
||||
return f2fs_ioc_get_features(filp, arg);
|
||||
case FS_IOC_FSGETXATTR:
|
||||
return f2fs_ioc_fsgetxattr(filp, arg);
|
||||
case FS_IOC_FSSETXATTR:
|
||||
return f2fs_ioc_fssetxattr(filp, arg);
|
||||
case F2FS_IOC_GET_PIN_FILE:
|
||||
return f2fs_ioc_get_pin_file(filp, arg);
|
||||
case F2FS_IOC_SET_PIN_FILE:
|
||||
@@ -4518,12 +4386,6 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
return -ENOSPC;
|
||||
|
||||
switch (cmd) {
|
||||
case FS_IOC32_GETFLAGS:
|
||||
cmd = FS_IOC_GETFLAGS;
|
||||
break;
|
||||
case FS_IOC32_SETFLAGS:
|
||||
cmd = FS_IOC_SETFLAGS;
|
||||
break;
|
||||
case FS_IOC32_GETVERSION:
|
||||
cmd = FS_IOC_GETVERSION;
|
||||
break;
|
||||
@@ -4552,8 +4414,6 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
case F2FS_IOC_DEFRAGMENT:
|
||||
case F2FS_IOC_FLUSH_DEVICE:
|
||||
case F2FS_IOC_GET_FEATURES:
|
||||
case FS_IOC_FSGETXATTR:
|
||||
case FS_IOC_FSSETXATTR:
|
||||
case F2FS_IOC_GET_PIN_FILE:
|
||||
case F2FS_IOC_SET_PIN_FILE:
|
||||
case F2FS_IOC_PRECACHE_EXTENTS:
|
||||
|
||||
@@ -1327,6 +1327,8 @@ const struct inode_operations f2fs_dir_inode_operations = {
|
||||
.set_acl = f2fs_set_acl,
|
||||
.listxattr = f2fs_listxattr,
|
||||
.fiemap = f2fs_fiemap,
|
||||
.fileattr_get = f2fs_fileattr_get,
|
||||
.fileattr_set = f2fs_fileattr_set,
|
||||
};
|
||||
|
||||
const struct inode_operations f2fs_symlink_inode_operations = {
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ obj-$(CONFIG_FUSE_FS) += fuse.o
|
||||
obj-$(CONFIG_CUSE) += cuse.o
|
||||
obj-$(CONFIG_VIRTIO_FS) += virtiofs.o
|
||||
|
||||
fuse-y := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o
|
||||
fuse-y := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o
|
||||
fuse-$(CONFIG_FUSE_DAX) += dax.o
|
||||
|
||||
virtiofs-y := virtio_fs.o
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user