This patch introduces two ioctls:
* f2fs_ioc_get_extra_attr
* f2fs_ioc_set_extra_attr
to get or modify values in 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 <[email protected]>
---
fs/f2fs/f2fs.h | 2 +
fs/f2fs/file.c | 289 ++++++++++++++++++++++++++++++++++++--
fs/f2fs/inode.c | 21 +++
fs/f2fs/super.c | 2 +-
fs/f2fs/xattr.h | 1 +
include/uapi/linux/f2fs.h | 35 +++++
6 files changed, 337 insertions(+), 13 deletions(-)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index faa27f41f39d4..08e8527734df5 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3475,6 +3475,8 @@ int f2fs_pin_file_control(struct inode *inode, bool inc);
void f2fs_set_inode_flags(struct inode *inode);
bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page);
void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page);
+int f2fs_inode_chksum_get(struct f2fs_sb_info *sbi, struct inode *inode,
+ u32 *chksum);
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino);
struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino);
int f2fs_try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 78aa8cff4b41d..cdb192faf7c36 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -25,6 +25,8 @@
#include <linux/fileattr.h>
#include <linux/fadvise.h>
#include <linux/iomap.h>
+#include <linux/lz4.h>
+#include <linux/zstd.h>
#include "f2fs.h"
#include "node.h"
@@ -3376,10 +3378,12 @@ static int f2fs_ioc_setfslabel(struct file *filp, unsigned long arg)
return err;
}
-static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
+static int f2fs_get_compress_blocks(struct file *filp, unsigned int attr_size)
{
struct inode *inode = file_inode(filp);
- __u64 blocks;
+
+ if (attr_size != sizeof(__u64))
+ return -EINVAL;
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
@@ -3387,7 +3391,14 @@ static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
if (!f2fs_compressed_file(inode))
return -EINVAL;
- blocks = atomic_read(&F2FS_I(inode)->i_compr_blocks);
+ return atomic_read(&F2FS_I(inode)->i_compr_blocks);
+}
+
+static int f2fs_ioc_get_compress_blocks(struct file *filp, unsigned long arg)
+{
+ __u64 blocks;
+
+ blocks = f2fs_get_compress_blocks(filp, sizeof(blocks));
return put_user(blocks, (u64 __user *)arg);
}
@@ -3905,10 +3916,14 @@ static int f2fs_sec_trim_file(struct file *filp, unsigned long arg)
return ret;
}
-static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
+static int f2fs_get_compress_option_v2(struct file *filp,
+ unsigned long attr, __u16 *attr_size)
{
struct inode *inode = file_inode(filp);
- struct f2fs_comp_option option;
+ struct f2fs_comp_option_v2 option;
+
+ if (sizeof(option) < *attr_size)
+ *attr_size = sizeof(option);
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
@@ -3922,31 +3937,42 @@ static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
option.algorithm = F2FS_I(inode)->i_compress_algorithm;
option.log_cluster_size = F2FS_I(inode)->i_log_cluster_size;
+ option.level = F2FS_I(inode)->i_compress_level;
+ option.flag = F2FS_I(inode)->i_compress_flag;
inode_unlock_shared(inode);
- if (copy_to_user((struct f2fs_comp_option __user *)arg, &option,
- sizeof(option)))
+ if (copy_to_user((void __user *)attr, &option, *attr_size))
return -EFAULT;
return 0;
}
-static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
+static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
+{
+ __u16 size = sizeof(struct f2fs_comp_option);
+
+ return f2fs_get_compress_option_v2(filp, arg, &size);
+}
+
+static int f2fs_set_compress_option_v2(struct file *filp,
+ unsigned long attr, __u16 *attr_size)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct f2fs_comp_option option;
+ struct f2fs_comp_option_v2 option;
int ret = 0;
+ if (sizeof(option) < *attr_size)
+ *attr_size = sizeof(option);
+
if (!f2fs_sb_has_compression(sbi))
return -EOPNOTSUPP;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
- if (copy_from_user(&option, (struct f2fs_comp_option __user *)arg,
- sizeof(option)))
+ if (copy_from_user(&option, (void __user *)attr, *attr_size))
return -EFAULT;
if (!f2fs_compressed_file(inode) ||
@@ -3955,6 +3981,28 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
option.algorithm >= COMPRESS_MAX)
return -EINVAL;
+ if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
+ if (option.level != 0) {
+ switch (option.algorithm) {
+ case COMPRESS_LZO:
+ case COMPRESS_LZORLE:
+ return -EINVAL;
+ case COMPRESS_LZ4:
+ if (option.level < LZ4HC_MIN_CLEVEL ||
+ option.level > LZ4HC_MAX_CLEVEL)
+ return -EINVAL;
+ break;
+ case COMPRESS_ZSTD:
+ if (option.level > zstd_max_clevel())
+ return -EINVAL;
+ break;
+ }
+ }
+
+ if (option.flag > BIT(COMPRESS_MAX_FLAG) - 1)
+ return -EINVAL;
+ }
+
file_start_write(filp);
inode_lock(inode);
@@ -3971,6 +4019,10 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
F2FS_I(inode)->i_compress_algorithm = option.algorithm;
F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size;
F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size);
+ if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
+ F2FS_I(inode)->i_compress_level = option.level;
+ F2FS_I(inode)->i_compress_flag = option.flag;
+ }
f2fs_mark_inode_dirty_sync(inode, true);
if (!f2fs_is_compress_backend_ready(inode))
@@ -3983,6 +4035,13 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
return ret;
}
+static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
+{
+ __u16 size = sizeof(struct f2fs_comp_option);
+
+ return f2fs_set_compress_option_v2(filp, arg, &size);
+}
+
static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
{
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, page_idx);
@@ -4168,6 +4227,208 @@ 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 *)attr.attr))
+ return -EFAULT;
+ } else if (attr.attr_size == sizeof(struct old_timespec32)) {
+ if (put_old_timespec32(&fi->i_crtime,
+ (void __user *)attr.attr))
+ return -EFAULT;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ case F2FS_EXTRA_ATTR_COMPR_BLOCKS:
+ ret = f2fs_get_compress_blocks(filp, attr.attr_size);
+ attr.attr = ret;
+ break;
+ case F2FS_EXTRA_ATTR_COMPR_OPTION:
+ ret = f2fs_get_compress_option_v2(filp, attr.attr,
+ &attr.attr_size);
+ 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_set_compress_option_v2(filp, attr.attr,
+ &attr.attr_size);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
@@ -4239,7 +4500,7 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case FS_IOC_SETFSLABEL:
return f2fs_ioc_setfslabel(filp, arg);
case F2FS_IOC_GET_COMPRESS_BLOCKS:
- return f2fs_get_compress_blocks(filp, arg);
+ return f2fs_ioc_get_compress_blocks(filp, arg);
case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
return f2fs_release_compress_blocks(filp, arg);
case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
@@ -4254,6 +4515,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/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 0a17484443299..aef9c1fd37dca 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -204,6 +204,27 @@ void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page)
ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page));
}
+int f2fs_inode_chksum_get(struct f2fs_sb_info *sbi,
+ struct inode *inode, u32 *chksum)
+{
+ struct page *ipage;
+ struct f2fs_inode_info *fi = F2FS_I(inode);
+ struct f2fs_inode *ri;
+
+ if (!f2fs_sb_has_inode_chksum(sbi) ||
+ !f2fs_has_extra_attr(inode) ||
+ !F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_inode_checksum))
+ return -EOPNOTSUPP;
+
+ ipage = f2fs_get_node_page(sbi, inode->i_ino);
+ if (IS_ERR(ipage))
+ return PTR_ERR(ipage);
+
+ *chksum = f2fs_inode_chksum(sbi, ipage);
+ f2fs_put_page(ipage, true);
+ return 0;
+}
+
static bool sanity_check_compress_inode(struct inode *inode,
struct f2fs_inode *ri)
{
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 374c990810ead..64adaec4e98e0 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1361,7 +1361,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
return -EINVAL;
}
- min_size = sizeof(struct f2fs_xattr_header) / sizeof(__le32);
+ min_size = MIN_INLINE_XATTR_SIZE;
max_size = MAX_INLINE_XATTR_SIZE;
if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
index 416d652774a33..b1811c392e6f1 100644
--- a/fs/f2fs/xattr.h
+++ b/fs/f2fs/xattr.h
@@ -83,6 +83,7 @@ struct f2fs_xattr_entry {
sizeof(struct f2fs_xattr_header) - \
sizeof(struct f2fs_xattr_entry))
+#define MIN_INLINE_XATTR_SIZE (sizeof(struct f2fs_xattr_header) / sizeof(__le32))
#define MAX_INLINE_XATTR_SIZE \
(DEF_ADDRS_PER_INODE - \
F2FS_TOTAL_EXTRA_ATTR_SIZE / sizeof(__le32) - \
diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
index 955d440be1046..a8fdaa22c7bda 100644
--- a/include/uapi/linux/f2fs.h
+++ b/include/uapi/linux/f2fs.h
@@ -44,6 +44,11 @@
#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.
* Flags for going down operation used by FS_IOC_GOINGDOWN
@@ -96,4 +101,34 @@ struct f2fs_comp_option {
__u8 log_cluster_size;
};
+struct f2fs_comp_option_v2 {
+ __u8 algorithm;
+ __u8 log_cluster_size;
+ __u8 level;
+ __u8 flag;
+};
+
+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,
+ * i_compress_flag
+ */
+ 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
This patch adds get_attr and set_attr to access inode's extra
attributes.
Signed-off-by: Sheng Yong <[email protected]>
---
tools/f2fs_io/f2fs_io.c | 165 ++++++++++++++++++++++++++++++++++++++++
tools/f2fs_io/f2fs_io.h | 34 +++++++++
2 files changed, 199 insertions(+)
diff --git a/tools/f2fs_io/f2fs_io.c b/tools/f2fs_io/f2fs_io.c
index 5bc0baf..70e0347 100644
--- a/tools/f2fs_io/f2fs_io.c
+++ b/tools/f2fs_io/f2fs_io.c
@@ -1311,6 +1311,169 @@ static void do_gc(int argc, char **argv, const struct cmd_desc *cmd)
exit(0);
}
+#define get_attr_desc "get inode extra attribute"
+#define get_attr_help "f2fs_io get_attr [field] [file_path]\n\n" \
+"field can be\n" \
+" total_size\n" \
+" isize\n" \
+" inline_xattr_size\n" \
+" projid\n" \
+" inode_chksum\n" \
+" crtime\n" \
+" cblocks\n" \
+" coption\n"
+
+static const char *extra_attr_fields[] = {
+ [F2FS_EXTRA_ATTR_TOTAL_SIZE] = "total_size",
+ [F2FS_EXTRA_ATTR_ISIZE] = "isize",
+ [F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE] = "inline_xattr_size",
+ [F2FS_EXTRA_ATTR_PROJID] = "projid",
+ [F2FS_EXTRA_ATTR_INODE_CHKSUM] = "inode_chksum",
+ [F2FS_EXTRA_ATTR_CRTIME] = "crtime",
+ [F2FS_EXTRA_ATTR_COMPR_BLOCKS] = "cblocks",
+ [F2FS_EXTRA_ATTR_COMPR_OPTION] = "coption",
+};
+
+static void do_get_attr(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ struct f2fs_extra_attr attr = {0};
+ struct timespec ts = {0};
+ struct f2fs_comp_option_v2 coption = {0};
+ int ret, fd;
+
+ if (argc != 3) {
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ for (attr.field = 0; attr.field < F2FS_EXTRA_ATTR_MAX; attr.field++) {
+ if (!strcmp(extra_attr_fields[attr.field], argv[1]))
+ break;
+ }
+
+ switch (attr.field) {
+ case F2FS_EXTRA_ATTR_TOTAL_SIZE:
+ case F2FS_EXTRA_ATTR_ISIZE:
+ case F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE:
+ case F2FS_EXTRA_ATTR_PROJID:
+ case F2FS_EXTRA_ATTR_INODE_CHKSUM:
+ case F2FS_EXTRA_ATTR_COMPR_BLOCKS:
+ attr.attr_size = sizeof(attr.attr);
+ break;
+ case F2FS_EXTRA_ATTR_CRTIME:
+ attr.attr_size = sizeof(ts);
+ attr.attr = (unsigned long)&ts;
+ break;
+ case F2FS_EXTRA_ATTR_COMPR_OPTION:
+ attr.attr_size = sizeof(coption);
+ attr.attr = (unsigned long)&coption;
+ break;
+ default:
+ die("Unknown field");
+ }
+
+ fd = xopen(argv[2], O_RDONLY, 0);
+
+ ret = ioctl(fd, F2FS_IOC_GET_EXTRA_ATTR, &attr);
+ if (ret < 0)
+ die_errno("F2FS_IOC_GET_EXTRA_ATTR failed");
+
+ switch (attr.field) {
+ case F2FS_EXTRA_ATTR_TOTAL_SIZE:
+ case F2FS_EXTRA_ATTR_ISIZE:
+ case F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE:
+ case F2FS_EXTRA_ATTR_PROJID:
+ case F2FS_EXTRA_ATTR_COMPR_BLOCKS:
+ printf("%"PRIu64"\n", attr.attr);
+ break;
+ case F2FS_EXTRA_ATTR_INODE_CHKSUM:
+ printf("0x%"PRIx64"\n", attr.attr);
+ break;
+ case F2FS_EXTRA_ATTR_CRTIME:
+ printf("sec: %lu\nnsec: %lu\n", ts.tv_sec, ts.tv_nsec);
+ break;
+ case F2FS_EXTRA_ATTR_COMPR_OPTION:
+ printf("compression algorithm:%u\n", coption.algorithm);
+ printf("compression cluster log size:%u\n", coption.log_cluster_size);
+ printf("compression level:%u\n", coption.level);
+ printf("compression flag:0x%x\n", coption.flag);
+ break;
+ default:
+ die("Unknown field");
+ }
+
+ exit(0);
+}
+
+#define set_attr_desc "set inode extra attribute"
+#define set_attr_help "f2fs_io set_attr [field] [values] [file_path]\n\n" \
+"field can be\n" \
+" inline_xattr_size : [values] is [size]\n" \
+" coption : [values] are [algorithm] [log_cluster_size] [level] [flag]\n" \
+" algorithm : compression algorithm (0:lzo, 1: lz4, 2:zstd, 3:lzorle)\n" \
+" log_cluster_size : compression cluster log size (2 <= log_size <= 8)\n" \
+" level : compression level\n" \
+" flag : compression flag (1:chksum)\n"
+
+static void do_set_attr(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ struct f2fs_extra_attr attr = {0};
+ struct f2fs_comp_option_v2 coption = {0};
+ int i;
+ int ret, fd;
+
+ if (argc < 4)
+ goto out;
+
+ if (!strcmp(argv[1], extra_attr_fields[F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE])) {
+ if (argc != 4)
+ goto out;
+ i = 2;
+ attr.field = F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE;
+ attr.attr_size = sizeof(attr.attr);
+ attr.attr = atoi(argv[i++]);
+ } else if (!strcmp(argv[1], extra_attr_fields[F2FS_EXTRA_ATTR_COMPR_OPTION])) {
+ if (argc != 7)
+ goto out;
+ i = 2;
+ coption.algorithm = atoi(argv[i++]);
+ coption.log_cluster_size = atoi(argv[i++]);
+ coption.level = atoi(argv[i++]);
+ coption.flag = atoi(argv[i++]);
+ attr.field = F2FS_EXTRA_ATTR_COMPR_OPTION;
+ attr.attr_size = sizeof(coption);
+ attr.attr = (unsigned long)&coption;
+ } else {
+ die("Unknown or read only field");
+ }
+
+ fd = xopen(argv[i], O_WRONLY, 0);
+
+ ret = ioctl(fd, F2FS_IOC_SET_EXTRA_ATTR, &attr);
+ if (ret < 0)
+ die_errno("F2FS_IOC_SET_EXTRA_ATTR failed");
+
+ switch (attr.field) {
+ case F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE:
+ printf("set inline_xattr_size:%"PRIu64"\n", attr.attr);
+ break;
+ case F2FS_EXTRA_ATTR_COMPR_OPTION:
+ printf("set compression option: algorithm=%u, "
+ "log_cluster_size=%u, level=%u, flag=0x%x\n",
+ coption.algorithm, coption.log_cluster_size,
+ coption.level, coption.flag);
+ break;
+ }
+
+ exit(0);
+out:
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+}
+
+
#define CMD_HIDDEN 0x0001
#define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 }
#define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN }
@@ -1343,6 +1506,8 @@ const struct cmd_desc cmd_list[] = {
CMD(get_filename_encrypt_mode),
CMD(rename),
CMD(gc),
+ CMD(get_attr),
+ CMD(set_attr),
{ NULL, NULL, NULL, NULL, 0 }
};
diff --git a/tools/f2fs_io/f2fs_io.h b/tools/f2fs_io/f2fs_io.h
index 58be8f8..b0be15c 100644
--- a/tools/f2fs_io/f2fs_io.h
+++ b/tools/f2fs_io/f2fs_io.h
@@ -91,6 +91,10 @@ typedef u32 __be32;
#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)
#ifndef FSCRYPT_POLICY_V1
#define FSCRYPT_POLICY_V1 0
@@ -216,3 +220,33 @@ struct f2fs_comp_option {
u8 algorithm;
u8 log_cluster_size;
};
+
+struct f2fs_comp_option_v2 {
+ u8 algorithm;
+ u8 log_cluster_size;
+ u8 level;
+ u8 flag;
+};
+
+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, /* r2, 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,
+ * i_compress_flag
+ */
+ 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 */
+};
--
2.40.1
Hi Sheng,
kernel test robot noticed the following build errors:
[auto build test ERROR on jaegeuk-f2fs/dev-test]
[also build test ERROR on jaegeuk-f2fs/dev next-20230525]
[cannot apply to linus/master v6.4-rc4]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sheng-Yong/f2fs-add-f2fs_ioc_-get-set-_extra_attr/20230529-093611
base: https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git dev-test
patch link: https://lore.kernel.org/r/20230529013502.2230810-1-shengyong%40oppo.com
patch subject: [PATCH] f2fs: add f2fs_ioc_[get|set]_extra_attr
config: i386-randconfig-i086-20230529 (https://download.01.org/0day-ci/archive/20230529/[email protected]/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build):
# https://github.com/intel-lab-lkp/linux/commit/519a8b3bbd4d743ae67c32dfef61e8bfa0951cc5
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Sheng-Yong/f2fs-add-f2fs_ioc_-get-set-_extra_attr/20230529-093611
git checkout 519a8b3bbd4d743ae67c32dfef61e8bfa0951cc5
# save the config file
mkdir build_dir && cp config build_dir/.config
make W=1 O=build_dir ARCH=i386 olddefconfig
make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
ld: fs/f2fs/file.o: in function `f2fs_set_compress_option_v2':
>> fs/f2fs/file.c:3996: undefined reference to `zstd_max_clevel'
vim +3996 fs/f2fs/file.c
3957
3958 static int f2fs_set_compress_option_v2(struct file *filp,
3959 unsigned long attr, __u16 *attr_size)
3960 {
3961 struct inode *inode = file_inode(filp);
3962 struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
3963 struct f2fs_comp_option_v2 option;
3964 int ret = 0;
3965
3966 if (sizeof(option) < *attr_size)
3967 *attr_size = sizeof(option);
3968
3969 if (!f2fs_sb_has_compression(sbi))
3970 return -EOPNOTSUPP;
3971
3972 if (!(filp->f_mode & FMODE_WRITE))
3973 return -EBADF;
3974
3975 if (copy_from_user(&option, (void __user *)attr, *attr_size))
3976 return -EFAULT;
3977
3978 if (!f2fs_compressed_file(inode) ||
3979 option.log_cluster_size < MIN_COMPRESS_LOG_SIZE ||
3980 option.log_cluster_size > MAX_COMPRESS_LOG_SIZE ||
3981 option.algorithm >= COMPRESS_MAX)
3982 return -EINVAL;
3983
3984 if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
3985 if (option.level != 0) {
3986 switch (option.algorithm) {
3987 case COMPRESS_LZO:
3988 case COMPRESS_LZORLE:
3989 return -EINVAL;
3990 case COMPRESS_LZ4:
3991 if (option.level < LZ4HC_MIN_CLEVEL ||
3992 option.level > LZ4HC_MAX_CLEVEL)
3993 return -EINVAL;
3994 break;
3995 case COMPRESS_ZSTD:
> 3996 if (option.level > zstd_max_clevel())
3997 return -EINVAL;
3998 break;
3999 }
4000 }
4001
4002 if (option.flag > BIT(COMPRESS_MAX_FLAG) - 1)
4003 return -EINVAL;
4004 }
4005
4006 file_start_write(filp);
4007 inode_lock(inode);
4008
4009 if (f2fs_is_mmap_file(inode) || get_dirty_pages(inode)) {
4010 ret = -EBUSY;
4011 goto out;
4012 }
4013
4014 if (F2FS_HAS_BLOCKS(inode)) {
4015 ret = -EFBIG;
4016 goto out;
4017 }
4018
4019 F2FS_I(inode)->i_compress_algorithm = option.algorithm;
4020 F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size;
4021 F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size);
4022 if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
4023 F2FS_I(inode)->i_compress_level = option.level;
4024 F2FS_I(inode)->i_compress_flag = option.flag;
4025 }
4026 f2fs_mark_inode_dirty_sync(inode, true);
4027
4028 if (!f2fs_is_compress_backend_ready(inode))
4029 f2fs_warn(sbi, "compression algorithm is successfully set, "
4030 "but current kernel doesn't support this algorithm.");
4031 out:
4032 inode_unlock(inode);
4033 file_end_write(filp);
4034
4035 return ret;
4036 }
4037
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Sheng,
kernel test robot noticed the following build warnings:
[auto build test WARNING on jaegeuk-f2fs/dev-test]
[also build test WARNING on jaegeuk-f2fs/dev next-20230525]
[cannot apply to linus/master v6.4-rc4]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sheng-Yong/f2fs-add-f2fs_ioc_-get-set-_extra_attr/20230529-093611
base: https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git dev-test
patch link: https://lore.kernel.org/r/20230529013502.2230810-1-shengyong%40oppo.com
patch subject: [PATCH] f2fs: add f2fs_ioc_[get|set]_extra_attr
config: arc-buildonly-randconfig-r004-20230529 (https://download.01.org/0day-ci/archive/20230529/[email protected]/config)
compiler: arceb-elf-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
mkdir -p ~/bin
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/519a8b3bbd4d743ae67c32dfef61e8bfa0951cc5
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Sheng-Yong/f2fs-add-f2fs_ioc_-get-set-_extra_attr/20230529-093611
git checkout 519a8b3bbd4d743ae67c32dfef61e8bfa0951cc5
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 ~/bin/make.cross W=1 O=build_dir ARCH=arc olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 ~/bin/make.cross W=1 O=build_dir ARCH=arc SHELL=/bin/bash fs/f2fs/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
fs/f2fs/file.c: In function 'f2fs_ioc_get_extra_attr':
>> fs/f2fs/file.c:4312:44: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
4312 | (void __user *)attr.attr))
| ^
fs/f2fs/file.c:4316:48: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
4316 | (void __user *)attr.attr))
| ^
vim +4312 fs/f2fs/file.c
4262
4263 static int f2fs_ioc_get_extra_attr(struct file *filp, unsigned long arg)
4264 {
4265 struct inode *inode = file_inode(filp);
4266 struct f2fs_inode_info *fi = F2FS_I(inode);
4267 struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
4268 struct f2fs_extra_attr attr;
4269 u32 chksum;
4270 int ret = 0;
4271
4272 if (!f2fs_has_extra_attr(inode))
4273 return -EOPNOTSUPP;
4274
4275 if (copy_from_user(&attr, (void __user *)arg, sizeof(attr)))
4276 return -EFAULT;
4277
4278 if (attr.field >= F2FS_EXTRA_ATTR_MAX)
4279 return -EINVAL;
4280
4281 if (!extra_attr_fits_in_inode(inode, attr.field))
4282 return -EOPNOTSUPP;
4283
4284 switch (attr.field) {
4285 case F2FS_EXTRA_ATTR_TOTAL_SIZE:
4286 attr.attr = F2FS_TOTAL_EXTRA_ATTR_SIZE;
4287 break;
4288 case F2FS_EXTRA_ATTR_ISIZE:
4289 attr.attr = fi->i_extra_isize;
4290 break;
4291 case F2FS_EXTRA_ATTR_INLINE_XATTR_SIZE:
4292 if (!f2fs_has_inline_xattr(inode))
4293 return -EOPNOTSUPP;
4294 attr.attr = get_inline_xattr_addrs(inode);
4295 break;
4296 case F2FS_EXTRA_ATTR_PROJID:
4297 if (!f2fs_sb_has_project_quota(F2FS_I_SB(inode)))
4298 return -EOPNOTSUPP;
4299 attr.attr = from_kprojid(&init_user_ns, fi->i_projid);
4300 break;
4301 case F2FS_EXTRA_ATTR_INODE_CHKSUM:
4302 ret = f2fs_inode_chksum_get(sbi, inode, &chksum);
4303 if (ret)
4304 return ret;
4305 attr.attr = chksum;
4306 break;
4307 case F2FS_EXTRA_ATTR_CRTIME:
4308 if (!f2fs_sb_has_inode_crtime(sbi))
4309 return -EOPNOTSUPP;
4310 if (attr.attr_size == sizeof(struct timespec64)) {
4311 if (put_timespec64(&fi->i_crtime,
> 4312 (void __user *)attr.attr))
4313 return -EFAULT;
4314 } else if (attr.attr_size == sizeof(struct old_timespec32)) {
4315 if (put_old_timespec32(&fi->i_crtime,
4316 (void __user *)attr.attr))
4317 return -EFAULT;
4318 } else {
4319 return -EINVAL;
4320 }
4321 break;
4322 case F2FS_EXTRA_ATTR_COMPR_BLOCKS:
4323 ret = f2fs_get_compress_blocks(filp, attr.attr_size);
4324 attr.attr = ret;
4325 break;
4326 case F2FS_EXTRA_ATTR_COMPR_OPTION:
4327 ret = f2fs_get_compress_option_v2(filp, attr.attr,
4328 &attr.attr_size);
4329 break;
4330 default:
4331 return -EINVAL;
4332 }
4333
4334 if (ret < 0)
4335 return ret;
4336
4337 if (copy_to_user((void __user *)arg, &attr, sizeof(attr)))
4338 return -EFAULT;
4339
4340 return 0;
4341 }
4342
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Sheng,
kernel test robot noticed the following build errors:
[auto build test ERROR on jaegeuk-f2fs/dev-test]
[also build test ERROR on jaegeuk-f2fs/dev next-20230525]
[cannot apply to linus/master v6.4-rc4]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sheng-Yong/f2fs-add-f2fs_ioc_-get-set-_extra_attr/20230529-093611
base: https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git dev-test
patch link: https://lore.kernel.org/r/20230529013502.2230810-1-shengyong%40oppo.com
patch subject: [PATCH] f2fs: add f2fs_ioc_[get|set]_extra_attr
config: x86_64-randconfig-x093-20230529 (https://download.01.org/0day-ci/archive/20230529/[email protected]/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build):
# https://github.com/intel-lab-lkp/linux/commit/519a8b3bbd4d743ae67c32dfef61e8bfa0951cc5
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Sheng-Yong/f2fs-add-f2fs_ioc_-get-set-_extra_attr/20230529-093611
git checkout 519a8b3bbd4d743ae67c32dfef61e8bfa0951cc5
# save the config file
mkdir build_dir && cp config build_dir/.config
make W=1 O=build_dir ARCH=x86_64 olddefconfig
make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: "zstd_max_clevel" [fs/f2fs/f2fs.ko] undefined!
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
This patch introduces two ioctls:
* f2fs_ioc_get_extra_attr
* f2fs_ioc_set_extra_attr
to get or modify values in 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 <[email protected]>
---
v2:
* fix compiling error if CONFIG_F2FS_FS_ZSTD is disabled by adding a
helper f2fs_is_compress_level_valid()
* fix compiling warning for casting unsinged long long to pointer
---
fs/f2fs/compress.c | 34 +++++
fs/f2fs/f2fs.h | 4 +
fs/f2fs/file.c | 273 ++++++++++++++++++++++++++++++++++++--
fs/f2fs/inode.c | 21 +++
fs/f2fs/super.c | 2 +-
fs/f2fs/xattr.h | 1 +
include/uapi/linux/f2fs.h | 35 +++++
7 files changed, 357 insertions(+), 13 deletions(-)
diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 905b7c39a2b32..3a2c05c849b0a 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -55,6 +55,7 @@ struct f2fs_compress_ops {
int (*init_decompress_ctx)(struct decompress_io_ctx *dic);
void (*destroy_decompress_ctx)(struct decompress_io_ctx *dic);
int (*decompress_pages)(struct decompress_io_ctx *dic);
+ bool (*is_level_valid)(struct inode *inode, int level);
};
static unsigned int offset_in_cluster(struct compress_ctx *cc, pgoff_t index)
@@ -232,6 +233,7 @@ static const struct f2fs_compress_ops f2fs_lzo_ops = {
.destroy_compress_ctx = lzo_destroy_compress_ctx,
.compress_pages = lzo_compress_pages,
.decompress_pages = lzo_decompress_pages,
+ .is_level_valid = NULL,
};
#endif
@@ -308,11 +310,24 @@ static int lz4_decompress_pages(struct decompress_io_ctx *dic)
return 0;
}
+static bool lz4_is_level_valid(struct inode *inode, int level)
+{
+ if (F2FS_I(inode)->i_compress_level == 0)
+ /* lz4 */
+ return level == 0;
+
+ /* lz4hc */
+ if (level >= LZ4HC_MIN_CLEVEL && level <= LZ4HC_MAX_CLEVEL)
+ return true;
+ return false;
+}
+
static const struct f2fs_compress_ops f2fs_lz4_ops = {
.init_compress_ctx = lz4_init_compress_ctx,
.destroy_compress_ctx = lz4_destroy_compress_ctx,
.compress_pages = lz4_compress_pages,
.decompress_pages = lz4_decompress_pages,
+ .is_level_valid = lz4_is_level_valid,
};
#endif
@@ -477,6 +492,13 @@ static int zstd_decompress_pages(struct decompress_io_ctx *dic)
return 0;
}
+static bool zstd_is_level_valid(struct inode *inode, int level)
+{
+ if (level < zstd_min_clevel() || level > zstd_max_clevel())
+ return false;
+ return true;
+}
+
static const struct f2fs_compress_ops f2fs_zstd_ops = {
.init_compress_ctx = zstd_init_compress_ctx,
.destroy_compress_ctx = zstd_destroy_compress_ctx,
@@ -484,6 +506,7 @@ static const struct f2fs_compress_ops f2fs_zstd_ops = {
.init_decompress_ctx = zstd_init_decompress_ctx,
.destroy_decompress_ctx = zstd_destroy_decompress_ctx,
.decompress_pages = zstd_decompress_pages,
+ .is_level_valid = zstd_is_level_valid,
};
#endif
@@ -508,6 +531,7 @@ static const struct f2fs_compress_ops f2fs_lzorle_ops = {
.destroy_compress_ctx = lzo_destroy_compress_ctx,
.compress_pages = lzorle_compress_pages,
.decompress_pages = lzo_decompress_pages,
+ .is_level_valid = NULL,
};
#endif
#endif
@@ -542,6 +566,16 @@ bool f2fs_is_compress_backend_ready(struct inode *inode)
return f2fs_cops[F2FS_I(inode)->i_compress_algorithm];
}
+bool f2fs_is_compress_level_valid(struct inode *inode, int alg, int lvl)
+{
+ const struct f2fs_compress_ops *cops = f2fs_cops[alg];
+
+ if (cops->is_level_valid)
+ return cops->is_level_valid(inode, lvl);
+
+ return lvl == 0;
+}
+
static mempool_t *compress_page_pool;
static int num_compress_pages = 512;
module_param(num_compress_pages, uint, 0444);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index faa27f41f39d4..b92b96eb133cb 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3475,6 +3475,8 @@ int f2fs_pin_file_control(struct inode *inode, bool inc);
void f2fs_set_inode_flags(struct inode *inode);
bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page);
void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page);
+int f2fs_inode_chksum_get(struct f2fs_sb_info *sbi, struct inode *inode,
+ u32 *chksum);
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino);
struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino);
int f2fs_try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink);
@@ -4232,6 +4234,7 @@ bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock);
void f2fs_compress_write_end_io(struct bio *bio, struct page *page);
bool f2fs_is_compress_backend_ready(struct inode *inode);
+bool f2fs_is_compress_level_valid(struct inode *inode, int alg, int lvl);
int __init f2fs_init_compress_mempool(void);
void f2fs_destroy_compress_mempool(void);
void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task);
@@ -4296,6 +4299,7 @@ static inline bool f2fs_is_compress_backend_ready(struct inode *inode)
/* not support compression */
return false;
}
+static inline bool f2fs_is_compress_level_valid(struct inode *inode, int alg, int lvl) { return false; }
static inline struct page *f2fs_compress_control_page(struct page *page)
{
WARN_ON_ONCE(1);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 78aa8cff4b41d..3de64ce8dd491 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -3376,10 +3376,12 @@ static int f2fs_ioc_setfslabel(struct file *filp, unsigned long arg)
return err;
}
-static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
+static int f2fs_get_compress_blocks(struct file *filp, unsigned int attr_size)
{
struct inode *inode = file_inode(filp);
- __u64 blocks;
+
+ if (attr_size != sizeof(__u64))
+ return -EINVAL;
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
@@ -3387,7 +3389,14 @@ static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
if (!f2fs_compressed_file(inode))
return -EINVAL;
- blocks = atomic_read(&F2FS_I(inode)->i_compr_blocks);
+ return atomic_read(&F2FS_I(inode)->i_compr_blocks);
+}
+
+static int f2fs_ioc_get_compress_blocks(struct file *filp, unsigned long arg)
+{
+ __u64 blocks;
+
+ blocks = f2fs_get_compress_blocks(filp, sizeof(blocks));
return put_user(blocks, (u64 __user *)arg);
}
@@ -3905,10 +3914,14 @@ static int f2fs_sec_trim_file(struct file *filp, unsigned long arg)
return ret;
}
-static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
+static int f2fs_get_compress_option_v2(struct file *filp,
+ unsigned long attr, __u16 *attr_size)
{
struct inode *inode = file_inode(filp);
- struct f2fs_comp_option option;
+ struct f2fs_comp_option_v2 option;
+
+ if (sizeof(option) < *attr_size)
+ *attr_size = sizeof(option);
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
@@ -3922,31 +3935,42 @@ static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
option.algorithm = F2FS_I(inode)->i_compress_algorithm;
option.log_cluster_size = F2FS_I(inode)->i_log_cluster_size;
+ option.level = F2FS_I(inode)->i_compress_level;
+ option.flag = F2FS_I(inode)->i_compress_flag;
inode_unlock_shared(inode);
- if (copy_to_user((struct f2fs_comp_option __user *)arg, &option,
- sizeof(option)))
+ if (copy_to_user((void __user *)attr, &option, *attr_size))
return -EFAULT;
return 0;
}
-static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
+static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
+{
+ __u16 size = sizeof(struct f2fs_comp_option);
+
+ return f2fs_get_compress_option_v2(filp, arg, &size);
+}
+
+static int f2fs_set_compress_option_v2(struct file *filp,
+ unsigned long attr, __u16 *attr_size)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct f2fs_comp_option option;
+ struct f2fs_comp_option_v2 option;
int ret = 0;
+ if (sizeof(option) < *attr_size)
+ *attr_size = sizeof(option);
+
if (!f2fs_sb_has_compression(sbi))
return -EOPNOTSUPP;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
- if (copy_from_user(&option, (struct f2fs_comp_option __user *)arg,
- sizeof(option)))
+ if (copy_from_user(&option, (void __user *)attr, *attr_size))
return -EFAULT;
if (!f2fs_compressed_file(inode) ||
@@ -3955,6 +3979,14 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
option.algorithm >= COMPRESS_MAX)
return -EINVAL;
+ if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
+ if (!f2fs_is_compress_level_valid(inode, option.algorithm,
+ option.level))
+ return -EINVAL;
+ if (option.flag > BIT(COMPRESS_MAX_FLAG) - 1)
+ return -EINVAL;
+ }
+
file_start_write(filp);
inode_lock(inode);
@@ -3971,6 +4003,10 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
F2FS_I(inode)->i_compress_algorithm = option.algorithm;
F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size;
F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size);
+ if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
+ F2FS_I(inode)->i_compress_level = option.level;
+ F2FS_I(inode)->i_compress_flag = option.flag;
+ }
f2fs_mark_inode_dirty_sync(inode, true);
if (!f2fs_is_compress_backend_ready(inode))
@@ -3983,6 +4019,13 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
return ret;
}
+static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
+{
+ __u16 size = sizeof(struct f2fs_comp_option);
+
+ return f2fs_set_compress_option_v2(filp, arg, &size);
+}
+
static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
{
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, page_idx);
@@ -4168,6 +4211,208 @@ 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:
+ ret = f2fs_get_compress_blocks(filp, attr.attr_size);
+ attr.attr = ret;
+ break;
+ case F2FS_EXTRA_ATTR_COMPR_OPTION:
+ ret = f2fs_get_compress_option_v2(filp, attr.attr,
+ &attr.attr_size);
+ 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_set_compress_option_v2(filp, attr.attr,
+ &attr.attr_size);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
@@ -4239,7 +4484,7 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case FS_IOC_SETFSLABEL:
return f2fs_ioc_setfslabel(filp, arg);
case F2FS_IOC_GET_COMPRESS_BLOCKS:
- return f2fs_get_compress_blocks(filp, arg);
+ return f2fs_ioc_get_compress_blocks(filp, arg);
case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
return f2fs_release_compress_blocks(filp, arg);
case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
@@ -4254,6 +4499,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/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 0a17484443299..aef9c1fd37dca 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -204,6 +204,27 @@ void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page)
ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page));
}
+int f2fs_inode_chksum_get(struct f2fs_sb_info *sbi,
+ struct inode *inode, u32 *chksum)
+{
+ struct page *ipage;
+ struct f2fs_inode_info *fi = F2FS_I(inode);
+ struct f2fs_inode *ri;
+
+ if (!f2fs_sb_has_inode_chksum(sbi) ||
+ !f2fs_has_extra_attr(inode) ||
+ !F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_inode_checksum))
+ return -EOPNOTSUPP;
+
+ ipage = f2fs_get_node_page(sbi, inode->i_ino);
+ if (IS_ERR(ipage))
+ return PTR_ERR(ipage);
+
+ *chksum = f2fs_inode_chksum(sbi, ipage);
+ f2fs_put_page(ipage, true);
+ return 0;
+}
+
static bool sanity_check_compress_inode(struct inode *inode,
struct f2fs_inode *ri)
{
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 374c990810ead..64adaec4e98e0 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1361,7 +1361,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
return -EINVAL;
}
- min_size = sizeof(struct f2fs_xattr_header) / sizeof(__le32);
+ min_size = MIN_INLINE_XATTR_SIZE;
max_size = MAX_INLINE_XATTR_SIZE;
if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
index 416d652774a33..b1811c392e6f1 100644
--- a/fs/f2fs/xattr.h
+++ b/fs/f2fs/xattr.h
@@ -83,6 +83,7 @@ struct f2fs_xattr_entry {
sizeof(struct f2fs_xattr_header) - \
sizeof(struct f2fs_xattr_entry))
+#define MIN_INLINE_XATTR_SIZE (sizeof(struct f2fs_xattr_header) / sizeof(__le32))
#define MAX_INLINE_XATTR_SIZE \
(DEF_ADDRS_PER_INODE - \
F2FS_TOTAL_EXTRA_ATTR_SIZE / sizeof(__le32) - \
diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
index 955d440be1046..a8fdaa22c7bda 100644
--- a/include/uapi/linux/f2fs.h
+++ b/include/uapi/linux/f2fs.h
@@ -44,6 +44,11 @@
#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.
* Flags for going down operation used by FS_IOC_GOINGDOWN
@@ -96,4 +101,34 @@ struct f2fs_comp_option {
__u8 log_cluster_size;
};
+struct f2fs_comp_option_v2 {
+ __u8 algorithm;
+ __u8 log_cluster_size;
+ __u8 level;
+ __u8 flag;
+};
+
+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,
+ * i_compress_flag
+ */
+ 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
On 2023/5/30 14:34, Sheng Yong wrote:
> This patch introduces two ioctls:
> * f2fs_ioc_get_extra_attr
> * f2fs_ioc_set_extra_attr
> to get or modify values in 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 <[email protected]>
Reviewed-by: Chao Yu <[email protected]>
Thanks,
On 2023/5/29 9:35, Sheng Yong wrote:
> This patch adds get_attr and set_attr to access inode's extra
> attributes.
It needs to update f2fs_io(8) manual as well?
Thanks,
On Mon, May 29, 2023 at 09:35:00AM +0800, Sheng Yong via Linux-f2fs-devel wrote:
> This patch introduces two ioctls:
> * f2fs_ioc_get_extra_attr
> * f2fs_ioc_set_extra_attr
> to get or modify values in 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 <[email protected]>
Aren't there enough things called extra or extended attributes already? Besides
the standard "extended attributes" retrievable with the getxattr() system call,
there is already the FS_IOC_FSGETXATTR ioctl too.
- Eric
On 2023/6/1 11:16, Eric Biggers wrote:
> On Mon, May 29, 2023 at 09:35:00AM +0800, Sheng Yong via Linux-f2fs-devel wrote:
>> This patch introduces two ioctls:
>> * f2fs_ioc_get_extra_attr
>> * f2fs_ioc_set_extra_attr
>> to get or modify values in 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 <[email protected]>
>
> Aren't there enough things called extra or extended attributes already? Besides
> the standard "extended attributes" retrievable with the getxattr() system call,
> there is already the FS_IOC_FSGETXATTR ioctl too.
Hi, Eric,
The name extra_attr is a bit confusing :-(
But f2fs usually extends new features through extra_attr, like compression, inode
chksum. Since this area is easy to extend, new features could also be added here
in the future. To avoid adding new ioctls for new features, these two could help
integrate with all extra_attr related ioctls. And it seems not appropriate to add
new fields or get/set f2fs-only attributes by FS_IOC_FSGETXATTR/FS_IOC_FSGETXATTR.
xattrs could not be accessed through these two, only inline xattr size is allowed
to be modified for an empty file.
thanks,
shengyong
>
> - Eric
This patch introduces two ioctls:
* f2fs_ioc_get_extra_attr
* f2fs_ioc_set_extra_attr
to get or modify values in 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 <[email protected]>
---
v3:
* setting lz4(hc) level correctly
v2:
* fix compiling error if CONFIG_F2FS_FS_ZSTD is disabled by adding a
helper f2fs_is_compress_level_valid()
* fix compiling warning for casting unsinged long long to pointer
---
fs/f2fs/compress.c | 33 +++++
fs/f2fs/f2fs.h | 4 +
fs/f2fs/file.c | 273 ++++++++++++++++++++++++++++++++++++--
fs/f2fs/inode.c | 21 +++
fs/f2fs/super.c | 2 +-
fs/f2fs/xattr.h | 1 +
include/uapi/linux/f2fs.h | 35 +++++
7 files changed, 356 insertions(+), 13 deletions(-)
diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 905b7c39a2b32..3fd804277059d 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -55,6 +55,7 @@ struct f2fs_compress_ops {
int (*init_decompress_ctx)(struct decompress_io_ctx *dic);
void (*destroy_decompress_ctx)(struct decompress_io_ctx *dic);
int (*decompress_pages)(struct decompress_io_ctx *dic);
+ bool (*is_level_valid)(int level);
};
static unsigned int offset_in_cluster(struct compress_ctx *cc, pgoff_t index)
@@ -232,6 +233,7 @@ static const struct f2fs_compress_ops f2fs_lzo_ops = {
.destroy_compress_ctx = lzo_destroy_compress_ctx,
.compress_pages = lzo_compress_pages,
.decompress_pages = lzo_decompress_pages,
+ .is_level_valid = NULL,
};
#endif
@@ -308,11 +310,23 @@ static int lz4_decompress_pages(struct decompress_io_ctx *dic)
return 0;
}
+static bool lz4_is_level_valid(int level)
+{
+ if (level == 0)
+ return true;
+#ifdef CONFIG_F2FS_FS_LZ4HC
+ if (level >= LZ4HC_MIN_CLEVEL && level <= LZ4HC_MAX_CLEVEL)
+ return true;
+#endif
+ return false;
+}
+
static const struct f2fs_compress_ops f2fs_lz4_ops = {
.init_compress_ctx = lz4_init_compress_ctx,
.destroy_compress_ctx = lz4_destroy_compress_ctx,
.compress_pages = lz4_compress_pages,
.decompress_pages = lz4_decompress_pages,
+ .is_level_valid = lz4_is_level_valid,
};
#endif
@@ -477,6 +491,13 @@ static int zstd_decompress_pages(struct decompress_io_ctx *dic)
return 0;
}
+static bool zstd_is_level_valid(int level)
+{
+ if (level < zstd_min_clevel() || level > zstd_max_clevel())
+ return false;
+ return true;
+}
+
static const struct f2fs_compress_ops f2fs_zstd_ops = {
.init_compress_ctx = zstd_init_compress_ctx,
.destroy_compress_ctx = zstd_destroy_compress_ctx,
@@ -484,6 +505,7 @@ static const struct f2fs_compress_ops f2fs_zstd_ops = {
.init_decompress_ctx = zstd_init_decompress_ctx,
.destroy_decompress_ctx = zstd_destroy_decompress_ctx,
.decompress_pages = zstd_decompress_pages,
+ .is_level_valid = zstd_is_level_valid,
};
#endif
@@ -508,6 +530,7 @@ static const struct f2fs_compress_ops f2fs_lzorle_ops = {
.destroy_compress_ctx = lzo_destroy_compress_ctx,
.compress_pages = lzorle_compress_pages,
.decompress_pages = lzo_decompress_pages,
+ .is_level_valid = NULL,
};
#endif
#endif
@@ -542,6 +565,16 @@ bool f2fs_is_compress_backend_ready(struct inode *inode)
return f2fs_cops[F2FS_I(inode)->i_compress_algorithm];
}
+bool f2fs_is_compress_level_valid(int alg, int lvl)
+{
+ const struct f2fs_compress_ops *cops = f2fs_cops[alg];
+
+ if (cops->is_level_valid)
+ return cops->is_level_valid(lvl);
+
+ return lvl == 0;
+}
+
static mempool_t *compress_page_pool;
static int num_compress_pages = 512;
module_param(num_compress_pages, uint, 0444);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index faa27f41f39d4..9fcf8f66c860c 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3475,6 +3475,8 @@ int f2fs_pin_file_control(struct inode *inode, bool inc);
void f2fs_set_inode_flags(struct inode *inode);
bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page);
void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page);
+int f2fs_inode_chksum_get(struct f2fs_sb_info *sbi, struct inode *inode,
+ u32 *chksum);
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino);
struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino);
int f2fs_try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink);
@@ -4232,6 +4234,7 @@ bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock);
void f2fs_compress_write_end_io(struct bio *bio, struct page *page);
bool f2fs_is_compress_backend_ready(struct inode *inode);
+bool f2fs_is_compress_level_valid(int alg, int lvl);
int __init f2fs_init_compress_mempool(void);
void f2fs_destroy_compress_mempool(void);
void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task);
@@ -4296,6 +4299,7 @@ static inline bool f2fs_is_compress_backend_ready(struct inode *inode)
/* not support compression */
return false;
}
+static inline bool f2fs_is_compress_level_valid(int alg, int lvl) { return false; }
static inline struct page *f2fs_compress_control_page(struct page *page)
{
WARN_ON_ONCE(1);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 78aa8cff4b41d..353a2edacc549 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -3376,10 +3376,12 @@ static int f2fs_ioc_setfslabel(struct file *filp, unsigned long arg)
return err;
}
-static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
+static int f2fs_get_compress_blocks(struct file *filp, unsigned int attr_size)
{
struct inode *inode = file_inode(filp);
- __u64 blocks;
+
+ if (attr_size != sizeof(__u64))
+ return -EINVAL;
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
@@ -3387,7 +3389,14 @@ static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
if (!f2fs_compressed_file(inode))
return -EINVAL;
- blocks = atomic_read(&F2FS_I(inode)->i_compr_blocks);
+ return atomic_read(&F2FS_I(inode)->i_compr_blocks);
+}
+
+static int f2fs_ioc_get_compress_blocks(struct file *filp, unsigned long arg)
+{
+ __u64 blocks;
+
+ blocks = f2fs_get_compress_blocks(filp, sizeof(blocks));
return put_user(blocks, (u64 __user *)arg);
}
@@ -3905,10 +3914,14 @@ static int f2fs_sec_trim_file(struct file *filp, unsigned long arg)
return ret;
}
-static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
+static int f2fs_get_compress_option_v2(struct file *filp,
+ unsigned long attr, __u16 *attr_size)
{
struct inode *inode = file_inode(filp);
- struct f2fs_comp_option option;
+ struct f2fs_comp_option_v2 option;
+
+ if (sizeof(option) < *attr_size)
+ *attr_size = sizeof(option);
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
@@ -3922,31 +3935,42 @@ static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
option.algorithm = F2FS_I(inode)->i_compress_algorithm;
option.log_cluster_size = F2FS_I(inode)->i_log_cluster_size;
+ option.level = F2FS_I(inode)->i_compress_level;
+ option.flag = F2FS_I(inode)->i_compress_flag;
inode_unlock_shared(inode);
- if (copy_to_user((struct f2fs_comp_option __user *)arg, &option,
- sizeof(option)))
+ if (copy_to_user((void __user *)attr, &option, *attr_size))
return -EFAULT;
return 0;
}
-static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
+static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
+{
+ __u16 size = sizeof(struct f2fs_comp_option);
+
+ return f2fs_get_compress_option_v2(filp, arg, &size);
+}
+
+static int f2fs_set_compress_option_v2(struct file *filp,
+ unsigned long attr, __u16 *attr_size)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct f2fs_comp_option option;
+ struct f2fs_comp_option_v2 option;
int ret = 0;
+ if (sizeof(option) < *attr_size)
+ *attr_size = sizeof(option);
+
if (!f2fs_sb_has_compression(sbi))
return -EOPNOTSUPP;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
- if (copy_from_user(&option, (struct f2fs_comp_option __user *)arg,
- sizeof(option)))
+ if (copy_from_user(&option, (void __user *)attr, *attr_size))
return -EFAULT;
if (!f2fs_compressed_file(inode) ||
@@ -3955,6 +3979,14 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
option.algorithm >= COMPRESS_MAX)
return -EINVAL;
+ if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
+ if (!f2fs_is_compress_level_valid(option.algorithm,
+ option.level))
+ return -EINVAL;
+ if (option.flag > BIT(COMPRESS_MAX_FLAG) - 1)
+ return -EINVAL;
+ }
+
file_start_write(filp);
inode_lock(inode);
@@ -3971,6 +4003,10 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
F2FS_I(inode)->i_compress_algorithm = option.algorithm;
F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size;
F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size);
+ if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
+ F2FS_I(inode)->i_compress_level = option.level;
+ F2FS_I(inode)->i_compress_flag = option.flag;
+ }
f2fs_mark_inode_dirty_sync(inode, true);
if (!f2fs_is_compress_backend_ready(inode))
@@ -3983,6 +4019,13 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
return ret;
}
+static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
+{
+ __u16 size = sizeof(struct f2fs_comp_option);
+
+ return f2fs_set_compress_option_v2(filp, arg, &size);
+}
+
static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
{
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, page_idx);
@@ -4168,6 +4211,208 @@ 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:
+ ret = f2fs_get_compress_blocks(filp, attr.attr_size);
+ attr.attr = ret;
+ break;
+ case F2FS_EXTRA_ATTR_COMPR_OPTION:
+ ret = f2fs_get_compress_option_v2(filp, attr.attr,
+ &attr.attr_size);
+ 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_set_compress_option_v2(filp, attr.attr,
+ &attr.attr_size);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
@@ -4239,7 +4484,7 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case FS_IOC_SETFSLABEL:
return f2fs_ioc_setfslabel(filp, arg);
case F2FS_IOC_GET_COMPRESS_BLOCKS:
- return f2fs_get_compress_blocks(filp, arg);
+ return f2fs_ioc_get_compress_blocks(filp, arg);
case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
return f2fs_release_compress_blocks(filp, arg);
case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
@@ -4254,6 +4499,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/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 0a17484443299..aef9c1fd37dca 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -204,6 +204,27 @@ void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page)
ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page));
}
+int f2fs_inode_chksum_get(struct f2fs_sb_info *sbi,
+ struct inode *inode, u32 *chksum)
+{
+ struct page *ipage;
+ struct f2fs_inode_info *fi = F2FS_I(inode);
+ struct f2fs_inode *ri;
+
+ if (!f2fs_sb_has_inode_chksum(sbi) ||
+ !f2fs_has_extra_attr(inode) ||
+ !F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_inode_checksum))
+ return -EOPNOTSUPP;
+
+ ipage = f2fs_get_node_page(sbi, inode->i_ino);
+ if (IS_ERR(ipage))
+ return PTR_ERR(ipage);
+
+ *chksum = f2fs_inode_chksum(sbi, ipage);
+ f2fs_put_page(ipage, true);
+ return 0;
+}
+
static bool sanity_check_compress_inode(struct inode *inode,
struct f2fs_inode *ri)
{
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 374c990810ead..64adaec4e98e0 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1361,7 +1361,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
return -EINVAL;
}
- min_size = sizeof(struct f2fs_xattr_header) / sizeof(__le32);
+ min_size = MIN_INLINE_XATTR_SIZE;
max_size = MAX_INLINE_XATTR_SIZE;
if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
index 416d652774a33..b1811c392e6f1 100644
--- a/fs/f2fs/xattr.h
+++ b/fs/f2fs/xattr.h
@@ -83,6 +83,7 @@ struct f2fs_xattr_entry {
sizeof(struct f2fs_xattr_header) - \
sizeof(struct f2fs_xattr_entry))
+#define MIN_INLINE_XATTR_SIZE (sizeof(struct f2fs_xattr_header) / sizeof(__le32))
#define MAX_INLINE_XATTR_SIZE \
(DEF_ADDRS_PER_INODE - \
F2FS_TOTAL_EXTRA_ATTR_SIZE / sizeof(__le32) - \
diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
index 955d440be1046..a8fdaa22c7bda 100644
--- a/include/uapi/linux/f2fs.h
+++ b/include/uapi/linux/f2fs.h
@@ -44,6 +44,11 @@
#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.
* Flags for going down operation used by FS_IOC_GOINGDOWN
@@ -96,4 +101,34 @@ struct f2fs_comp_option {
__u8 log_cluster_size;
};
+struct f2fs_comp_option_v2 {
+ __u8 algorithm;
+ __u8 log_cluster_size;
+ __u8 level;
+ __u8 flag;
+};
+
+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,
+ * i_compress_flag
+ */
+ 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
Could you please split the patches to have one topic each?
I do see
- is_level_valid
- f2fs_ioc_get_compress_blocks
- f2fs_get_compress_option_v2
- f2fs_ioc_get|set_extra_attr
Thanks,
On 06/04, Sheng Yong wrote:
> This patch introduces two ioctls:
> * f2fs_ioc_get_extra_attr
> * f2fs_ioc_set_extra_attr
> to get or modify values in 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 <[email protected]>
> ---
> v3:
> * setting lz4(hc) level correctly
> v2:
> * fix compiling error if CONFIG_F2FS_FS_ZSTD is disabled by adding a
> helper f2fs_is_compress_level_valid()
> * fix compiling warning for casting unsinged long long to pointer
>
> ---
> fs/f2fs/compress.c | 33 +++++
> fs/f2fs/f2fs.h | 4 +
> fs/f2fs/file.c | 273 ++++++++++++++++++++++++++++++++++++--
> fs/f2fs/inode.c | 21 +++
> fs/f2fs/super.c | 2 +-
> fs/f2fs/xattr.h | 1 +
> include/uapi/linux/f2fs.h | 35 +++++
> 7 files changed, 356 insertions(+), 13 deletions(-)
>
> diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
> index 905b7c39a2b32..3fd804277059d 100644
> --- a/fs/f2fs/compress.c
> +++ b/fs/f2fs/compress.c
> @@ -55,6 +55,7 @@ struct f2fs_compress_ops {
> int (*init_decompress_ctx)(struct decompress_io_ctx *dic);
> void (*destroy_decompress_ctx)(struct decompress_io_ctx *dic);
> int (*decompress_pages)(struct decompress_io_ctx *dic);
> + bool (*is_level_valid)(int level);
> };
>
> static unsigned int offset_in_cluster(struct compress_ctx *cc, pgoff_t index)
> @@ -232,6 +233,7 @@ static const struct f2fs_compress_ops f2fs_lzo_ops = {
> .destroy_compress_ctx = lzo_destroy_compress_ctx,
> .compress_pages = lzo_compress_pages,
> .decompress_pages = lzo_decompress_pages,
> + .is_level_valid = NULL,
> };
> #endif
>
> @@ -308,11 +310,23 @@ static int lz4_decompress_pages(struct decompress_io_ctx *dic)
> return 0;
> }
>
> +static bool lz4_is_level_valid(int level)
> +{
> + if (level == 0)
> + return true;
> +#ifdef CONFIG_F2FS_FS_LZ4HC
> + if (level >= LZ4HC_MIN_CLEVEL && level <= LZ4HC_MAX_CLEVEL)
> + return true;
> +#endif
> + return false;
> +}
> +
> static const struct f2fs_compress_ops f2fs_lz4_ops = {
> .init_compress_ctx = lz4_init_compress_ctx,
> .destroy_compress_ctx = lz4_destroy_compress_ctx,
> .compress_pages = lz4_compress_pages,
> .decompress_pages = lz4_decompress_pages,
> + .is_level_valid = lz4_is_level_valid,
> };
> #endif
>
> @@ -477,6 +491,13 @@ static int zstd_decompress_pages(struct decompress_io_ctx *dic)
> return 0;
> }
>
> +static bool zstd_is_level_valid(int level)
> +{
> + if (level < zstd_min_clevel() || level > zstd_max_clevel())
> + return false;
> + return true;
> +}
> +
> static const struct f2fs_compress_ops f2fs_zstd_ops = {
> .init_compress_ctx = zstd_init_compress_ctx,
> .destroy_compress_ctx = zstd_destroy_compress_ctx,
> @@ -484,6 +505,7 @@ static const struct f2fs_compress_ops f2fs_zstd_ops = {
> .init_decompress_ctx = zstd_init_decompress_ctx,
> .destroy_decompress_ctx = zstd_destroy_decompress_ctx,
> .decompress_pages = zstd_decompress_pages,
> + .is_level_valid = zstd_is_level_valid,
> };
> #endif
>
> @@ -508,6 +530,7 @@ static const struct f2fs_compress_ops f2fs_lzorle_ops = {
> .destroy_compress_ctx = lzo_destroy_compress_ctx,
> .compress_pages = lzorle_compress_pages,
> .decompress_pages = lzo_decompress_pages,
> + .is_level_valid = NULL,
> };
> #endif
> #endif
> @@ -542,6 +565,16 @@ bool f2fs_is_compress_backend_ready(struct inode *inode)
> return f2fs_cops[F2FS_I(inode)->i_compress_algorithm];
> }
>
> +bool f2fs_is_compress_level_valid(int alg, int lvl)
> +{
> + const struct f2fs_compress_ops *cops = f2fs_cops[alg];
> +
> + if (cops->is_level_valid)
> + return cops->is_level_valid(lvl);
> +
> + return lvl == 0;
> +}
> +
> static mempool_t *compress_page_pool;
> static int num_compress_pages = 512;
> module_param(num_compress_pages, uint, 0444);
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index faa27f41f39d4..9fcf8f66c860c 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -3475,6 +3475,8 @@ int f2fs_pin_file_control(struct inode *inode, bool inc);
> void f2fs_set_inode_flags(struct inode *inode);
> bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page);
> void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page);
> +int f2fs_inode_chksum_get(struct f2fs_sb_info *sbi, struct inode *inode,
> + u32 *chksum);
> struct inode *f2fs_iget(struct super_block *sb, unsigned long ino);
> struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino);
> int f2fs_try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink);
> @@ -4232,6 +4234,7 @@ bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
> int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock);
> void f2fs_compress_write_end_io(struct bio *bio, struct page *page);
> bool f2fs_is_compress_backend_ready(struct inode *inode);
> +bool f2fs_is_compress_level_valid(int alg, int lvl);
> int __init f2fs_init_compress_mempool(void);
> void f2fs_destroy_compress_mempool(void);
> void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task);
> @@ -4296,6 +4299,7 @@ static inline bool f2fs_is_compress_backend_ready(struct inode *inode)
> /* not support compression */
> return false;
> }
> +static inline bool f2fs_is_compress_level_valid(int alg, int lvl) { return false; }
> static inline struct page *f2fs_compress_control_page(struct page *page)
> {
> WARN_ON_ONCE(1);
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 78aa8cff4b41d..353a2edacc549 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -3376,10 +3376,12 @@ static int f2fs_ioc_setfslabel(struct file *filp, unsigned long arg)
> return err;
> }
>
> -static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
> +static int f2fs_get_compress_blocks(struct file *filp, unsigned int attr_size)
> {
> struct inode *inode = file_inode(filp);
> - __u64 blocks;
> +
> + if (attr_size != sizeof(__u64))
> + return -EINVAL;
>
> if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
> return -EOPNOTSUPP;
> @@ -3387,7 +3389,14 @@ static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
> if (!f2fs_compressed_file(inode))
> return -EINVAL;
>
> - blocks = atomic_read(&F2FS_I(inode)->i_compr_blocks);
> + return atomic_read(&F2FS_I(inode)->i_compr_blocks);
> +}
> +
> +static int f2fs_ioc_get_compress_blocks(struct file *filp, unsigned long arg)
> +{
> + __u64 blocks;
> +
> + blocks = f2fs_get_compress_blocks(filp, sizeof(blocks));
> return put_user(blocks, (u64 __user *)arg);
> }
>
> @@ -3905,10 +3914,14 @@ static int f2fs_sec_trim_file(struct file *filp, unsigned long arg)
> return ret;
> }
>
> -static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
> +static int f2fs_get_compress_option_v2(struct file *filp,
> + unsigned long attr, __u16 *attr_size)
> {
> struct inode *inode = file_inode(filp);
> - struct f2fs_comp_option option;
> + struct f2fs_comp_option_v2 option;
> +
> + if (sizeof(option) < *attr_size)
> + *attr_size = sizeof(option);
>
> if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
> return -EOPNOTSUPP;
> @@ -3922,31 +3935,42 @@ static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
>
> option.algorithm = F2FS_I(inode)->i_compress_algorithm;
> option.log_cluster_size = F2FS_I(inode)->i_log_cluster_size;
> + option.level = F2FS_I(inode)->i_compress_level;
> + option.flag = F2FS_I(inode)->i_compress_flag;
>
> inode_unlock_shared(inode);
>
> - if (copy_to_user((struct f2fs_comp_option __user *)arg, &option,
> - sizeof(option)))
> + if (copy_to_user((void __user *)attr, &option, *attr_size))
> return -EFAULT;
>
> return 0;
> }
>
> -static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
> +static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
> +{
> + __u16 size = sizeof(struct f2fs_comp_option);
> +
> + return f2fs_get_compress_option_v2(filp, arg, &size);
> +}
> +
> +static int f2fs_set_compress_option_v2(struct file *filp,
> + unsigned long attr, __u16 *attr_size)
> {
> struct inode *inode = file_inode(filp);
> struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> - struct f2fs_comp_option option;
> + struct f2fs_comp_option_v2 option;
> int ret = 0;
>
> + if (sizeof(option) < *attr_size)
> + *attr_size = sizeof(option);
> +
> if (!f2fs_sb_has_compression(sbi))
> return -EOPNOTSUPP;
>
> if (!(filp->f_mode & FMODE_WRITE))
> return -EBADF;
>
> - if (copy_from_user(&option, (struct f2fs_comp_option __user *)arg,
> - sizeof(option)))
> + if (copy_from_user(&option, (void __user *)attr, *attr_size))
> return -EFAULT;
>
> if (!f2fs_compressed_file(inode) ||
> @@ -3955,6 +3979,14 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
> option.algorithm >= COMPRESS_MAX)
> return -EINVAL;
>
> + if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
> + if (!f2fs_is_compress_level_valid(option.algorithm,
> + option.level))
> + return -EINVAL;
> + if (option.flag > BIT(COMPRESS_MAX_FLAG) - 1)
> + return -EINVAL;
> + }
> +
> file_start_write(filp);
> inode_lock(inode);
>
> @@ -3971,6 +4003,10 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
> F2FS_I(inode)->i_compress_algorithm = option.algorithm;
> F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size;
> F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size);
> + if (*attr_size == sizeof(struct f2fs_comp_option_v2)) {
> + F2FS_I(inode)->i_compress_level = option.level;
> + F2FS_I(inode)->i_compress_flag = option.flag;
> + }
> f2fs_mark_inode_dirty_sync(inode, true);
>
> if (!f2fs_is_compress_backend_ready(inode))
> @@ -3983,6 +4019,13 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
> return ret;
> }
>
> +static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
> +{
> + __u16 size = sizeof(struct f2fs_comp_option);
> +
> + return f2fs_set_compress_option_v2(filp, arg, &size);
> +}
> +
> static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
> {
> DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, page_idx);
> @@ -4168,6 +4211,208 @@ 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:
> + ret = f2fs_get_compress_blocks(filp, attr.attr_size);
> + attr.attr = ret;
> + break;
> + case F2FS_EXTRA_ATTR_COMPR_OPTION:
> + ret = f2fs_get_compress_option_v2(filp, attr.attr,
> + &attr.attr_size);
> + 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_set_compress_option_v2(filp, attr.attr,
> + &attr.attr_size);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> {
> switch (cmd) {
> @@ -4239,7 +4484,7 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> case FS_IOC_SETFSLABEL:
> return f2fs_ioc_setfslabel(filp, arg);
> case F2FS_IOC_GET_COMPRESS_BLOCKS:
> - return f2fs_get_compress_blocks(filp, arg);
> + return f2fs_ioc_get_compress_blocks(filp, arg);
> case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
> return f2fs_release_compress_blocks(filp, arg);
> case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
> @@ -4254,6 +4499,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/fs/f2fs/inode.c b/fs/f2fs/inode.c
> index 0a17484443299..aef9c1fd37dca 100644
> --- a/fs/f2fs/inode.c
> +++ b/fs/f2fs/inode.c
> @@ -204,6 +204,27 @@ void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page)
> ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page));
> }
>
> +int f2fs_inode_chksum_get(struct f2fs_sb_info *sbi,
> + struct inode *inode, u32 *chksum)
> +{
> + struct page *ipage;
> + struct f2fs_inode_info *fi = F2FS_I(inode);
> + struct f2fs_inode *ri;
> +
> + if (!f2fs_sb_has_inode_chksum(sbi) ||
> + !f2fs_has_extra_attr(inode) ||
> + !F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_inode_checksum))
> + return -EOPNOTSUPP;
> +
> + ipage = f2fs_get_node_page(sbi, inode->i_ino);
> + if (IS_ERR(ipage))
> + return PTR_ERR(ipage);
> +
> + *chksum = f2fs_inode_chksum(sbi, ipage);
> + f2fs_put_page(ipage, true);
> + return 0;
> +}
> +
> static bool sanity_check_compress_inode(struct inode *inode,
> struct f2fs_inode *ri)
> {
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index 374c990810ead..64adaec4e98e0 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -1361,7 +1361,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
> return -EINVAL;
> }
>
> - min_size = sizeof(struct f2fs_xattr_header) / sizeof(__le32);
> + min_size = MIN_INLINE_XATTR_SIZE;
> max_size = MAX_INLINE_XATTR_SIZE;
>
> if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
> diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
> index 416d652774a33..b1811c392e6f1 100644
> --- a/fs/f2fs/xattr.h
> +++ b/fs/f2fs/xattr.h
> @@ -83,6 +83,7 @@ struct f2fs_xattr_entry {
> sizeof(struct f2fs_xattr_header) - \
> sizeof(struct f2fs_xattr_entry))
>
> +#define MIN_INLINE_XATTR_SIZE (sizeof(struct f2fs_xattr_header) / sizeof(__le32))
> #define MAX_INLINE_XATTR_SIZE \
> (DEF_ADDRS_PER_INODE - \
> F2FS_TOTAL_EXTRA_ATTR_SIZE / sizeof(__le32) - \
> diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
> index 955d440be1046..a8fdaa22c7bda 100644
> --- a/include/uapi/linux/f2fs.h
> +++ b/include/uapi/linux/f2fs.h
> @@ -44,6 +44,11 @@
> #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.
> * Flags for going down operation used by FS_IOC_GOINGDOWN
> @@ -96,4 +101,34 @@ struct f2fs_comp_option {
> __u8 log_cluster_size;
> };
>
> +struct f2fs_comp_option_v2 {
> + __u8 algorithm;
> + __u8 log_cluster_size;
> + __u8 level;
> + __u8 flag;
> +};
> +
> +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,
> + * i_compress_flag
> + */
> + 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