summaryrefslogtreecommitdiff
path: root/fs/verity/pagecache.c
blob: 85dac384019479ad2240c4cfb1d91b2e8c402063 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2019 Google LLC
 */
#include "fsverity_private.h"

#include <linux/export.h>
#include <linux/fsverity.h>
#include <linux/pagemap.h>

/**
 * generic_read_merkle_tree_page - generic ->read_merkle_tree_page helper
 * @inode:	inode containing the Merkle tree
 * @index:	0-based index of the Merkle tree page in the inode
 *
 * The caller needs to adjust @index from the Merkle-tree relative index passed
 * to ->read_merkle_tree_page to the actual index where the Merkle tree is
 * stored in the page cache for @inode.
 */
struct page *generic_read_merkle_tree_page(struct inode *inode, pgoff_t index)
{
	struct folio *folio;

	folio = read_mapping_folio(inode->i_mapping, index, NULL);
	if (IS_ERR(folio))
		return ERR_CAST(folio);
	return folio_file_page(folio, index);
}
EXPORT_SYMBOL_GPL(generic_read_merkle_tree_page);

/**
 * generic_readahead_merkle_tree() - generic ->readahead_merkle_tree helper
 * @inode:	inode containing the Merkle tree
 * @index:	0-based index of the first Merkle tree page to read ahead in the
 *		inode
 * @nr_pages:	the number of Merkle tree pages that should be read ahead
 *
 * The caller needs to adjust @index from the Merkle-tree relative index passed
 * to ->read_merkle_tree_page to the actual index where the Merkle tree is
 * stored in the page cache for @inode.
 */
void generic_readahead_merkle_tree(struct inode *inode, pgoff_t index,
				   unsigned long nr_pages)
{
	struct folio *folio;

	lockdep_assert_held(&inode->i_mapping->invalidate_lock);

	folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
	if (folio == ERR_PTR(-ENOENT) ||
	    (!IS_ERR(folio) && !folio_test_uptodate(folio))) {
		DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);

		page_cache_ra_unbounded(&ractl, nr_pages, 0);
	}
	if (!IS_ERR(folio))
		folio_put(folio);
}
EXPORT_SYMBOL_GPL(generic_readahead_merkle_tree);

/**
 * fsverity_fill_zerohash() - fill folio with hashes of zero data block
 * @folio:	folio to fill
 * @offset:	offset in the folio to start
 * @len:	length of the range to fill with hashes
 * @vi:		fsverity info
 */
void fsverity_fill_zerohash(struct folio *folio, size_t offset, size_t len,
			      struct fsverity_info *vi)
{
	size_t off = offset;

	WARN_ON_ONCE(!IS_ALIGNED(offset, vi->tree_params.digest_size));
	WARN_ON_ONCE(!IS_ALIGNED(len, vi->tree_params.digest_size));

	for (; off < (offset + len); off += vi->tree_params.digest_size)
		memcpy_to_folio(folio, off, vi->tree_params.zero_digest,
				vi->tree_params.digest_size);
}
EXPORT_SYMBOL_GPL(fsverity_fill_zerohash);

/**
 * fsverity_pagecache_read() - read verity metadata via the pagecache
 * @inode:	copy from this inode's address space
 * @buf:	buffer to copy to
 * @count:	number of bytes to copy
 * @pos:	byte offset in the file to copy from
 *
 * Read some verity metadata from the inode.  __vfs_read() can't be used because
 * we need to read beyond i_size.
 */
int fsverity_pagecache_read(struct inode *inode, void *buf, size_t count,
			  loff_t pos)
{
	while (count) {
		struct folio *folio;
		size_t n;

		folio = read_mapping_folio(inode->i_mapping, pos >> PAGE_SHIFT,
					 NULL);
		if (IS_ERR(folio))
			return PTR_ERR(folio);

		n = memcpy_from_file_folio(buf, folio, pos, count);
		folio_put(folio);

		buf += n;
		pos += n;
		count -= n;
	}
	return 0;
}
EXPORT_SYMBOL_GPL(fsverity_pagecache_read);