mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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
					
				
					 2 changed files with 336 additions and 87 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										258
									
								
								fs/ext4/ioctl.c
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								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,41 +203,16 @@ static int uuid_is_zero(__u8 u[16])
 | 
			
		|||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 | 
			
		||||
static int ext4_ioctl_setflags(struct inode *inode,
 | 
			
		||||
			       unsigned int flags)
 | 
			
		||||
{
 | 
			
		||||
	struct inode *inode = file_inode(filp);
 | 
			
		||||
	struct super_block *sb = inode->i_sb;
 | 
			
		||||
	struct ext4_inode_info *ei = EXT4_I(inode);
 | 
			
		||||
	unsigned int flags;
 | 
			
		||||
 | 
			
		||||
	ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
 | 
			
		||||
 | 
			
		||||
	switch (cmd) {
 | 
			
		||||
	case EXT4_IOC_GETFLAGS:
 | 
			
		||||
		ext4_get_inode_flags(ei);
 | 
			
		||||
		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;
 | 
			
		||||
	int err = EPERM, migrate = 0;
 | 
			
		||||
	struct ext4_iloc iloc;
 | 
			
		||||
	unsigned int oldflags, mask, i;
 | 
			
		||||
	unsigned int jflag;
 | 
			
		||||
 | 
			
		||||
		if (!inode_owner_or_capable(inode))
 | 
			
		||||
			return -EACCES;
 | 
			
		||||
 | 
			
		||||
		if (get_user(flags, (int __user *) arg))
 | 
			
		||||
			return -EFAULT;
 | 
			
		||||
 | 
			
		||||
		err = mnt_want_write_file(filp);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
| 
						 | 
				
			
			@ -318,6 +294,178 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
	struct super_block *sb = inode->i_sb;
 | 
			
		||||
	struct ext4_inode_info *ei = EXT4_I(inode);
 | 
			
		||||
	unsigned int flags;
 | 
			
		||||
 | 
			
		||||
	ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
 | 
			
		||||
 | 
			
		||||
	switch (cmd) {
 | 
			
		||||
	case EXT4_IOC_GETFLAGS:
 | 
			
		||||
		ext4_get_inode_flags(ei);
 | 
			
		||||
		flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
 | 
			
		||||
		return put_user(flags, (int __user *) arg);
 | 
			
		||||
	case EXT4_IOC_SETFLAGS: {
 | 
			
		||||
		int err;
 | 
			
		||||
 | 
			
		||||
		if (!inode_owner_or_capable(inode))
 | 
			
		||||
			return -EACCES;
 | 
			
		||||
 | 
			
		||||
		if (get_user(flags, (int __user *) arg))
 | 
			
		||||
			return -EFAULT;
 | 
			
		||||
 | 
			
		||||
		err = mnt_want_write_file(filp);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
 | 
			
		||||
		flags = ext4_mask_flags(inode->i_mode, flags);
 | 
			
		||||
 | 
			
		||||
		mutex_lock(&inode->i_mutex);
 | 
			
		||||
		err = ext4_ioctl_setflags(inode, flags);
 | 
			
		||||
		mutex_unlock(&inode->i_mutex);
 | 
			
		||||
		mnt_drop_write_file(filp);
 | 
			
		||||
		return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -689,6 +837,60 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 | 
			
		|||
		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 a new issue