xref: /OK3568_Linux_fs/kernel/fs/ext4/verity.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * fs/ext4/verity.c: fs-verity support for ext4
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright 2019 Google LLC
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun  * Implementation of fsverity_operations for ext4.
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * ext4 stores the verity metadata (Merkle tree and fsverity_descriptor) past
12*4882a593Smuzhiyun  * the end of the file, starting at the first 64K boundary beyond i_size.  This
13*4882a593Smuzhiyun  * approach works because (a) verity files are readonly, and (b) pages fully
14*4882a593Smuzhiyun  * beyond i_size aren't visible to userspace but can be read/written internally
15*4882a593Smuzhiyun  * by ext4 with only some relatively small changes to ext4.  This approach
16*4882a593Smuzhiyun  * avoids having to depend on the EA_INODE feature and on rearchitecturing
17*4882a593Smuzhiyun  * ext4's xattr support to support paging multi-gigabyte xattrs into memory, and
18*4882a593Smuzhiyun  * to support encrypting xattrs.  Note that the verity metadata *must* be
19*4882a593Smuzhiyun  * encrypted when the file is, since it contains hashes of the plaintext data.
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  * Using a 64K boundary rather than a 4K one keeps things ready for
22*4882a593Smuzhiyun  * architectures with 64K pages, and it doesn't necessarily waste space on-disk
23*4882a593Smuzhiyun  * since there can be a hole between i_size and the start of the Merkle tree.
24*4882a593Smuzhiyun  */
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #include <linux/quotaops.h>
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #include "ext4.h"
29*4882a593Smuzhiyun #include "ext4_extents.h"
30*4882a593Smuzhiyun #include "ext4_jbd2.h"
31*4882a593Smuzhiyun 
ext4_verity_metadata_pos(const struct inode * inode)32*4882a593Smuzhiyun static inline loff_t ext4_verity_metadata_pos(const struct inode *inode)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	return round_up(inode->i_size, 65536);
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /*
38*4882a593Smuzhiyun  * Read some verity metadata from the inode.  __vfs_read() can't be used because
39*4882a593Smuzhiyun  * we need to read beyond i_size.
40*4882a593Smuzhiyun  */
pagecache_read(struct inode * inode,void * buf,size_t count,loff_t pos)41*4882a593Smuzhiyun static int pagecache_read(struct inode *inode, void *buf, size_t count,
42*4882a593Smuzhiyun 			  loff_t pos)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	while (count) {
45*4882a593Smuzhiyun 		size_t n = min_t(size_t, count,
46*4882a593Smuzhiyun 				 PAGE_SIZE - offset_in_page(pos));
47*4882a593Smuzhiyun 		struct page *page;
48*4882a593Smuzhiyun 		void *addr;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 		page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT,
51*4882a593Smuzhiyun 					 NULL);
52*4882a593Smuzhiyun 		if (IS_ERR(page))
53*4882a593Smuzhiyun 			return PTR_ERR(page);
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 		addr = kmap_atomic(page);
56*4882a593Smuzhiyun 		memcpy(buf, addr + offset_in_page(pos), n);
57*4882a593Smuzhiyun 		kunmap_atomic(addr);
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 		put_page(page);
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 		buf += n;
62*4882a593Smuzhiyun 		pos += n;
63*4882a593Smuzhiyun 		count -= n;
64*4882a593Smuzhiyun 	}
65*4882a593Smuzhiyun 	return 0;
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun /*
69*4882a593Smuzhiyun  * Write some verity metadata to the inode for FS_IOC_ENABLE_VERITY.
70*4882a593Smuzhiyun  * kernel_write() can't be used because the file descriptor is readonly.
71*4882a593Smuzhiyun  */
pagecache_write(struct inode * inode,const void * buf,size_t count,loff_t pos)72*4882a593Smuzhiyun static int pagecache_write(struct inode *inode, const void *buf, size_t count,
73*4882a593Smuzhiyun 			   loff_t pos)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun 	if (pos + count > inode->i_sb->s_maxbytes)
76*4882a593Smuzhiyun 		return -EFBIG;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	while (count) {
79*4882a593Smuzhiyun 		size_t n = min_t(size_t, count,
80*4882a593Smuzhiyun 				 PAGE_SIZE - offset_in_page(pos));
81*4882a593Smuzhiyun 		struct page *page;
82*4882a593Smuzhiyun 		void *fsdata;
83*4882a593Smuzhiyun 		void *addr;
84*4882a593Smuzhiyun 		int res;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 		res = pagecache_write_begin(NULL, inode->i_mapping, pos, n, 0,
87*4882a593Smuzhiyun 					    &page, &fsdata);
88*4882a593Smuzhiyun 		if (res)
89*4882a593Smuzhiyun 			return res;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 		addr = kmap_atomic(page);
92*4882a593Smuzhiyun 		memcpy(addr + offset_in_page(pos), buf, n);
93*4882a593Smuzhiyun 		kunmap_atomic(addr);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 		res = pagecache_write_end(NULL, inode->i_mapping, pos, n, n,
96*4882a593Smuzhiyun 					  page, fsdata);
97*4882a593Smuzhiyun 		if (res < 0)
98*4882a593Smuzhiyun 			return res;
99*4882a593Smuzhiyun 		if (res != n)
100*4882a593Smuzhiyun 			return -EIO;
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 		buf += n;
103*4882a593Smuzhiyun 		pos += n;
104*4882a593Smuzhiyun 		count -= n;
105*4882a593Smuzhiyun 	}
106*4882a593Smuzhiyun 	return 0;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun 
ext4_begin_enable_verity(struct file * filp)109*4882a593Smuzhiyun static int ext4_begin_enable_verity(struct file *filp)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun 	struct inode *inode = file_inode(filp);
112*4882a593Smuzhiyun 	const int credits = 2; /* superblock and inode for ext4_orphan_add() */
113*4882a593Smuzhiyun 	handle_t *handle;
114*4882a593Smuzhiyun 	int err;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	if (IS_DAX(inode) || ext4_test_inode_flag(inode, EXT4_INODE_DAX))
117*4882a593Smuzhiyun 		return -EINVAL;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	if (ext4_verity_in_progress(inode))
120*4882a593Smuzhiyun 		return -EBUSY;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	/*
123*4882a593Smuzhiyun 	 * Since the file was opened readonly, we have to initialize the jbd
124*4882a593Smuzhiyun 	 * inode and quotas here and not rely on ->open() doing it.  This must
125*4882a593Smuzhiyun 	 * be done before evicting the inline data.
126*4882a593Smuzhiyun 	 */
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	err = ext4_inode_attach_jinode(inode);
129*4882a593Smuzhiyun 	if (err)
130*4882a593Smuzhiyun 		return err;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	err = dquot_initialize(inode);
133*4882a593Smuzhiyun 	if (err)
134*4882a593Smuzhiyun 		return err;
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	err = ext4_convert_inline_data(inode);
137*4882a593Smuzhiyun 	if (err)
138*4882a593Smuzhiyun 		return err;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
141*4882a593Smuzhiyun 		ext4_warning_inode(inode,
142*4882a593Smuzhiyun 				   "verity is only allowed on extent-based files");
143*4882a593Smuzhiyun 		return -EOPNOTSUPP;
144*4882a593Smuzhiyun 	}
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	/*
147*4882a593Smuzhiyun 	 * ext4 uses the last allocated block to find the verity descriptor, so
148*4882a593Smuzhiyun 	 * we must remove any other blocks past EOF which might confuse things.
149*4882a593Smuzhiyun 	 */
150*4882a593Smuzhiyun 	err = ext4_truncate(inode);
151*4882a593Smuzhiyun 	if (err)
152*4882a593Smuzhiyun 		return err;
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	handle = ext4_journal_start(inode, EXT4_HT_INODE, credits);
155*4882a593Smuzhiyun 	if (IS_ERR(handle))
156*4882a593Smuzhiyun 		return PTR_ERR(handle);
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	err = ext4_orphan_add(handle, inode);
159*4882a593Smuzhiyun 	if (err == 0)
160*4882a593Smuzhiyun 		ext4_set_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS);
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	ext4_journal_stop(handle);
163*4882a593Smuzhiyun 	return err;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun /*
167*4882a593Smuzhiyun  * ext4 stores the verity descriptor beginning on the next filesystem block
168*4882a593Smuzhiyun  * boundary after the Merkle tree.  Then, the descriptor size is stored in the
169*4882a593Smuzhiyun  * last 4 bytes of the last allocated filesystem block --- which is either the
170*4882a593Smuzhiyun  * block in which the descriptor ends, or the next block after that if there
171*4882a593Smuzhiyun  * weren't at least 4 bytes remaining.
172*4882a593Smuzhiyun  *
173*4882a593Smuzhiyun  * We can't simply store the descriptor in an xattr because it *must* be
174*4882a593Smuzhiyun  * encrypted when ext4 encryption is used, but ext4 encryption doesn't encrypt
175*4882a593Smuzhiyun  * xattrs.  Also, if the descriptor includes a large signature blob it may be
176*4882a593Smuzhiyun  * too large to store in an xattr without the EA_INODE feature.
177*4882a593Smuzhiyun  */
ext4_write_verity_descriptor(struct inode * inode,const void * desc,size_t desc_size,u64 merkle_tree_size)178*4882a593Smuzhiyun static int ext4_write_verity_descriptor(struct inode *inode, const void *desc,
179*4882a593Smuzhiyun 					size_t desc_size, u64 merkle_tree_size)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun 	const u64 desc_pos = round_up(ext4_verity_metadata_pos(inode) +
182*4882a593Smuzhiyun 				      merkle_tree_size, i_blocksize(inode));
183*4882a593Smuzhiyun 	const u64 desc_end = desc_pos + desc_size;
184*4882a593Smuzhiyun 	const __le32 desc_size_disk = cpu_to_le32(desc_size);
185*4882a593Smuzhiyun 	const u64 desc_size_pos = round_up(desc_end + sizeof(desc_size_disk),
186*4882a593Smuzhiyun 					   i_blocksize(inode)) -
187*4882a593Smuzhiyun 				  sizeof(desc_size_disk);
188*4882a593Smuzhiyun 	int err;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	err = pagecache_write(inode, desc, desc_size, desc_pos);
191*4882a593Smuzhiyun 	if (err)
192*4882a593Smuzhiyun 		return err;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	return pagecache_write(inode, &desc_size_disk, sizeof(desc_size_disk),
195*4882a593Smuzhiyun 			       desc_size_pos);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun 
ext4_end_enable_verity(struct file * filp,const void * desc,size_t desc_size,u64 merkle_tree_size)198*4882a593Smuzhiyun static int ext4_end_enable_verity(struct file *filp, const void *desc,
199*4882a593Smuzhiyun 				  size_t desc_size, u64 merkle_tree_size)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun 	struct inode *inode = file_inode(filp);
202*4882a593Smuzhiyun 	const int credits = 2; /* superblock and inode for ext4_orphan_del() */
203*4882a593Smuzhiyun 	handle_t *handle;
204*4882a593Smuzhiyun 	struct ext4_iloc iloc;
205*4882a593Smuzhiyun 	int err = 0;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	/*
208*4882a593Smuzhiyun 	 * If an error already occurred (which fs/verity/ signals by passing
209*4882a593Smuzhiyun 	 * desc == NULL), then only clean-up is needed.
210*4882a593Smuzhiyun 	 */
211*4882a593Smuzhiyun 	if (desc == NULL)
212*4882a593Smuzhiyun 		goto cleanup;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	/* Append the verity descriptor. */
215*4882a593Smuzhiyun 	err = ext4_write_verity_descriptor(inode, desc, desc_size,
216*4882a593Smuzhiyun 					   merkle_tree_size);
217*4882a593Smuzhiyun 	if (err)
218*4882a593Smuzhiyun 		goto cleanup;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	/*
221*4882a593Smuzhiyun 	 * Write all pages (both data and verity metadata).  Note that this must
222*4882a593Smuzhiyun 	 * happen before clearing EXT4_STATE_VERITY_IN_PROGRESS; otherwise pages
223*4882a593Smuzhiyun 	 * beyond i_size won't be written properly.  For crash consistency, this
224*4882a593Smuzhiyun 	 * also must happen before the verity inode flag gets persisted.
225*4882a593Smuzhiyun 	 */
226*4882a593Smuzhiyun 	err = filemap_write_and_wait(inode->i_mapping);
227*4882a593Smuzhiyun 	if (err)
228*4882a593Smuzhiyun 		goto cleanup;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	/*
231*4882a593Smuzhiyun 	 * Finally, set the verity inode flag and remove the inode from the
232*4882a593Smuzhiyun 	 * orphan list (in a single transaction).
233*4882a593Smuzhiyun 	 */
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	handle = ext4_journal_start(inode, EXT4_HT_INODE, credits);
236*4882a593Smuzhiyun 	if (IS_ERR(handle)) {
237*4882a593Smuzhiyun 		err = PTR_ERR(handle);
238*4882a593Smuzhiyun 		goto cleanup;
239*4882a593Smuzhiyun 	}
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	err = ext4_orphan_del(handle, inode);
242*4882a593Smuzhiyun 	if (err)
243*4882a593Smuzhiyun 		goto stop_and_cleanup;
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	err = ext4_reserve_inode_write(handle, inode, &iloc);
246*4882a593Smuzhiyun 	if (err)
247*4882a593Smuzhiyun 		goto stop_and_cleanup;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	ext4_set_inode_flag(inode, EXT4_INODE_VERITY);
250*4882a593Smuzhiyun 	ext4_set_inode_flags(inode, false);
251*4882a593Smuzhiyun 	err = ext4_mark_iloc_dirty(handle, inode, &iloc);
252*4882a593Smuzhiyun 	if (err)
253*4882a593Smuzhiyun 		goto stop_and_cleanup;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	ext4_journal_stop(handle);
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	ext4_clear_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS);
258*4882a593Smuzhiyun 	return 0;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun stop_and_cleanup:
261*4882a593Smuzhiyun 	ext4_journal_stop(handle);
262*4882a593Smuzhiyun cleanup:
263*4882a593Smuzhiyun 	/*
264*4882a593Smuzhiyun 	 * Verity failed to be enabled, so clean up by truncating any verity
265*4882a593Smuzhiyun 	 * metadata that was written beyond i_size (both from cache and from
266*4882a593Smuzhiyun 	 * disk), removing the inode from the orphan list (if it wasn't done
267*4882a593Smuzhiyun 	 * already), and clearing EXT4_STATE_VERITY_IN_PROGRESS.
268*4882a593Smuzhiyun 	 */
269*4882a593Smuzhiyun 	truncate_inode_pages(inode->i_mapping, inode->i_size);
270*4882a593Smuzhiyun 	ext4_truncate(inode);
271*4882a593Smuzhiyun 	ext4_orphan_del(NULL, inode);
272*4882a593Smuzhiyun 	ext4_clear_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS);
273*4882a593Smuzhiyun 	return err;
274*4882a593Smuzhiyun }
275*4882a593Smuzhiyun 
ext4_get_verity_descriptor_location(struct inode * inode,size_t * desc_size_ret,u64 * desc_pos_ret)276*4882a593Smuzhiyun static int ext4_get_verity_descriptor_location(struct inode *inode,
277*4882a593Smuzhiyun 					       size_t *desc_size_ret,
278*4882a593Smuzhiyun 					       u64 *desc_pos_ret)
279*4882a593Smuzhiyun {
280*4882a593Smuzhiyun 	struct ext4_ext_path *path;
281*4882a593Smuzhiyun 	struct ext4_extent *last_extent;
282*4882a593Smuzhiyun 	u32 end_lblk;
283*4882a593Smuzhiyun 	u64 desc_size_pos;
284*4882a593Smuzhiyun 	__le32 desc_size_disk;
285*4882a593Smuzhiyun 	u32 desc_size;
286*4882a593Smuzhiyun 	u64 desc_pos;
287*4882a593Smuzhiyun 	int err;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	/*
290*4882a593Smuzhiyun 	 * Descriptor size is in last 4 bytes of last allocated block.
291*4882a593Smuzhiyun 	 * See ext4_write_verity_descriptor().
292*4882a593Smuzhiyun 	 */
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
295*4882a593Smuzhiyun 		EXT4_ERROR_INODE(inode, "verity file doesn't use extents");
296*4882a593Smuzhiyun 		return -EFSCORRUPTED;
297*4882a593Smuzhiyun 	}
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0);
300*4882a593Smuzhiyun 	if (IS_ERR(path))
301*4882a593Smuzhiyun 		return PTR_ERR(path);
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	last_extent = path[path->p_depth].p_ext;
304*4882a593Smuzhiyun 	if (!last_extent) {
305*4882a593Smuzhiyun 		EXT4_ERROR_INODE(inode, "verity file has no extents");
306*4882a593Smuzhiyun 		ext4_ext_drop_refs(path);
307*4882a593Smuzhiyun 		kfree(path);
308*4882a593Smuzhiyun 		return -EFSCORRUPTED;
309*4882a593Smuzhiyun 	}
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	end_lblk = le32_to_cpu(last_extent->ee_block) +
312*4882a593Smuzhiyun 		   ext4_ext_get_actual_len(last_extent);
313*4882a593Smuzhiyun 	desc_size_pos = (u64)end_lblk << inode->i_blkbits;
314*4882a593Smuzhiyun 	ext4_ext_drop_refs(path);
315*4882a593Smuzhiyun 	kfree(path);
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	if (desc_size_pos < sizeof(desc_size_disk))
318*4882a593Smuzhiyun 		goto bad;
319*4882a593Smuzhiyun 	desc_size_pos -= sizeof(desc_size_disk);
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	err = pagecache_read(inode, &desc_size_disk, sizeof(desc_size_disk),
322*4882a593Smuzhiyun 			     desc_size_pos);
323*4882a593Smuzhiyun 	if (err)
324*4882a593Smuzhiyun 		return err;
325*4882a593Smuzhiyun 	desc_size = le32_to_cpu(desc_size_disk);
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	/*
328*4882a593Smuzhiyun 	 * The descriptor is stored just before the desc_size_disk, but starting
329*4882a593Smuzhiyun 	 * on a filesystem block boundary.
330*4882a593Smuzhiyun 	 */
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	if (desc_size > INT_MAX || desc_size > desc_size_pos)
333*4882a593Smuzhiyun 		goto bad;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	desc_pos = round_down(desc_size_pos - desc_size, i_blocksize(inode));
336*4882a593Smuzhiyun 	if (desc_pos < ext4_verity_metadata_pos(inode))
337*4882a593Smuzhiyun 		goto bad;
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	*desc_size_ret = desc_size;
340*4882a593Smuzhiyun 	*desc_pos_ret = desc_pos;
341*4882a593Smuzhiyun 	return 0;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun bad:
344*4882a593Smuzhiyun 	EXT4_ERROR_INODE(inode, "verity file corrupted; can't find descriptor");
345*4882a593Smuzhiyun 	return -EFSCORRUPTED;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun 
ext4_get_verity_descriptor(struct inode * inode,void * buf,size_t buf_size)348*4882a593Smuzhiyun static int ext4_get_verity_descriptor(struct inode *inode, void *buf,
349*4882a593Smuzhiyun 				      size_t buf_size)
350*4882a593Smuzhiyun {
351*4882a593Smuzhiyun 	size_t desc_size = 0;
352*4882a593Smuzhiyun 	u64 desc_pos = 0;
353*4882a593Smuzhiyun 	int err;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	err = ext4_get_verity_descriptor_location(inode, &desc_size, &desc_pos);
356*4882a593Smuzhiyun 	if (err)
357*4882a593Smuzhiyun 		return err;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	if (buf_size) {
360*4882a593Smuzhiyun 		if (desc_size > buf_size)
361*4882a593Smuzhiyun 			return -ERANGE;
362*4882a593Smuzhiyun 		err = pagecache_read(inode, buf, desc_size, desc_pos);
363*4882a593Smuzhiyun 		if (err)
364*4882a593Smuzhiyun 			return err;
365*4882a593Smuzhiyun 	}
366*4882a593Smuzhiyun 	return desc_size;
367*4882a593Smuzhiyun }
368*4882a593Smuzhiyun 
ext4_read_merkle_tree_page(struct inode * inode,pgoff_t index,unsigned long num_ra_pages)369*4882a593Smuzhiyun static struct page *ext4_read_merkle_tree_page(struct inode *inode,
370*4882a593Smuzhiyun 					       pgoff_t index,
371*4882a593Smuzhiyun 					       unsigned long num_ra_pages)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun 	struct page *page;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED);
378*4882a593Smuzhiyun 	if (!page || !PageUptodate(page)) {
379*4882a593Smuzhiyun 		DEFINE_READAHEAD(ractl, NULL, inode->i_mapping, index);
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 		if (page)
382*4882a593Smuzhiyun 			put_page(page);
383*4882a593Smuzhiyun 		else if (num_ra_pages > 1)
384*4882a593Smuzhiyun 			page_cache_ra_unbounded(&ractl, num_ra_pages, 0);
385*4882a593Smuzhiyun 		page = read_mapping_page(inode->i_mapping, index, NULL);
386*4882a593Smuzhiyun 	}
387*4882a593Smuzhiyun 	return page;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun 
ext4_write_merkle_tree_block(struct inode * inode,const void * buf,u64 index,int log_blocksize)390*4882a593Smuzhiyun static int ext4_write_merkle_tree_block(struct inode *inode, const void *buf,
391*4882a593Smuzhiyun 					u64 index, int log_blocksize)
392*4882a593Smuzhiyun {
393*4882a593Smuzhiyun 	loff_t pos = ext4_verity_metadata_pos(inode) + (index << log_blocksize);
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	return pagecache_write(inode, buf, 1 << log_blocksize, pos);
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun const struct fsverity_operations ext4_verityops = {
399*4882a593Smuzhiyun 	.begin_enable_verity	= ext4_begin_enable_verity,
400*4882a593Smuzhiyun 	.end_enable_verity	= ext4_end_enable_verity,
401*4882a593Smuzhiyun 	.get_verity_descriptor	= ext4_get_verity_descriptor,
402*4882a593Smuzhiyun 	.read_merkle_tree_page	= ext4_read_merkle_tree_page,
403*4882a593Smuzhiyun 	.write_merkle_tree_block = ext4_write_merkle_tree_block,
404*4882a593Smuzhiyun };
405