lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20230612030121.2393541-6-shengyong@oppo.com>
Date:   Mon, 12 Jun 2023 11:01:20 +0800
From:   Sheng Yong <shengyong@...o.com>
To:     jaegeuk@...nel.org, chao@...nel.org
Cc:     linux-f2fs-devel@...ts.sourceforge.net,
        linux-kernel@...r.kernel.org, ebiggers@...nel.org,
        Sheng Yong <shengyong@...o.com>
Subject: [PATCH v4 5/6] f2fs: add f2fs_ioc_[get|set]_extra_attr

This patch introduces two ioctls:
  * f2fs_ioc_get_extra_attr
  * f2fs_ioc_set_extra_attr
to get or modify values in f2fs_inode's extra attribute area.

The argument of these two ioctls is `struct f2fs_extra_attr', which has
three members:
  * field: indicates which field in extra attribute area is handled
  * attr: value or userspace pointer
  * attr_size: size of `attr'

The `field' member could help extend functionality of these two ioctls
without modify or add new interfaces, if more fields are added into
extra attributes ares in the feture.

Signed-off-by: Sheng Yong <shengyong@...o.com>
---
 fs/f2fs/file.c            | 205 ++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/f2fs.h |  25 +++++
 2 files changed, 230 insertions(+)

diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index f8aa842b5d233..39d04f8f0bb6b 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -4179,6 +4179,207 @@ static int f2fs_ioc_compress_file(struct file *filp)
 	return ret;
 }
 
+static bool extra_attr_fits_in_inode(struct inode *inode, int field)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_inode *ri;
+
+	switch (field) {
+	case F2FS_EXTRA_ATTR_TOTAL_SIZE:
+	case F2FS_EXTRA_ATTR_ISIZE:
+	case F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE:
+		return true;
+	case F2FS_EXTRA_ATTR_PROJID:
+		if (!F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_projid))
+			return false;
+		return true;
+	case F2FS_EXTRA_ATTR_INODE_CHKSUM:
+		if (!F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_inode_checksum))
+			return false;
+		return true;
+	case F2FS_EXTRA_ATTR_CRTIME:
+		if (!F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_crtime))
+			return false;
+		return true;
+	case F2FS_EXTRA_ATTR_COMPR_BLOCKS:
+	case F2FS_EXTRA_ATTR_COMPR_OPTION:
+		if (!F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_compr_blocks))
+			return false;
+		return true;
+	default:
+		BUG_ON(1);
+		return false;
+	}
+}
+
+static int f2fs_ioc_get_extra_attr(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+	struct f2fs_extra_attr attr;
+	u32 chksum;
+	int ret = 0;
+
+	if (!f2fs_has_extra_attr(inode))
+		return -EOPNOTSUPP;
+
+	if (copy_from_user(&attr, (void __user *)arg, sizeof(attr)))
+		return -EFAULT;
+
+	if (attr.field >= F2FS_EXTRA_ATTR_MAX)
+		return -EINVAL;
+
+	if (!extra_attr_fits_in_inode(inode, attr.field))
+		return -EOPNOTSUPP;
+
+	switch (attr.field) {
+	case F2FS_EXTRA_ATTR_TOTAL_SIZE:
+		attr.attr = F2FS_TOTAL_EXTRA_ATTR_SIZE;
+		break;
+	case F2FS_EXTRA_ATTR_ISIZE:
+		attr.attr = fi->i_extra_isize;
+		break;
+	case F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE:
+		if (!f2fs_has_inline_xattr(inode))
+			return -EOPNOTSUPP;
+		attr.attr = get_inline_xattr_addrs(inode);
+		break;
+	case F2FS_EXTRA_ATTR_PROJID:
+		if (!f2fs_sb_has_project_quota(F2FS_I_SB(inode)))
+			return -EOPNOTSUPP;
+		attr.attr = from_kprojid(&init_user_ns, fi->i_projid);
+		break;
+	case F2FS_EXTRA_ATTR_INODE_CHKSUM:
+		ret = f2fs_inode_chksum_get(sbi, inode, &chksum);
+		if (ret)
+			return ret;
+		attr.attr = chksum;
+		break;
+	case F2FS_EXTRA_ATTR_CRTIME:
+		if (!f2fs_sb_has_inode_crtime(sbi))
+			return -EOPNOTSUPP;
+		if (attr.attr_size == sizeof(struct timespec64)) {
+			if (put_timespec64(&fi->i_crtime,
+					(void __user *)(uintptr_t)attr.attr))
+				return -EFAULT;
+		} else if (attr.attr_size == sizeof(struct old_timespec32)) {
+			if (put_old_timespec32(&fi->i_crtime,
+					(void __user *)(uintptr_t)attr.attr))
+				return -EFAULT;
+		} else {
+			return -EINVAL;
+		}
+		break;
+	case F2FS_EXTRA_ATTR_COMPR_BLOCKS:
+		if (attr.attr_size != sizeof(__u64))
+			return -EINVAL;
+		ret = f2fs_get_compress_blocks(inode, &attr.attr);
+		break;
+	case F2FS_EXTRA_ATTR_COMPR_OPTION:
+		ret = f2fs_ioc_get_compress_option(filp, attr.attr);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	if (copy_to_user((void __user *)arg, &attr, sizeof(attr)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int f2fs_ioc_set_extra_attr(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+	struct f2fs_extra_attr attr;
+	struct page *ipage;
+	void *inline_addr;
+	int ret;
+
+	if (!f2fs_has_extra_attr(inode))
+		return -EOPNOTSUPP;
+
+	if (copy_from_user(&attr, (void __user *)arg, sizeof(attr)))
+		return -EFAULT;
+
+	if (attr.field >= F2FS_EXTRA_ATTR_MAX)
+		return -EINVAL;
+
+	if (!extra_attr_fits_in_inode(inode, attr.field))
+		return -EOPNOTSUPP;
+
+	switch (attr.field) {
+	case F2FS_EXTRA_ATTR_TOTAL_SIZE:
+	case F2FS_EXTRA_ATTR_ISIZE:
+	case F2FS_EXTRA_ATTR_PROJID:
+	case F2FS_EXTRA_ATTR_INODE_CHKSUM:
+	case F2FS_EXTRA_ATTR_CRTIME:
+	case F2FS_EXTRA_ATTR_COMPR_BLOCKS:
+		/* read only attribtues */
+		return -EOPNOTSUPP;
+	case F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE:
+		if (!f2fs_sb_has_flexible_inline_xattr(sbi) ||
+		    !f2fs_has_inline_xattr(inode))
+			return -EOPNOTSUPP;
+		if (attr.attr < MIN_INLINE_XATTR_SIZE ||
+		    attr.attr > MAX_INLINE_XATTR_SIZE)
+			return -EINVAL;
+		inode_lock(inode);
+		f2fs_lock_op(sbi);
+		f2fs_down_write(&F2FS_I(inode)->i_xattr_sem);
+		if (i_size_read(inode) || F2FS_I(inode)->i_xattr_nid) {
+			/*
+			 * it is not allowed to set this field if the inode
+			 * has data or xattr node
+			 */
+			ret = -EFBIG;
+			goto xattr_out_unlock;
+		}
+		ipage = f2fs_get_node_page(sbi, inode->i_ino);
+		if (IS_ERR(ipage)) {
+			ret = PTR_ERR(ipage);
+			goto xattr_out_unlock;
+		}
+		inline_addr = inline_xattr_addr(inode, ipage);
+		if (!IS_XATTR_LAST_ENTRY(XATTR_FIRST_ENTRY(inline_addr))) {
+			ret = -EFBIG;
+		} else {
+			struct f2fs_xattr_header *hdr;
+			struct f2fs_xattr_entry *ent;
+
+			F2FS_I(inode)->i_inline_xattr_size = (int)attr.attr;
+			inline_addr = inline_xattr_addr(inode, ipage);
+			hdr = XATTR_HDR(inline_addr);
+			ent = XATTR_FIRST_ENTRY(inline_addr);
+			hdr->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC);
+			hdr->h_refcount = cpu_to_le32(1);
+			memset(ent, 0, attr.attr - sizeof(*hdr));
+			set_page_dirty(ipage);
+			ret = 0;
+		}
+		f2fs_put_page(ipage, 1);
+xattr_out_unlock:
+		f2fs_up_write(&F2FS_I(inode)->i_xattr_sem);
+		f2fs_unlock_op(sbi);
+		inode_unlock(inode);
+		if (!ret)
+			f2fs_balance_fs(sbi, true);
+		break;
+	case F2FS_EXTRA_ATTR_COMPR_OPTION:
+		ret = f2fs_ioc_set_compress_option(filp, attr.attr);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
 static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	switch (cmd) {
@@ -4265,6 +4466,10 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_decompress_file(filp);
 	case F2FS_IOC_COMPRESS_FILE:
 		return f2fs_ioc_compress_file(filp);
+	case F2FS_IOC_GET_EXTRA_ATTR:
+		return f2fs_ioc_get_extra_attr(filp, arg);
+	case F2FS_IOC_SET_EXTRA_ATTR:
+		return f2fs_ioc_set_extra_attr(filp, arg);
 	default:
 		return -ENOTTY;
 	}
diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
index 955d440be1046..2b53e90421bfc 100644
--- a/include/uapi/linux/f2fs.h
+++ b/include/uapi/linux/f2fs.h
@@ -43,6 +43,10 @@
 #define F2FS_IOC_DECOMPRESS_FILE	_IO(F2FS_IOCTL_MAGIC, 23)
 #define F2FS_IOC_COMPRESS_FILE		_IO(F2FS_IOCTL_MAGIC, 24)
 #define F2FS_IOC_START_ATOMIC_REPLACE	_IO(F2FS_IOCTL_MAGIC, 25)
+#define F2FS_IOC_GET_EXTRA_ATTR		_IOR(F2FS_IOCTL_MAGIC, 26,	\
+						struct f2fs_extra_attr)
+#define F2FS_IOC_SET_EXTRA_ATTR		_IOW(F2FS_IOCTL_MAGIC, 27,	\
+						struct f2fs_extra_attr)
 
 /*
  * should be same as XFS_IOC_GOINGDOWN.
@@ -96,4 +100,25 @@ struct f2fs_comp_option {
 	__u8 log_cluster_size;
 };
 
+enum {
+	F2FS_EXTRA_ATTR_TOTAL_SIZE,		/* ro, size of extra attr area */
+	F2FS_EXTRA_ATTR_ISIZE,			/* ro, i_extra_isize */
+	F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE,	/* rw, i_inline_xattr_size */
+	F2FS_EXTRA_ATTR_PROJID,			/* ro, i_projid */
+	F2FS_EXTRA_ATTR_INODE_CHKSUM,		/* ro, i_inode_chksum */
+	F2FS_EXTRA_ATTR_CRTIME,			/* ro, i_crtime, i_crtime_nsec */
+	F2FS_EXTRA_ATTR_COMPR_BLOCKS,		/* ro, i_compr_blocks */
+	F2FS_EXTRA_ATTR_COMPR_OPTION,		/* rw, i_compress_algorithm,
+						 *     i_log_cluster_size
+						 */
+	F2FS_EXTRA_ATTR_MAX,
+};
+
+struct f2fs_extra_attr {
+	__u8 field;		/* F2FS_EXTRA_ATTR_* */
+	__u8 rsvd1;
+	__u16 attr_size;	/* size of @attr */
+	__u32 rsvd2;
+	__u64 attr;		/* attr value or pointer */
+};
 #endif /* _UAPI_LINUX_F2FS_H */
-- 
2.40.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ