aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2018-04-24 13:26:17 -0700
committerEric Biggers <ebiggers@google.com>2018-04-24 13:26:17 -0700
commite458c1aa464c4d2e58bb97770825db23532e13b6 (patch)
tree25d843ff9ecf50794edf065e2512972673dfba87
parent6d6ce528ef9d05b70a5313b6e32d3d330ece9309 (diff)
downloadlinux-fs-verity-dev.tar.gz
f2fs: fs-verity supportfs-verity-dev
Add fs-verity support to f2fs. fs-verity provides transparent authentication of read-only files in a manner similar to dm-verity, by using a Merkle tree (hash tree) hidden at the end of the file. The Merkle tree and other metadata is written by a userspace program, which then uses an ioctl to set the verity bit. The expected root hash can then be provided via another ioctl. Signature verification is also planned but shouldn't require filesystem-specific changes. In f2fs, the main change is to the I/O path: ->readpage() and ->readpages() now verify data as it is read from verity files. Pages that fail verification are set to PG_error && !PG_uptodate, causing applications to see an I/O error. An internal flag also allows the ->read_metadata_page() verity operation to skip verification, to avoid recursion into the fs-verity workqueue. Hooks are also added to several f2fs filesystem operations: * ->open(), to deny opening verity files for writing and to set up the fsverity_info to prepare for I/O * ->getattr() to set up the fsverity_info to make stat() show the original data size of verity files * ->setattr() to deny truncating verity files * update_inode() to write out the full file size rather than the original data size, since for verity files the in-memory ->i_size is overridden with the original data size. Finally, the fs-verity ioctls are wired up. On f2fs, enabling fs-verity on a file requires that the filesystem has the 'verity' feature, i.e. it was created with 'mkfs.f2fs -O verity'. Signed-off-by: Eric Biggers <ebiggers@google.com>
-rw-r--r--fs/f2fs/Kconfig10
-rw-r--r--fs/f2fs/data.c57
-rw-r--r--fs/f2fs/dir.c2
-rw-r--r--fs/f2fs/f2fs.h23
-rw-r--r--fs/f2fs/file.c55
-rw-r--r--fs/f2fs/gc.c2
-rw-r--r--fs/f2fs/inode.c3
-rw-r--r--fs/f2fs/super.c37
-rw-r--r--fs/f2fs/sysfs.c11
9 files changed, 179 insertions, 21 deletions
diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig
index 9a20ef42fadde4..3473ba96a0545a 100644
--- a/fs/f2fs/Kconfig
+++ b/fs/f2fs/Kconfig
@@ -81,6 +81,16 @@ config F2FS_FS_ENCRYPTION
efficient since it avoids caching the encrypted and
decrypted pages in the page cache.
+config F2FS_FS_VERITY
+ bool "F2FS Verity"
+ depends on F2FS_FS
+ select FS_VERITY
+ help
+ Enable file-based authentication of f2fs files. This
+ efficiently validates the randomly-accessed content of files
+ using an authenticated dictionary structure hidden at the
+ end of immutable files.
+
config F2FS_IO_TRACE
bool "F2FS IO tracer"
depends on F2FS_FS
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 5477fc09c3cdf7..1aedf98df60df5 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -57,6 +57,7 @@ static bool __is_cp_guaranteed(struct page *page)
enum bio_post_read_step {
STEP_INITIAL = 0,
STEP_DECRYPT,
+ STEP_VERITY,
};
struct bio_post_read_ctx {
@@ -101,8 +102,23 @@ static void decrypt_work(struct work_struct *work)
bio_post_read_processing(ctx);
}
+static void verity_work(struct work_struct *work)
+{
+ struct bio_post_read_ctx *ctx =
+ container_of(work, struct bio_post_read_ctx, work);
+
+ fsverity_verify_bio(ctx->bio);
+
+ bio_post_read_processing(ctx);
+}
+
static void bio_post_read_processing(struct bio_post_read_ctx *ctx)
{
+ /*
+ * We use different work queues for decryption and for verity because
+ * verity may require reading metadata pages that need decryption, and
+ * we shouldn't recurse to the same workqueue.
+ */
switch (++ctx->cur_step) {
case STEP_DECRYPT:
if (ctx->enabled_steps & (1 << STEP_DECRYPT)) {
@@ -112,6 +128,14 @@ static void bio_post_read_processing(struct bio_post_read_ctx *ctx)
}
ctx->cur_step++;
/* fall-through */
+ case STEP_VERITY:
+ if (ctx->enabled_steps & (1 << STEP_VERITY)) {
+ INIT_WORK(&ctx->work, verity_work);
+ fsverity_enqueue_verify_work(&ctx->work);
+ return;
+ }
+ ctx->cur_step++;
+ /* fall-through */
default:
__read_end_io(ctx->bio);
}
@@ -534,7 +558,7 @@ out_fail:
}
static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
- unsigned nr_pages)
+ unsigned int nr_pages, int flags)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct bio *bio;
@@ -550,6 +574,8 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
if (f2fs_encrypted_file(inode))
post_read_steps |= 1 << STEP_DECRYPT;
+ if (f2fs_verity_file(inode) && !(flags & F2FS_GETPAGE_SKIP_VERITY))
+ post_read_steps |= 1 << STEP_VERITY;
if (post_read_steps) {
ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS);
if (!ctx) {
@@ -569,9 +595,9 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
/* This can handle encryption stuffs */
static int f2fs_submit_page_read(struct inode *inode, struct page *page,
- block_t blkaddr)
+ block_t blkaddr, int flags)
{
- struct bio *bio = f2fs_grab_read_bio(inode, blkaddr, 1);
+ struct bio *bio = f2fs_grab_read_bio(inode, blkaddr, 1, flags);
if (IS_ERR(bio))
return PTR_ERR(bio);
@@ -694,7 +720,7 @@ int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index)
}
struct page *get_read_data_page(struct inode *inode, pgoff_t index,
- int op_flags, bool for_write)
+ int op_flags, int flags)
{
struct address_space *mapping = inode->i_mapping;
struct dnode_of_data dn;
@@ -702,7 +728,8 @@ struct page *get_read_data_page(struct inode *inode, pgoff_t index,
struct extent_info ei = {0,0,0};
int err;
- page = f2fs_grab_cache_page(mapping, index, for_write);
+ page = f2fs_grab_cache_page(mapping, index,
+ (flags & F2FS_GETPAGE_FOR_WRITE));
if (!page)
return ERR_PTR(-ENOMEM);
@@ -741,7 +768,7 @@ got_it:
return page;
}
- err = f2fs_submit_page_read(inode, page, dn.data_blkaddr);
+ err = f2fs_submit_page_read(inode, page, dn.data_blkaddr, flags);
if (err)
goto put_err;
return page;
@@ -751,7 +778,7 @@ put_err:
return ERR_PTR(err);
}
-struct page *find_data_page(struct inode *inode, pgoff_t index)
+struct page *find_data_page(struct inode *inode, pgoff_t index, int flags)
{
struct address_space *mapping = inode->i_mapping;
struct page *page;
@@ -761,7 +788,7 @@ struct page *find_data_page(struct inode *inode, pgoff_t index)
return page;
f2fs_put_page(page, 0);
- page = get_read_data_page(inode, index, 0, false);
+ page = get_read_data_page(inode, index, 0, flags);
if (IS_ERR(page))
return page;
@@ -786,8 +813,9 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index,
{
struct address_space *mapping = inode->i_mapping;
struct page *page;
+ int flags = (for_write ? F2FS_GETPAGE_FOR_WRITE : 0);
repeat:
- page = get_read_data_page(inode, index, 0, for_write);
+ page = get_read_data_page(inode, index, 0, flags);
if (IS_ERR(page))
return page;
@@ -1458,8 +1486,8 @@ static int f2fs_mpage_readpages(struct address_space *mapping,
block_in_file = (sector_t)page->index;
last_block = block_in_file + nr_pages;
- last_block_in_file = (i_size_read(inode) + blocksize - 1) >>
- blkbits;
+ last_block_in_file = (fsverity_full_isize(inode) +
+ blocksize - 1) >> blkbits;
if (last_block > last_block_in_file)
last_block = last_block_in_file;
@@ -1496,6 +1524,9 @@ got_it:
}
} else {
zero_user_segment(page, 0, PAGE_SIZE);
+ if (f2fs_verity_file(inode) &&
+ !fsverity_verify_page(page))
+ goto set_error_page;
if (!PageUptodate(page))
SetPageUptodate(page);
unlock_page(page);
@@ -1513,7 +1544,7 @@ submit_and_realloc:
bio = NULL;
}
if (bio == NULL) {
- bio = f2fs_grab_read_bio(inode, block_nr, nr_pages);
+ bio = f2fs_grab_read_bio(inode, block_nr, nr_pages, 0);
if (IS_ERR(bio)) {
bio = NULL;
goto set_error_page;
@@ -2300,7 +2331,7 @@ repeat:
zero_user_segment(page, 0, PAGE_SIZE);
SetPageUptodate(page);
} else {
- err = f2fs_submit_page_read(inode, page, blkaddr);
+ err = f2fs_submit_page_read(inode, page, blkaddr, 0);
if (err)
goto fail;
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 8c9c2f31b253cf..3c4d4462a2676d 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -171,7 +171,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
for (; bidx < end_block; bidx++) {
/* no need to allocate new dentry pages to all the indices */
- dentry_page = find_data_page(dir, bidx);
+ dentry_page = find_data_page(dir, bidx, 0);
if (IS_ERR(dentry_page)) {
if (PTR_ERR(dentry_page) == -ENOENT) {
room = true;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index f32a0c79702f99..e7db228a4d95e8 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -29,6 +29,9 @@
#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_F2FS_FS_ENCRYPTION)
#include <linux/fscrypt.h>
+#define __FS_HAS_VERITY IS_ENABLED(CONFIG_F2FS_FS_VERITY)
+#include <linux/fsverity.h>
+
#ifdef CONFIG_F2FS_CHECK_FS
#define f2fs_bug_on(sbi, condition) BUG_ON(condition)
#else
@@ -146,7 +149,7 @@ struct f2fs_mount_info {
#define F2FS_FEATURE_QUOTA_INO 0x0080
#define F2FS_FEATURE_INODE_CRTIME 0x0100
#define F2FS_FEATURE_LOST_FOUND 0x0200
-#define F2FS_FEATURE_VERITY 0x0400 /* reserved */
+#define F2FS_FEATURE_VERITY 0x0400
#define F2FS_HAS_FEATURE(sb, mask) \
((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0)
@@ -599,7 +602,7 @@ enum {
#define FADVISE_ENC_NAME_BIT 0x08
#define FADVISE_KEEP_SIZE_BIT 0x10
#define FADVISE_HOT_BIT 0x20
-#define FADVISE_VERITY_BIT 0x40 /* reserved */
+#define FADVISE_VERITY_BIT 0x40
#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT)
#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT)
@@ -617,6 +620,8 @@ enum {
#define file_is_hot(inode) is_file(inode, FADVISE_HOT_BIT)
#define file_set_hot(inode) set_file(inode, FADVISE_HOT_BIT)
#define file_clear_hot(inode) clear_file(inode, FADVISE_HOT_BIT)
+#define file_is_verity(inode) is_file(inode, FADVISE_VERITY_BIT)
+#define file_set_verity(inode) set_file(inode, FADVISE_VERITY_BIT)
#define DEF_DIR_LEVEL 0
@@ -2858,6 +2863,8 @@ void destroy_checkpoint_caches(void);
/*
* data.c
*/
+#define F2FS_GETPAGE_FOR_WRITE 0x01
+#define F2FS_GETPAGE_SKIP_VERITY 0x02
int f2fs_init_post_read_processing(void);
void f2fs_destroy_post_read_processing(void);
void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type);
@@ -2878,8 +2885,8 @@ int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index);
int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from);
int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index);
struct page *get_read_data_page(struct inode *inode, pgoff_t index,
- int op_flags, bool for_write);
-struct page *find_data_page(struct inode *inode, pgoff_t index);
+ int op_flags, int flags);
+struct page *find_data_page(struct inode *inode, pgoff_t index, int flags);
struct page *get_lock_data_page(struct inode *inode, pgoff_t index,
bool for_write);
struct page *get_new_data_page(struct inode *inode,
@@ -3219,13 +3226,18 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode)
#endif
}
+static inline bool f2fs_verity_file(struct inode *inode)
+{
+ return file_is_verity(inode);
+}
+
/*
* Returns true if the reads of the inode's data need to undergo some
* postprocessing step, like decryption or authenticity verification.
*/
static inline bool f2fs_post_read_required(struct inode *inode)
{
- return f2fs_encrypted_file(inode);
+ return f2fs_encrypted_file(inode) || f2fs_verity_file(inode);
}
#define F2FS_FEATURE_FUNCS(name, flagname) \
@@ -3243,6 +3255,7 @@ F2FS_FEATURE_FUNCS(flexible_inline_xattr, FLEXIBLE_INLINE_XATTR);
F2FS_FEATURE_FUNCS(quota_ino, QUOTA_INO);
F2FS_FEATURE_FUNCS(inode_crtime, INODE_CRTIME);
F2FS_FEATURE_FUNCS(lost_found, LOST_FOUND);
+F2FS_FEATURE_FUNCS(verity, VERITY);
#ifdef CONFIG_BLK_DEV_ZONED
static inline int get_blkz_type(struct f2fs_sb_info *sbi,
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index cc08956334a079..b96e7dc3057354 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -481,6 +481,12 @@ static int f2fs_file_open(struct inode *inode, struct file *filp)
if (err)
return err;
+ if (f2fs_verity_file(inode)) {
+ err = fsverity_file_open(inode, filp);
+ if (err)
+ return err;
+ }
+
filp->f_mode |= FMODE_NOWAIT;
return dquot_file_open(inode, filp);
@@ -678,6 +684,22 @@ int f2fs_getattr(const struct path *path, struct kstat *stat,
struct f2fs_inode *ri;
unsigned int flags;
+ if (f2fs_verity_file(inode)) {
+ /*
+ * For fs-verity we need to override i_size with the original
+ * data i_size. This requires I/O to the file which with
+ * fscrypt requires that the key be set up. But, if the key is
+ * unavailable just continue on without the i_size override.
+ */
+ int err = fscrypt_require_key(inode);
+
+ if (err != -ENOKEY) {
+ err = fsverity_prepare_getattr(inode);
+ if (err)
+ return err;
+ }
+ }
+
if (f2fs_has_extra_attr(inode) &&
f2fs_sb_has_inode_crtime(inode->i_sb) &&
F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_crtime)) {
@@ -761,6 +783,12 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
if (err)
return err;
+ if (f2fs_verity_file(inode)) {
+ err = fsverity_prepare_setattr(dentry, attr);
+ if (err)
+ return err;
+ }
+
if (is_quota_modification(inode, attr)) {
err = dquot_initialize(inode);
if (err)
@@ -2829,6 +2857,27 @@ static int f2fs_ioc_precache_extents(struct file *filp, unsigned long arg)
return f2fs_precache_extents(file_inode(filp));
}
+static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg)
+{
+ struct inode *inode = file_inode(filp);
+
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+
+ if (!f2fs_sb_has_verity(inode->i_sb)) {
+ f2fs_msg(inode->i_sb, KERN_WARNING,
+ "Can't enable fs-verity on inode %lu: the fs-verity feature is disabled on this filesystem.\n",
+ inode->i_ino);
+ return -EOPNOTSUPP;
+ }
+
+ return fsverity_ioctl_enable(filp, (const void __user *)arg);
+}
+
+static int f2fs_ioc_set_verity_measurement(struct file *filp, unsigned long arg)
+{
+ return fsverity_ioctl_set_measurement(filp, (const void __user *)arg);
+}
+
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -2885,6 +2934,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return f2fs_ioc_set_pin_file(filp, arg);
case F2FS_IOC_PRECACHE_EXTENTS:
return f2fs_ioc_precache_extents(filp, arg);
+ case FS_IOC_ENABLE_VERITY:
+ return f2fs_ioc_enable_verity(filp, arg);
+ case FS_IOC_SET_VERITY_MEASUREMENT:
+ return f2fs_ioc_set_verity_measurement(filp, arg);
default:
return -ENOTTY;
}
@@ -2992,6 +3045,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case F2FS_IOC_GET_PIN_FILE:
case F2FS_IOC_SET_PIN_FILE:
case F2FS_IOC_PRECACHE_EXTENTS:
+ case FS_IOC_ENABLE_VERITY:
+ case FS_IOC_SET_VERITY_MEASUREMENT:
break;
default:
return -ENOIOCTLCMD;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 70418b34c5f6ef..9a366cdc87fa6a 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -865,7 +865,7 @@ next_step:
start_bidx = start_bidx_of_node(nofs, inode);
data_page = get_read_data_page(inode,
start_bidx + ofs_in_node, REQ_RAHEAD,
- true);
+ F2FS_GETPAGE_FOR_WRITE);
up_write(&F2FS_I(inode)->dio_rwsem[WRITE]);
if (IS_ERR(data_page)) {
iput(inode);
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index e0d9e8f27ed2b1..5e6e9f11bf1666 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -390,7 +390,7 @@ void update_inode(struct inode *inode, struct page *node_page)
ri->i_uid = cpu_to_le32(i_uid_read(inode));
ri->i_gid = cpu_to_le32(i_gid_read(inode));
ri->i_links = cpu_to_le32(inode->i_nlink);
- ri->i_size = cpu_to_le64(i_size_read(inode));
+ ri->i_size = cpu_to_le64(fsverity_full_isize(inode));
ri->i_blocks = cpu_to_le64(SECTOR_TO_BLOCK(inode->i_blocks) + 1);
if (et) {
@@ -593,6 +593,7 @@ no_delete:
}
out_clear:
fscrypt_put_encryption_info(inode);
+ fsverity_cleanup_inode(inode);
clear_inode(inode);
}
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index f31643718c76b3..12d069ce9baf32 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1946,6 +1946,40 @@ static const struct fscrypt_operations f2fs_cryptops = {
};
#endif
+#ifdef CONFIG_F2FS_FS_VERITY
+static bool f2fs_is_verity(struct inode *inode)
+{
+ return f2fs_verity_file(inode);
+}
+
+static int f2fs_set_verity(struct inode *inode)
+{
+ int err;
+
+ err = f2fs_convert_inline_inode(inode);
+ if (err)
+ return err;
+
+ file_set_verity(inode);
+ f2fs_mark_inode_dirty_sync(inode, true);
+ return 0;
+}
+
+static struct page *f2fs_read_metadata_page(struct inode *inode, pgoff_t index)
+{
+ if (WARN_ON(f2fs_has_inline_data(inode)))
+ return ERR_PTR(-EINVAL);
+
+ return find_data_page(inode, index, F2FS_GETPAGE_SKIP_VERITY);
+}
+
+static const struct fsverity_operations f2fs_verityops = {
+ .is_verity = f2fs_is_verity,
+ .set_verity = f2fs_set_verity,
+ .read_metadata_page = f2fs_read_metadata_page,
+};
+#endif /* CONFIG_F2FS_FS_VERITY */
+
static struct inode *f2fs_nfs_get_inode(struct super_block *sb,
u64 ino, u32 generation)
{
@@ -2695,6 +2729,9 @@ try_onemore:
#ifdef CONFIG_F2FS_FS_ENCRYPTION
sb->s_cop = &f2fs_cryptops;
#endif
+#ifdef CONFIG_F2FS_FS_VERITY
+ sb->s_vop = &f2fs_verityops;
+#endif
sb->s_xattr = f2fs_xattr_handlers;
sb->s_export_op = &f2fs_export_ops;
sb->s_magic = F2FS_SUPER_MAGIC;
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index f33a56d6e6dd79..40eb7e912e1ec9 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -119,6 +119,9 @@ static ssize_t features_show(struct f2fs_attr *a,
if (f2fs_sb_has_lost_found(sb))
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "lost_found");
+ if (f2fs_sb_has_verity(sb))
+ len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
+ len ? ", " : "", "verity");
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
return len;
}
@@ -296,6 +299,7 @@ enum feat_id {
FEAT_QUOTA_INO,
FEAT_INODE_CRTIME,
FEAT_LOST_FOUND,
+ FEAT_VERITY,
};
static ssize_t f2fs_feature_show(struct f2fs_attr *a,
@@ -312,6 +316,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a,
case FEAT_QUOTA_INO:
case FEAT_INODE_CRTIME:
case FEAT_LOST_FOUND:
+ case FEAT_VERITY:
return snprintf(buf, PAGE_SIZE, "supported\n");
}
return 0;
@@ -392,6 +397,9 @@ F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR);
F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO);
F2FS_FEATURE_RO_ATTR(inode_crtime, FEAT_INODE_CRTIME);
F2FS_FEATURE_RO_ATTR(lost_found, FEAT_LOST_FOUND);
+#ifdef CONFIG_F2FS_FS_VERITY
+F2FS_FEATURE_RO_ATTR(verity, FEAT_VERITY);
+#endif
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
static struct attribute *f2fs_attrs[] = {
@@ -448,6 +456,9 @@ static struct attribute *f2fs_feat_attrs[] = {
ATTR_LIST(quota_ino),
ATTR_LIST(inode_crtime),
ATTR_LIST(lost_found),
+#ifdef CONFIG_F2FS_FS_VERITY
+ ATTR_LIST(verity),
+#endif
NULL,
};