forked from Minki/linux
ext4: add FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR interface support
This patch adds FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR ioctl interface support for ext4. The interface is kept consistent with XFS_IOC_FSGETXATTR/XFS_IOC_FSGETXATTR. Signed-off-by: Li Xi <lixi@ddn.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Reviewed-by: Andreas Dilger <adilger@dilger.ca> Reviewed-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
689c958cbe
commit
9b7365fc1c
@ -381,6 +381,13 @@ struct flex_groups {
|
||||
#define EXT4_FL_USER_VISIBLE 0x304BDFFF /* User visible flags */
|
||||
#define EXT4_FL_USER_MODIFIABLE 0x204380FF /* User modifiable flags */
|
||||
|
||||
#define EXT4_FL_XFLAG_VISIBLE (EXT4_SYNC_FL | \
|
||||
EXT4_IMMUTABLE_FL | \
|
||||
EXT4_APPEND_FL | \
|
||||
EXT4_NODUMP_FL | \
|
||||
EXT4_NOATIME_FL | \
|
||||
EXT4_PROJINHERIT_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 |\
|
||||
@ -619,6 +626,46 @@ enum {
|
||||
#define EXT4_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
|
||||
#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
|
||||
|
||||
#ifndef FS_IOC_FSGETXATTR
|
||||
/* Until the uapi changes get merged for project quota... */
|
||||
|
||||
#define FS_IOC_FSGETXATTR _IOR('X', 31, struct fsxattr)
|
||||
#define FS_IOC_FSSETXATTR _IOW('X', 32, struct fsxattr)
|
||||
|
||||
/*
|
||||
* Structure for FS_IOC_FSGETXATTR and FS_IOC_FSSETXATTR.
|
||||
*/
|
||||
struct fsxattr {
|
||||
__u32 fsx_xflags; /* xflags field value (get/set) */
|
||||
__u32 fsx_extsize; /* extsize field value (get/set)*/
|
||||
__u32 fsx_nextents; /* nextents field value (get) */
|
||||
__u32 fsx_projid; /* project identifier (get/set) */
|
||||
unsigned char fsx_pad[12];
|
||||
};
|
||||
|
||||
/*
|
||||
* Flags for the fsx_xflags field
|
||||
*/
|
||||
#define FS_XFLAG_REALTIME 0x00000001 /* data in realtime volume */
|
||||
#define FS_XFLAG_PREALLOC 0x00000002 /* preallocated file extents */
|
||||
#define FS_XFLAG_IMMUTABLE 0x00000008 /* file cannot be modified */
|
||||
#define FS_XFLAG_APPEND 0x00000010 /* all writes append */
|
||||
#define FS_XFLAG_SYNC 0x00000020 /* all writes synchronous */
|
||||
#define FS_XFLAG_NOATIME 0x00000040 /* do not update access time */
|
||||
#define FS_XFLAG_NODUMP 0x00000080 /* do not include in backups */
|
||||
#define FS_XFLAG_RTINHERIT 0x00000100 /* create with rt bit set */
|
||||
#define FS_XFLAG_PROJINHERIT 0x00000200 /* create with parents projid */
|
||||
#define FS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */
|
||||
#define FS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */
|
||||
#define FS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */
|
||||
#define FS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */
|
||||
#define FS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */
|
||||
#define FS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */
|
||||
#endif /* !defined(FS_IOC_FSGETXATTR) */
|
||||
|
||||
#define EXT4_IOC_FSGETXATTR FS_IOC_FSGETXATTR
|
||||
#define EXT4_IOC_FSSETXATTR FS_IOC_FSSETXATTR
|
||||
|
||||
#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
|
||||
/*
|
||||
* ioctl commands in 32 bit emulation
|
||||
|
376
fs/ext4/ioctl.c
376
fs/ext4/ioctl.c
@ -14,6 +14,7 @@
|
||||
#include <linux/mount.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "ext4_jbd2.h"
|
||||
#include "ext4.h"
|
||||
@ -202,6 +203,238 @@ static int uuid_is_zero(__u8 u[16])
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ext4_ioctl_setflags(struct inode *inode,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
handle_t *handle = NULL;
|
||||
int err = EPERM, migrate = 0;
|
||||
struct ext4_iloc iloc;
|
||||
unsigned int oldflags, mask, i;
|
||||
unsigned int jflag;
|
||||
|
||||
/* Is it quota file? Do not allow user to mess with it */
|
||||
if (IS_NOQUOTA(inode))
|
||||
goto flags_out;
|
||||
|
||||
oldflags = ei->i_flags;
|
||||
|
||||
/* The JOURNAL_DATA flag is modifiable only by root */
|
||||
jflag = flags & EXT4_JOURNAL_DATA_FL;
|
||||
|
||||
/*
|
||||
* 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
|
||||
if (!capable(CAP_LINUX_IMMUTABLE))
|
||||
goto flags_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The JOURNAL_DATA flag can only be changed by
|
||||
* the relevant capability.
|
||||
*/
|
||||
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
|
||||
if (!capable(CAP_SYS_RESOURCE))
|
||||
goto flags_out;
|
||||
}
|
||||
if ((flags ^ oldflags) & EXT4_EXTENTS_FL)
|
||||
migrate = 1;
|
||||
|
||||
if (flags & EXT4_EOFBLOCKS_FL) {
|
||||
/* we don't support adding EOFBLOCKS flag */
|
||||
if (!(oldflags & EXT4_EOFBLOCKS_FL)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto flags_out;
|
||||
}
|
||||
} else if (oldflags & EXT4_EOFBLOCKS_FL)
|
||||
ext4_truncate(inode);
|
||||
|
||||
handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
|
||||
if (IS_ERR(handle)) {
|
||||
err = PTR_ERR(handle);
|
||||
goto flags_out;
|
||||
}
|
||||
if (IS_SYNC(inode))
|
||||
ext4_handle_sync(handle);
|
||||
err = ext4_reserve_inode_write(handle, inode, &iloc);
|
||||
if (err)
|
||||
goto flags_err;
|
||||
|
||||
for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
|
||||
if (!(mask & EXT4_FL_USER_MODIFIABLE))
|
||||
continue;
|
||||
if (mask & flags)
|
||||
ext4_set_inode_flag(inode, i);
|
||||
else
|
||||
ext4_clear_inode_flag(inode, i);
|
||||
}
|
||||
|
||||
ext4_set_inode_flags(inode);
|
||||
inode->i_ctime = ext4_current_time(inode);
|
||||
|
||||
err = ext4_mark_iloc_dirty(handle, inode, &iloc);
|
||||
flags_err:
|
||||
ext4_journal_stop(handle);
|
||||
if (err)
|
||||
goto flags_out;
|
||||
|
||||
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
|
||||
err = ext4_change_inode_journal_flag(inode, jflag);
|
||||
if (err)
|
||||
goto flags_out;
|
||||
if (migrate) {
|
||||
if (flags & EXT4_EXTENTS_FL)
|
||||
err = ext4_ext_migrate(inode);
|
||||
else
|
||||
err = ext4_ind_migrate(inode);
|
||||
}
|
||||
|
||||
flags_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
static int ext4_ioctl_setproject(struct file *filp, __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;
|
||||
handle_t *handle;
|
||||
kprojid_t kprojid;
|
||||
struct ext4_iloc iloc;
|
||||
struct ext4_inode *raw_inode;
|
||||
|
||||
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
|
||||
EXT4_FEATURE_RO_COMPAT_PROJECT)) {
|
||||
if (projid != EXT4_DEF_PROJID)
|
||||
return -EOPNOTSUPP;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
kprojid = make_kprojid(&init_user_ns, (projid_t)projid);
|
||||
|
||||
if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
|
||||
return 0;
|
||||
|
||||
err = mnt_want_write_file(filp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = -EPERM;
|
||||
mutex_lock(&inode->i_mutex);
|
||||
/* Is it quota file? Do not allow user to mess with it */
|
||||
if (IS_NOQUOTA(inode))
|
||||
goto out_unlock;
|
||||
|
||||
err = ext4_get_inode_loc(inode, &iloc);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
raw_inode = ext4_raw_inode(&iloc);
|
||||
if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) {
|
||||
err = -EOVERFLOW;
|
||||
brelse(iloc.bh);
|
||||
goto out_unlock;
|
||||
}
|
||||
brelse(iloc.bh);
|
||||
|
||||
dquot_initialize(inode);
|
||||
|
||||
handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
|
||||
EXT4_QUOTA_INIT_BLOCKS(sb) +
|
||||
EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
|
||||
if (IS_ERR(handle)) {
|
||||
err = PTR_ERR(handle);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = ext4_reserve_inode_write(handle, inode, &iloc);
|
||||
if (err)
|
||||
goto out_stop;
|
||||
|
||||
if (sb_has_quota_limits_enabled(sb, PRJQUOTA)) {
|
||||
struct dquot *transfer_to[MAXQUOTAS] = { };
|
||||
|
||||
transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
|
||||
if (transfer_to[PRJQUOTA]) {
|
||||
err = __dquot_transfer(inode, transfer_to);
|
||||
dqput(transfer_to[PRJQUOTA]);
|
||||
if (err)
|
||||
goto out_dirty;
|
||||
}
|
||||
}
|
||||
EXT4_I(inode)->i_projid = kprojid;
|
||||
inode->i_ctime = ext4_current_time(inode);
|
||||
out_dirty:
|
||||
rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
|
||||
if (!err)
|
||||
err = rc;
|
||||
out_stop:
|
||||
ext4_journal_stop(handle);
|
||||
out_unlock:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
mnt_drop_write_file(filp);
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
|
||||
{
|
||||
if (projid != EXT4_DEF_PROJID)
|
||||
return -EOPNOTSUPP;
|
||||
return 0;
|
||||
}
|
||||
#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;
|
||||
return xflags;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
return iflags;
|
||||
}
|
||||
|
||||
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
@ -217,11 +450,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
|
||||
return put_user(flags, (int __user *) arg);
|
||||
case EXT4_IOC_SETFLAGS: {
|
||||
handle_t *handle = NULL;
|
||||
int err, migrate = 0;
|
||||
struct ext4_iloc iloc;
|
||||
unsigned int oldflags, mask, i;
|
||||
unsigned int jflag;
|
||||
int err;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EACCES;
|
||||
@ -235,89 +464,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
|
||||
flags = ext4_mask_flags(inode->i_mode, flags);
|
||||
|
||||
err = -EPERM;
|
||||
mutex_lock(&inode->i_mutex);
|
||||
/* Is it quota file? Do not allow user to mess with it */
|
||||
if (IS_NOQUOTA(inode))
|
||||
goto flags_out;
|
||||
|
||||
oldflags = ei->i_flags;
|
||||
|
||||
/* The JOURNAL_DATA flag is modifiable only by root */
|
||||
jflag = flags & EXT4_JOURNAL_DATA_FL;
|
||||
|
||||
/*
|
||||
* 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
|
||||
if (!capable(CAP_LINUX_IMMUTABLE))
|
||||
goto flags_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The JOURNAL_DATA flag can only be changed by
|
||||
* the relevant capability.
|
||||
*/
|
||||
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
|
||||
if (!capable(CAP_SYS_RESOURCE))
|
||||
goto flags_out;
|
||||
}
|
||||
if ((flags ^ oldflags) & EXT4_EXTENTS_FL)
|
||||
migrate = 1;
|
||||
|
||||
if (flags & EXT4_EOFBLOCKS_FL) {
|
||||
/* we don't support adding EOFBLOCKS flag */
|
||||
if (!(oldflags & EXT4_EOFBLOCKS_FL)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto flags_out;
|
||||
}
|
||||
} else if (oldflags & EXT4_EOFBLOCKS_FL)
|
||||
ext4_truncate(inode);
|
||||
|
||||
handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
|
||||
if (IS_ERR(handle)) {
|
||||
err = PTR_ERR(handle);
|
||||
goto flags_out;
|
||||
}
|
||||
if (IS_SYNC(inode))
|
||||
ext4_handle_sync(handle);
|
||||
err = ext4_reserve_inode_write(handle, inode, &iloc);
|
||||
if (err)
|
||||
goto flags_err;
|
||||
|
||||
for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
|
||||
if (!(mask & EXT4_FL_USER_MODIFIABLE))
|
||||
continue;
|
||||
if (mask & flags)
|
||||
ext4_set_inode_flag(inode, i);
|
||||
else
|
||||
ext4_clear_inode_flag(inode, i);
|
||||
}
|
||||
|
||||
ext4_set_inode_flags(inode);
|
||||
inode->i_ctime = ext4_current_time(inode);
|
||||
|
||||
err = ext4_mark_iloc_dirty(handle, inode, &iloc);
|
||||
flags_err:
|
||||
ext4_journal_stop(handle);
|
||||
if (err)
|
||||
goto flags_out;
|
||||
|
||||
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
|
||||
err = ext4_change_inode_journal_flag(inode, jflag);
|
||||
if (err)
|
||||
goto flags_out;
|
||||
if (migrate) {
|
||||
if (flags & EXT4_EXTENTS_FL)
|
||||
err = ext4_ext_migrate(inode);
|
||||
else
|
||||
err = ext4_ind_migrate(inode);
|
||||
}
|
||||
|
||||
flags_out:
|
||||
err = ext4_ioctl_setflags(inode, flags);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
mnt_drop_write_file(filp);
|
||||
return err;
|
||||
@ -689,6 +837,60 @@ encryption_policy_out:
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
case EXT4_IOC_FSGETXATTR:
|
||||
{
|
||||
struct fsxattr fa;
|
||||
|
||||
memset(&fa, 0, sizeof(struct fsxattr));
|
||||
ext4_get_inode_flags(ei);
|
||||
fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE);
|
||||
|
||||
if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
|
||||
EXT4_FEATURE_RO_COMPAT_PROJECT)) {
|
||||
fa.fsx_projid = (__u32)from_kprojid(&init_user_ns,
|
||||
EXT4_I(inode)->i_projid);
|
||||
}
|
||||
|
||||
if (copy_to_user((struct fsxattr __user *)arg,
|
||||
&fa, sizeof(fa)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case EXT4_IOC_FSSETXATTR:
|
||||
{
|
||||
struct fsxattr 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(inode))
|
||||
return -EACCES;
|
||||
|
||||
err = mnt_want_write_file(filp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
flags = ext4_xflags_to_iflags(fa.fsx_xflags);
|
||||
flags = ext4_mask_flags(inode->i_mode, flags);
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) |
|
||||
(flags & EXT4_FL_XFLAG_VISIBLE);
|
||||
err = ext4_ioctl_setflags(inode, flags);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
mnt_drop_write_file(filp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ext4_ioctl_setproject(filp, fa.fsx_projid);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user