18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * fs/ext4/verity.c: fs-verity support for ext4
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2019 Google LLC
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * Implementation of fsverity_operations for ext4.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * ext4 stores the verity metadata (Merkle tree and fsverity_descriptor) past
128c2ecf20Sopenharmony_ci * the end of the file, starting at the first 64K boundary beyond i_size.  This
138c2ecf20Sopenharmony_ci * approach works because (a) verity files are readonly, and (b) pages fully
148c2ecf20Sopenharmony_ci * beyond i_size aren't visible to userspace but can be read/written internally
158c2ecf20Sopenharmony_ci * by ext4 with only some relatively small changes to ext4.  This approach
168c2ecf20Sopenharmony_ci * avoids having to depend on the EA_INODE feature and on rearchitecturing
178c2ecf20Sopenharmony_ci * ext4's xattr support to support paging multi-gigabyte xattrs into memory, and
188c2ecf20Sopenharmony_ci * to support encrypting xattrs.  Note that the verity metadata *must* be
198c2ecf20Sopenharmony_ci * encrypted when the file is, since it contains hashes of the plaintext data.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * Using a 64K boundary rather than a 4K one keeps things ready for
228c2ecf20Sopenharmony_ci * architectures with 64K pages, and it doesn't necessarily waste space on-disk
238c2ecf20Sopenharmony_ci * since there can be a hole between i_size and the start of the Merkle tree.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/quotaops.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "ext4.h"
298c2ecf20Sopenharmony_ci#include "ext4_extents.h"
308c2ecf20Sopenharmony_ci#include "ext4_jbd2.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic inline loff_t ext4_verity_metadata_pos(const struct inode *inode)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	return round_up(inode->i_size, 65536);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/*
388c2ecf20Sopenharmony_ci * Read some verity metadata from the inode.  __vfs_read() can't be used because
398c2ecf20Sopenharmony_ci * we need to read beyond i_size.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_cistatic int pagecache_read(struct inode *inode, void *buf, size_t count,
428c2ecf20Sopenharmony_ci			  loff_t pos)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	while (count) {
458c2ecf20Sopenharmony_ci		size_t n = min_t(size_t, count,
468c2ecf20Sopenharmony_ci				 PAGE_SIZE - offset_in_page(pos));
478c2ecf20Sopenharmony_ci		struct page *page;
488c2ecf20Sopenharmony_ci		void *addr;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT,
518c2ecf20Sopenharmony_ci					 NULL);
528c2ecf20Sopenharmony_ci		if (IS_ERR(page))
538c2ecf20Sopenharmony_ci			return PTR_ERR(page);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		addr = kmap_atomic(page);
568c2ecf20Sopenharmony_ci		memcpy(buf, addr + offset_in_page(pos), n);
578c2ecf20Sopenharmony_ci		kunmap_atomic(addr);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		put_page(page);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci		buf += n;
628c2ecf20Sopenharmony_ci		pos += n;
638c2ecf20Sopenharmony_ci		count -= n;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci	return 0;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/*
698c2ecf20Sopenharmony_ci * Write some verity metadata to the inode for FS_IOC_ENABLE_VERITY.
708c2ecf20Sopenharmony_ci * kernel_write() can't be used because the file descriptor is readonly.
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_cistatic int pagecache_write(struct inode *inode, const void *buf, size_t count,
738c2ecf20Sopenharmony_ci			   loff_t pos)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	if (pos + count > inode->i_sb->s_maxbytes)
768c2ecf20Sopenharmony_ci		return -EFBIG;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	while (count) {
798c2ecf20Sopenharmony_ci		size_t n = min_t(size_t, count,
808c2ecf20Sopenharmony_ci				 PAGE_SIZE - offset_in_page(pos));
818c2ecf20Sopenharmony_ci		struct page *page;
828c2ecf20Sopenharmony_ci		void *fsdata = NULL;
838c2ecf20Sopenharmony_ci		int res;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		res = pagecache_write_begin(NULL, inode->i_mapping, pos, n, 0,
868c2ecf20Sopenharmony_ci					    &page, &fsdata);
878c2ecf20Sopenharmony_ci		if (res)
888c2ecf20Sopenharmony_ci			return res;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		memcpy_to_page(page, offset_in_page(pos), buf, n);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci		res = pagecache_write_end(NULL, inode->i_mapping, pos, n, n,
938c2ecf20Sopenharmony_ci					  page, fsdata);
948c2ecf20Sopenharmony_ci		if (res < 0)
958c2ecf20Sopenharmony_ci			return res;
968c2ecf20Sopenharmony_ci		if (res != n)
978c2ecf20Sopenharmony_ci			return -EIO;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		buf += n;
1008c2ecf20Sopenharmony_ci		pos += n;
1018c2ecf20Sopenharmony_ci		count -= n;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int ext4_begin_enable_verity(struct file *filp)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filp);
1098c2ecf20Sopenharmony_ci	const int credits = 2; /* superblock and inode for ext4_orphan_add() */
1108c2ecf20Sopenharmony_ci	handle_t *handle;
1118c2ecf20Sopenharmony_ci	int err;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (IS_DAX(inode) || ext4_test_inode_flag(inode, EXT4_INODE_DAX))
1148c2ecf20Sopenharmony_ci		return -EINVAL;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (ext4_verity_in_progress(inode))
1178c2ecf20Sopenharmony_ci		return -EBUSY;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/*
1208c2ecf20Sopenharmony_ci	 * Since the file was opened readonly, we have to initialize the jbd
1218c2ecf20Sopenharmony_ci	 * inode and quotas here and not rely on ->open() doing it.  This must
1228c2ecf20Sopenharmony_ci	 * be done before evicting the inline data.
1238c2ecf20Sopenharmony_ci	 */
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	err = ext4_inode_attach_jinode(inode);
1268c2ecf20Sopenharmony_ci	if (err)
1278c2ecf20Sopenharmony_ci		return err;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	err = dquot_initialize(inode);
1308c2ecf20Sopenharmony_ci	if (err)
1318c2ecf20Sopenharmony_ci		return err;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	err = ext4_convert_inline_data(inode);
1348c2ecf20Sopenharmony_ci	if (err)
1358c2ecf20Sopenharmony_ci		return err;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
1388c2ecf20Sopenharmony_ci		ext4_warning_inode(inode,
1398c2ecf20Sopenharmony_ci				   "verity is only allowed on extent-based files");
1408c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/*
1448c2ecf20Sopenharmony_ci	 * ext4 uses the last allocated block to find the verity descriptor, so
1458c2ecf20Sopenharmony_ci	 * we must remove any other blocks past EOF which might confuse things.
1468c2ecf20Sopenharmony_ci	 */
1478c2ecf20Sopenharmony_ci	err = ext4_truncate(inode);
1488c2ecf20Sopenharmony_ci	if (err)
1498c2ecf20Sopenharmony_ci		return err;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	handle = ext4_journal_start(inode, EXT4_HT_INODE, credits);
1528c2ecf20Sopenharmony_ci	if (IS_ERR(handle))
1538c2ecf20Sopenharmony_ci		return PTR_ERR(handle);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	err = ext4_orphan_add(handle, inode);
1568c2ecf20Sopenharmony_ci	if (err == 0)
1578c2ecf20Sopenharmony_ci		ext4_set_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	ext4_journal_stop(handle);
1608c2ecf20Sopenharmony_ci	return err;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/*
1648c2ecf20Sopenharmony_ci * ext4 stores the verity descriptor beginning on the next filesystem block
1658c2ecf20Sopenharmony_ci * boundary after the Merkle tree.  Then, the descriptor size is stored in the
1668c2ecf20Sopenharmony_ci * last 4 bytes of the last allocated filesystem block --- which is either the
1678c2ecf20Sopenharmony_ci * block in which the descriptor ends, or the next block after that if there
1688c2ecf20Sopenharmony_ci * weren't at least 4 bytes remaining.
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * We can't simply store the descriptor in an xattr because it *must* be
1718c2ecf20Sopenharmony_ci * encrypted when ext4 encryption is used, but ext4 encryption doesn't encrypt
1728c2ecf20Sopenharmony_ci * xattrs.  Also, if the descriptor includes a large signature blob it may be
1738c2ecf20Sopenharmony_ci * too large to store in an xattr without the EA_INODE feature.
1748c2ecf20Sopenharmony_ci */
1758c2ecf20Sopenharmony_cistatic int ext4_write_verity_descriptor(struct inode *inode, const void *desc,
1768c2ecf20Sopenharmony_ci					size_t desc_size, u64 merkle_tree_size)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	const u64 desc_pos = round_up(ext4_verity_metadata_pos(inode) +
1798c2ecf20Sopenharmony_ci				      merkle_tree_size, i_blocksize(inode));
1808c2ecf20Sopenharmony_ci	const u64 desc_end = desc_pos + desc_size;
1818c2ecf20Sopenharmony_ci	const __le32 desc_size_disk = cpu_to_le32(desc_size);
1828c2ecf20Sopenharmony_ci	const u64 desc_size_pos = round_up(desc_end + sizeof(desc_size_disk),
1838c2ecf20Sopenharmony_ci					   i_blocksize(inode)) -
1848c2ecf20Sopenharmony_ci				  sizeof(desc_size_disk);
1858c2ecf20Sopenharmony_ci	int err;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	err = pagecache_write(inode, desc, desc_size, desc_pos);
1888c2ecf20Sopenharmony_ci	if (err)
1898c2ecf20Sopenharmony_ci		return err;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return pagecache_write(inode, &desc_size_disk, sizeof(desc_size_disk),
1928c2ecf20Sopenharmony_ci			       desc_size_pos);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int ext4_end_enable_verity(struct file *filp, const void *desc,
1968c2ecf20Sopenharmony_ci				  size_t desc_size, u64 merkle_tree_size)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filp);
1998c2ecf20Sopenharmony_ci	const int credits = 2; /* superblock and inode for ext4_orphan_del() */
2008c2ecf20Sopenharmony_ci	handle_t *handle;
2018c2ecf20Sopenharmony_ci	struct ext4_iloc iloc;
2028c2ecf20Sopenharmony_ci	int err = 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/*
2058c2ecf20Sopenharmony_ci	 * If an error already occurred (which fs/verity/ signals by passing
2068c2ecf20Sopenharmony_ci	 * desc == NULL), then only clean-up is needed.
2078c2ecf20Sopenharmony_ci	 */
2088c2ecf20Sopenharmony_ci	if (desc == NULL)
2098c2ecf20Sopenharmony_ci		goto cleanup;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* Append the verity descriptor. */
2128c2ecf20Sopenharmony_ci	err = ext4_write_verity_descriptor(inode, desc, desc_size,
2138c2ecf20Sopenharmony_ci					   merkle_tree_size);
2148c2ecf20Sopenharmony_ci	if (err)
2158c2ecf20Sopenharmony_ci		goto cleanup;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/*
2188c2ecf20Sopenharmony_ci	 * Write all pages (both data and verity metadata).  Note that this must
2198c2ecf20Sopenharmony_ci	 * happen before clearing EXT4_STATE_VERITY_IN_PROGRESS; otherwise pages
2208c2ecf20Sopenharmony_ci	 * beyond i_size won't be written properly.  For crash consistency, this
2218c2ecf20Sopenharmony_ci	 * also must happen before the verity inode flag gets persisted.
2228c2ecf20Sopenharmony_ci	 */
2238c2ecf20Sopenharmony_ci	err = filemap_write_and_wait(inode->i_mapping);
2248c2ecf20Sopenharmony_ci	if (err)
2258c2ecf20Sopenharmony_ci		goto cleanup;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/*
2288c2ecf20Sopenharmony_ci	 * Finally, set the verity inode flag and remove the inode from the
2298c2ecf20Sopenharmony_ci	 * orphan list (in a single transaction).
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	handle = ext4_journal_start(inode, EXT4_HT_INODE, credits);
2338c2ecf20Sopenharmony_ci	if (IS_ERR(handle)) {
2348c2ecf20Sopenharmony_ci		err = PTR_ERR(handle);
2358c2ecf20Sopenharmony_ci		goto cleanup;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	err = ext4_orphan_del(handle, inode);
2398c2ecf20Sopenharmony_ci	if (err)
2408c2ecf20Sopenharmony_ci		goto stop_and_cleanup;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	err = ext4_reserve_inode_write(handle, inode, &iloc);
2438c2ecf20Sopenharmony_ci	if (err)
2448c2ecf20Sopenharmony_ci		goto stop_and_cleanup;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	ext4_set_inode_flag(inode, EXT4_INODE_VERITY);
2478c2ecf20Sopenharmony_ci	ext4_set_inode_flags(inode, false);
2488c2ecf20Sopenharmony_ci	err = ext4_mark_iloc_dirty(handle, inode, &iloc);
2498c2ecf20Sopenharmony_ci	if (err)
2508c2ecf20Sopenharmony_ci		goto stop_and_cleanup;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	ext4_journal_stop(handle);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	ext4_clear_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS);
2558c2ecf20Sopenharmony_ci	return 0;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistop_and_cleanup:
2588c2ecf20Sopenharmony_ci	ext4_journal_stop(handle);
2598c2ecf20Sopenharmony_cicleanup:
2608c2ecf20Sopenharmony_ci	/*
2618c2ecf20Sopenharmony_ci	 * Verity failed to be enabled, so clean up by truncating any verity
2628c2ecf20Sopenharmony_ci	 * metadata that was written beyond i_size (both from cache and from
2638c2ecf20Sopenharmony_ci	 * disk), removing the inode from the orphan list (if it wasn't done
2648c2ecf20Sopenharmony_ci	 * already), and clearing EXT4_STATE_VERITY_IN_PROGRESS.
2658c2ecf20Sopenharmony_ci	 */
2668c2ecf20Sopenharmony_ci	truncate_inode_pages(inode->i_mapping, inode->i_size);
2678c2ecf20Sopenharmony_ci	ext4_truncate(inode);
2688c2ecf20Sopenharmony_ci	ext4_orphan_del(NULL, inode);
2698c2ecf20Sopenharmony_ci	ext4_clear_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS);
2708c2ecf20Sopenharmony_ci	return err;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic int ext4_get_verity_descriptor_location(struct inode *inode,
2748c2ecf20Sopenharmony_ci					       size_t *desc_size_ret,
2758c2ecf20Sopenharmony_ci					       u64 *desc_pos_ret)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct ext4_ext_path *path;
2788c2ecf20Sopenharmony_ci	struct ext4_extent *last_extent;
2798c2ecf20Sopenharmony_ci	u32 end_lblk;
2808c2ecf20Sopenharmony_ci	u64 desc_size_pos;
2818c2ecf20Sopenharmony_ci	__le32 desc_size_disk;
2828c2ecf20Sopenharmony_ci	u32 desc_size;
2838c2ecf20Sopenharmony_ci	u64 desc_pos;
2848c2ecf20Sopenharmony_ci	int err;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/*
2878c2ecf20Sopenharmony_ci	 * Descriptor size is in last 4 bytes of last allocated block.
2888c2ecf20Sopenharmony_ci	 * See ext4_write_verity_descriptor().
2898c2ecf20Sopenharmony_ci	 */
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
2928c2ecf20Sopenharmony_ci		EXT4_ERROR_INODE(inode, "verity file doesn't use extents");
2938c2ecf20Sopenharmony_ci		return -EFSCORRUPTED;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0);
2978c2ecf20Sopenharmony_ci	if (IS_ERR(path))
2988c2ecf20Sopenharmony_ci		return PTR_ERR(path);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	last_extent = path[path->p_depth].p_ext;
3018c2ecf20Sopenharmony_ci	if (!last_extent) {
3028c2ecf20Sopenharmony_ci		EXT4_ERROR_INODE(inode, "verity file has no extents");
3038c2ecf20Sopenharmony_ci		ext4_ext_drop_refs(path);
3048c2ecf20Sopenharmony_ci		kfree(path);
3058c2ecf20Sopenharmony_ci		return -EFSCORRUPTED;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	end_lblk = le32_to_cpu(last_extent->ee_block) +
3098c2ecf20Sopenharmony_ci		   ext4_ext_get_actual_len(last_extent);
3108c2ecf20Sopenharmony_ci	desc_size_pos = (u64)end_lblk << inode->i_blkbits;
3118c2ecf20Sopenharmony_ci	ext4_ext_drop_refs(path);
3128c2ecf20Sopenharmony_ci	kfree(path);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (desc_size_pos < sizeof(desc_size_disk))
3158c2ecf20Sopenharmony_ci		goto bad;
3168c2ecf20Sopenharmony_ci	desc_size_pos -= sizeof(desc_size_disk);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	err = pagecache_read(inode, &desc_size_disk, sizeof(desc_size_disk),
3198c2ecf20Sopenharmony_ci			     desc_size_pos);
3208c2ecf20Sopenharmony_ci	if (err)
3218c2ecf20Sopenharmony_ci		return err;
3228c2ecf20Sopenharmony_ci	desc_size = le32_to_cpu(desc_size_disk);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/*
3258c2ecf20Sopenharmony_ci	 * The descriptor is stored just before the desc_size_disk, but starting
3268c2ecf20Sopenharmony_ci	 * on a filesystem block boundary.
3278c2ecf20Sopenharmony_ci	 */
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (desc_size > INT_MAX || desc_size > desc_size_pos)
3308c2ecf20Sopenharmony_ci		goto bad;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	desc_pos = round_down(desc_size_pos - desc_size, i_blocksize(inode));
3338c2ecf20Sopenharmony_ci	if (desc_pos < ext4_verity_metadata_pos(inode))
3348c2ecf20Sopenharmony_ci		goto bad;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	*desc_size_ret = desc_size;
3378c2ecf20Sopenharmony_ci	*desc_pos_ret = desc_pos;
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cibad:
3418c2ecf20Sopenharmony_ci	EXT4_ERROR_INODE(inode, "verity file corrupted; can't find descriptor");
3428c2ecf20Sopenharmony_ci	return -EFSCORRUPTED;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic int ext4_get_verity_descriptor(struct inode *inode, void *buf,
3468c2ecf20Sopenharmony_ci				      size_t buf_size)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	size_t desc_size = 0;
3498c2ecf20Sopenharmony_ci	u64 desc_pos = 0;
3508c2ecf20Sopenharmony_ci	int err;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	err = ext4_get_verity_descriptor_location(inode, &desc_size, &desc_pos);
3538c2ecf20Sopenharmony_ci	if (err)
3548c2ecf20Sopenharmony_ci		return err;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (buf_size) {
3578c2ecf20Sopenharmony_ci		if (desc_size > buf_size)
3588c2ecf20Sopenharmony_ci			return -ERANGE;
3598c2ecf20Sopenharmony_ci		err = pagecache_read(inode, buf, desc_size, desc_pos);
3608c2ecf20Sopenharmony_ci		if (err)
3618c2ecf20Sopenharmony_ci			return err;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci	return desc_size;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic struct page *ext4_read_merkle_tree_page(struct inode *inode,
3678c2ecf20Sopenharmony_ci					       pgoff_t index,
3688c2ecf20Sopenharmony_ci					       unsigned long num_ra_pages)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct page *page;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED);
3758c2ecf20Sopenharmony_ci	if (!page || !PageUptodate(page)) {
3768c2ecf20Sopenharmony_ci		DEFINE_READAHEAD(ractl, NULL, inode->i_mapping, index);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		if (page)
3798c2ecf20Sopenharmony_ci			put_page(page);
3808c2ecf20Sopenharmony_ci		else if (num_ra_pages > 1)
3818c2ecf20Sopenharmony_ci			page_cache_ra_unbounded(&ractl, num_ra_pages, 0);
3828c2ecf20Sopenharmony_ci		page = read_mapping_page(inode->i_mapping, index, NULL);
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci	return page;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int ext4_write_merkle_tree_block(struct inode *inode, const void *buf,
3888c2ecf20Sopenharmony_ci					u64 index, int log_blocksize)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	loff_t pos = ext4_verity_metadata_pos(inode) + (index << log_blocksize);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return pagecache_write(inode, buf, 1 << log_blocksize, pos);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ciconst struct fsverity_operations ext4_verityops = {
3968c2ecf20Sopenharmony_ci	.begin_enable_verity	= ext4_begin_enable_verity,
3978c2ecf20Sopenharmony_ci	.end_enable_verity	= ext4_end_enable_verity,
3988c2ecf20Sopenharmony_ci	.get_verity_descriptor	= ext4_get_verity_descriptor,
3998c2ecf20Sopenharmony_ci	.read_merkle_tree_page	= ext4_read_merkle_tree_page,
4008c2ecf20Sopenharmony_ci	.write_merkle_tree_block = ext4_write_merkle_tree_block,
4018c2ecf20Sopenharmony_ci};
402