Re: [PATCH v6 03/22] fsverity: generate and store zero-block hash

Andrey Albershteyn
2026-04-02T14:47:39Z

On 2026-04-01 15:27:17, Eric Biggers wrote:
> On Tue, Mar 31, 2026 at 11:28:04PM +0200, Andrey Albershteyn wrote:
> > Compute the hash of one filesystem block's worth of zeros. A filesystem
> > implementation can decide to elide merkle tree blocks containing only
> > this hash and synthesize the contents at read time.
> >
> > Let's pretend that there's a file containing six data blocks and whose
> > merkle tree looks roughly like this:
> >
> > root
> > +--leaf0
> > | +--data0
> > | +--data1
> > | `--data2
> > `--leaf1
> > +--data3
> > +--data4
> > `--data5
> >
> > If data[0-2] are sparse holes, then leaf0 will contain a repeating
> > sequence of @zero_digest. Therefore, leaf0 need not be written to disk
> > because its contents can be synthesized.
> >
> > A subsequent xfs patch will use this to reduce the size of the merkle
> > tree when dealing with sparse gold master disk images and the like.
> >
> > Add a helper to pre-fill folio with hashes of empty blocks. This will be
> > used by iomap to synthesize blocks full of zero hashes on the fly.
> >
> > Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> > ---
> > fs/verity/fsverity_private.h | 3 +++
> > fs/verity/open.c | 3 +++
> > fs/verity/pagecache.c | 22 ++++++++++++++++++++++
> > include/linux/fsverity.h | 8 ++++++++
> > 4 files changed, 36 insertions(+)
>
> Acked-by: Eric Biggers <ebiggers@kernel.org>
>
> The example given in the commit message is a bit misleading, though.
> Usually there are actually 128 hashes per block, and a block of hashes
> covers 512 KiB. So this optimization applies only where there is a hole
> in the file's data of size (at least) 512 KiB, aligned to the same
> amount.
>
> It's also worth noting that this optimization is being done only for the
> first level. The levels above that are still being stored. So, this
> doesn't really enable e.g. exabyte sized sparse regions, as a block will
> still be stored for each 64 MiB (instead of every 512 KiB).
>
> I'm okay with this if you want to do this, but I just want to make sure
> its limitations are well-understood.

Sure, I will fix the example and add a note that this is only for
data level.

I think for higher levels it could be later added transparently.
Old tree blocks will be read by iomap if exist, and for new format
block will be generated.

--
- Andrey

>
> > + /* the hash of a merkle block-sized buffer of zeroes */
> > + u8 zero_digest[FS_VERITY_MAX_DIGEST_SIZE];
>
> "the hash of an all-zeroes block" would be clearer. This is the hash
> from fsverity_hash_block() which includes the optional salt, not the
> hash from fsverity_hash_buffer() which does not include the salt.
>
> > +/**
> > + * fsverity_fill_zerohash() - fill folio with hashes of zero data block
> > + * @folio: folio to fill
> > + * @poff: offset in the folio to start
> > + * @plen: length of the range to fill with hashes
>
> Maybe go with (len, offset) for consistency with
> fsverity_verify_blocks(). (I assume the "p" prefix stands for "page",
> which is misleading since this works with a folio.)
>
> > +void fsverity_fill_zerohash(struct folio *folio, size_t poff, size_t plen,
> > + struct fsverity_info *vi)
> > +{
> > + size_t offset = poff;
> > +
> > + WARN_ON_ONCE(!IS_ALIGNED(poff, vi->tree_params.digest_size));
> > + WARN_ON_ONCE(!IS_ALIGNED(plen, vi->tree_params.digest_size));
> > +
> > + for (; offset < (poff + plen); offset += vi->tree_params.digest_size)
> > + memcpy_to_folio(folio, offset, vi->tree_params.zero_digest,
> > + vi->tree_params.digest_size);
>
> This could be done more efficiently, especially on HIGHMEM. Probably
> fine for now though, especially since the intersection of anyone wanting
> XFS && fsverity && HIGHMEM is likely to be extremely small.
>
> - Eric
>