Linux EXT4 FS development
 help / color / mirror / Atom feed
* [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree
@ 2026-09-18 11:15 Andrey Albershteyn
  2026-09-18 11:15 ` [PATCH v16 01/21] fsverity: report validation errors through fserror to fsnotify Andrey Albershteyn
                   ` (21 more replies)
  0 siblings, 22 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

Hi all,

This is next revision of fsverity for XFS.

The main change from v15 is addion of kmem cache
xfs_fsverity_ioend_cache instead of adding work_struct to iomap's ioend
struct.

Patches without review:
[PATCH v16 12/21] xfs: use read ioend for fsverity data verification
[PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten
[PATCH v16 14/21] xfs: don't remove written extents past EOF on

This series based on block/for-next (has lazy-bounce series)

block/for-next:
https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git/log/?h=for-next

kernel:
https://git.kernel.org/pub/scm/linux/kernel/git/aalbersh/xfs-linux.git/log/?h=fsverity

xfsprogs:
https://git.kernel.org/pub/scm/linux/kernel/git/aalbersh/xfsprogs-dev.git/log/?h=fsverity

xfstests:
https://git.kernel.org/pub/scm/linux/kernel/git/aalbersh/xfstests-dev.git/log/?h=fsverity

v15:
https://lore.kernel.org/fsverity/20260817070240.GA17371@lst.de/T/#t

v14:
https://lore.kernel.org/fsverity/anmFWhPNOqe4uyht@aalbersh-thinkpadx1carbongen13.rmtcz.csb/T/#t

v13:
https://lore.kernel.org/fsverity/20260721184346.416657-1-aalbersh@kernel.org/T/#t

v12:
https://lore.kernel.org/fsverity/al8xgOwueDOzGakK@aalbersh-thinkpadx1carbongen13.rmtcz.csb/T/#t

v11:
https://lore.kernel.org/all/20260710085256.3464201-1-aalbersh@kernel.org/

v10:
https://lore.kernel.org/fsverity/20260520123722.405752-1-aalbersh@kernel.org/#r

v9:
https://lore.kernel.org/fsverity/20260428083332.768693-1-aalbersh@kernel.org/#r

To: djwong@kernel.org
To: ebiggers@kernel.org
To: hch@lst.de
To: Carlos Maiolino <cem@kernel.org>

Cc: fsverity@lists.linux.dev
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-xfs@vger.kernel.org
Cc: linux-unionfs@vger.kernel.org
Cc: linux-ext4@vger.kernel.org
Cc: linux-f2fs-devel@lists.sourceforge.net
Cc: linux-btrfs@vger.kernel.org

Cc: david@fromorbit.com

---
Changes in v16:
- Rebase to block/for-next
- Removed work_struct from ioend in favor of kmem_cache structs
- Minor adjustments from v15 review
- Skip written extents in lower level of __xfs_bunmapi()
Changes in v15:
- Pull BIO in task context patches
- Drop flag argument in xfs_free_eofblocks()
- Call xfs_free_eofblocks() on fsverity inodes
- Comments and commit messages updates
- Rebased to v7.2-rc7
- Dropped patch for fsverity_fill_zerohash() with highmem optimization
Changes in v14:
- Rebase to lazy-bounce@hch-misc
- Adjust read ioends to BIO in task context flow
- MMAPLOCK/sb_internal deadlock fix reported by sashiko
- Add missing delalloc clean up in verity_end_enable
- Use xfs_free_eofblocks() instead of writing own clean up routine
- Modify xfs_free_eofblocks() to be able to clean unwritten only
- Dropped fix patch for truncate/set_size check
- Various minor code and #include rearrangements for bisecting
Changes in v13:
- Hoisted statx reporting to common code
- Added read ioend sorted for worker self-deadlock fix
- Adjusted fsverity flags in zoned write path
Changes in v12:
- Refactored xfs_fsverity_cancel_unwritten()
- Switched to using inode_set_flags()
- Add a lock for COW fork reading
- Add pagecache truncation in cleanup path
- Added ERANGE and EBADMSG to fsverity scrub handling
- Add missing XFS_FSVERITY_CONSTRUCTION in various xfs_iomap
- Rebase to -rc3
Changes in v11:
- Drop wrong overlayfs patch
- Drop already merged iomap and fsverity patches
- Update to I_INO() use instead of ip->i_ino
- Sashiko.dev fixes. See list of issues below.
Changes in v10:
- Rebase to v7.1-rc3 with relevant adjustments
- Initialize ioend->io_vi to NULL to not get write work onto verity wq
- Range diff below
Changes in v9:
- Fix fsverity_fill_zerohash() parameter names
- A few fixes found by sashiko.dev:
	- Replace ip->i_mount->m_attr_geo->blksize with m_sb.sb_blocksize
	- Don't call xfs_trans_cancel() after xfs_trans_commit() in
	  xfs_fsverity_end_enable()
	- Call xfs_fsverity_delete_metadata() if verity enable failed
	- Change start/end type from xfs_fileoff_t to loff_t
	- Return xfs_trans_commit() error from
	  xfs_fsverity_cancel_unwritten()
Changes in v8:
- Return fsverity_ensure_verity_info() errors from
  ovl_ensure_verity_loaded()
Changes in v7:
- Move kerneldoc to fsverity_ensure_verity_info() definition
- Drop patch adding XFS traces
- Fix overly long line in the comment
- Make order of fserror and fsverity_error consistent
- Add overlay patch converting to fsverity_ensure_verity_info()
Changes in v6:
- Removed stub for fsverity_ensure_verity_info() as it's optimized out
- Rename fsverity_folio_zero_hash() to fsverify_fill_zerohash()
- Merge patches 8 to 10 into one
- Merge patch gerating zero_hash and fsverity_fill_zerohash() into one
- Add kerneldoc to fsverity_ensure_verity_info()
- Add comments to iomap_block_needs_zeroing()
Changes in v5:
- Add fserror_report_data_lost() for data blocks in page spanning EOF
- Issue fsverity metadata readahead in data readahead
- iomap_fsverity_write() return type fix
- Use of S_ISREG(mode)
- Make 65536 #define instead of open-coded
- Use transaction per unwritten extent removal
- Fetch fsverity_info for all fsverity metadata
- Revert fsverity_folio_zero_hash() stub as used in iomap
- Extend cancel_unwritten to whole file range to remove cow leftovers
- Drop delayed allocation on the COW fork on fsverity completion
Changes in v4:
- Use fserror interface in fsverity instead of fs callback
- Hoist pagecache_read from f2fs/ext4 to fsverity
- Refactor iomap code
- Fetch fsverity_info only for file data and merkle tree holes
- Do not disable preallocation, remove unwritten extents instead
- Offload fsverity hash I/O to fsverity workqueue in read path
- Store merkle tree at round_up(i_size, 64k)
- Add a spacing between merkle tree and fsverity descriptor as next 64k
  aligned block
- Squash helpers into first user commits
- Squash on-disk format changes into single commit
- Drop different offset for pagecache/on-disk
- Don't zero out pages in higher order folios in write path
- Link to v3: https://lore.kernel.org/fsverity/20260217231937.1183679-1-aalbersh@kernel.org/T/#t
Changes in v3:
- Different on-disk and pagecache offset
- Use read path ioends
- Switch to hashtable fsverity info
- Synthesize merkle tree blocks full of zeroes
- Other minor refactors
- Link to v2: https://lore.kernel.org/fsverity/20260114164210.GO15583@frogsfrogsfrogs/T/#t
Changes in v2:
- Move to VFS interface for merkle tree block reading
- Drop patchset for per filesystem workqueues
- Change how offsets of the descriptor and tree metadata is calculated
- Store fs-verity descriptor in data fork side by side with merkle tree
- Simplify iomap changes, remove interface for post eof read/write
- Get rid of extended attribute implementation
- Link to v1: https://lore.kernel.org/r/20250728-fsverity-v1-0-9e5443af0e34@kernel.org

Andrey Albershteyn (19):
  fsverity: report validation errors through fserror to fsnotify
  fsverity: expose ensure_fsverity_info()
  fsverity: pass digest size and hash of the all-zeroes block to ->write
  fsverity: hoist pagecache_read from f2fs/ext4 to fsverity
  fsverity: don't allow setting DAX file attribute on fsverity files
  fsverity: hoist statx reporting of fs-verity flag
  xfs: introduce fsverity on-disk changes
  xfs: don't allow to enable DAX on fs-verity sealed inode
  xfs: disable direct read path for fs-verity files
  xfs: don't report dio_mem_align and dio_offset_align for fsverity
    files
  xfs: handle fsverity I/O in write/read path
  xfs: use read ioend for fsverity data verification
  xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in
    __xfs_bunmapi()
  xfs: don't remove written extents past EOF on fsverity inodes
  xfs: add fs-verity support
  xfs: initialize fs-verity on file open
  xfs: add fs-verity ioctls
  xfs: introduce health state for corrupted fsverity metadata
  xfs: enable ro-compat fs-verity flag

Darrick J. Wong (2):
  xfs: advertise fs-verity being available on filesystem
  xfs: check and repair the verity inode flag state

 fs/btrfs/inode.c               |   3 -
 fs/btrfs/verity.c              |   6 +-
 fs/ext4/inode.c                |   5 +-
 fs/ext4/verity.c               |  36 +--
 fs/f2fs/file.c                 |   5 +-
 fs/f2fs/verity.c               |  34 +--
 fs/file_attr.c                 |  11 +-
 fs/stat.c                      |   6 +-
 fs/verity/enable.c             |   4 +-
 fs/verity/open.c               |  26 ++-
 fs/verity/pagecache.c          |  33 +++
 fs/verity/verify.c             |   4 +
 fs/xfs/Makefile                |   1 +
 fs/xfs/libxfs/xfs_bmap.c       |  17 +-
 fs/xfs/libxfs/xfs_bmap.h       |   6 +-
 fs/xfs/libxfs/xfs_format.h     |  35 ++-
 fs/xfs/libxfs/xfs_fs.h         |   2 +
 fs/xfs/libxfs/xfs_health.h     |   4 +-
 fs/xfs/libxfs/xfs_inode_buf.c  |   8 +
 fs/xfs/libxfs/xfs_inode_util.c |   5 +-
 fs/xfs/libxfs/xfs_sb.c         |   4 +
 fs/xfs/scrub/common.c          |  55 +++++
 fs/xfs/scrub/common.h          |   2 +
 fs/xfs/scrub/inode.c           |   7 +
 fs/xfs/scrub/inode_repair.c    |  36 +++
 fs/xfs/xfs_aops.c              |  49 +++-
 fs/xfs/xfs_bmap_util.c         |  25 +-
 fs/xfs/xfs_file.c              |  71 ++++--
 fs/xfs/xfs_fsverity.c          | 407 +++++++++++++++++++++++++++++++++
 fs/xfs/xfs_fsverity.h          |  36 +++
 fs/xfs/xfs_health.c            |   1 +
 fs/xfs/xfs_inode.h             |   6 +
 fs/xfs/xfs_ioctl.c             |  14 ++
 fs/xfs/xfs_ioend.c             |  65 +++++-
 fs/xfs/xfs_ioend.h             |   4 +-
 fs/xfs/xfs_iomap.c             |  31 ++-
 fs/xfs/xfs_iomap.h             |   5 +-
 fs/xfs/xfs_iops.c              |  12 +-
 fs/xfs/xfs_message.c           |   4 +
 fs/xfs/xfs_message.h           |   1 +
 fs/xfs/xfs_mount.h             |   4 +
 fs/xfs/xfs_super.c             |  22 ++
 include/linux/fsverity.h       |  10 +-
 43 files changed, 984 insertions(+), 138 deletions(-)
 create mode 100644 fs/xfs/xfs_fsverity.c
 create mode 100644 fs/xfs/xfs_fsverity.h

Range-diff against v15:
 1:  1aec2e239aff =  1:  094627f6c973 fsverity: report validation errors through fserror to fsnotify
 2:  eb9775389956 =  2:  877456f91be2 fsverity: expose ensure_fsverity_info()
 3:  62bc33559c3f =  3:  1ab385e28833 fsverity: pass digest size and hash of the all-zeroes block to ->write
 4:  5d4f63be1694 =  4:  d984a8f7ac2a fsverity: hoist pagecache_read from f2fs/ext4 to fsverity
 5:  4c9bfc9694f6 =  5:  bc529e4308a2 fsverity: don't allow setting DAX file attribute on fsverity files
 6:  4a0f5a07d0b1 =  6:  11b7b6d6b237 fsverity: hoist statx reporting of fs-verity flag
 7:  1695341b674c <  -:  ------------ block: add task-context bio completion infrastructure
 8:  43f924f152af <  -:  ------------ block: don't delay bio task completions
 9:  ab8ed572ce79 <  -:  ------------ iomap: add a iomap_ioend_flags helper
10:  3477d06ae82e <  -:  ------------ iomap: add a IOMAP_IOEND_INTEGRITY flag
12:  d96ffea67314 =  7:  f94348c524e8 xfs: introduce fsverity on-disk changes
13:  d8da1e4acec1 =  8:  6c459e4ba14a xfs: don't allow to enable DAX on fs-verity sealed inode
14:  f2125f58f0d5 !  9:  61dbf3277865 xfs: disable direct read path for fs-verity files
    @@ Commit message
     
      ## fs/xfs/xfs_file.c ##
     @@
    - #include <linux/fadvise.h>
      #include <linux/mount.h>
      #include <linux/filelock.h>
    + #include <linux/bio-integrity.h>
     +#include <linux/fsverity.h>
      
      static const struct vm_operations_struct xfs_file_vm_ops;
    @@ fs/xfs/xfs_file.c: xfs_file_dio_read(
     +		return xfs_file_buffered_read(iocb, to);
     +	}
      	if (mapping_stable_writes(iocb->ki_filp->f_mapping)) {
    - 		dio_ops = &xfs_dio_read_bounce_ops;
    - 		dio_flags |= IOMAP_DIO_BOUNCE;
    + 		ret = iomap_dio_rw(iocb, to, &xfs_read_iomap_ops,
    + 				&xfs_dio_read_bounce_ops, 0, NULL, 0);
     @@ fs/xfs/xfs_file.c: xfs_file_dax_read(
      	struct kiocb		*iocb,
      	struct iov_iter		*to)
15:  a30ca3efd40a ! 10:  a915e649b7b5 xfs: don't report dio_mem_align and dio_offset_align for fsverity files
    @@ Commit message
     
         Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
         Acked-by: Eric Biggers <ebiggers@kernel.org>
    +    Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
     
      ## fs/xfs/xfs_iops.c ##
     @@
16:  0d9385f44edb ! 11:  9920843a040c xfs: handle fsverity I/O in write/read path
    @@ Commit message
         the inode.
     
         Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
    +    Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
    +    Reviewed-by: Christoph Hellwig <hch@lst.de>
     
      ## fs/xfs/Makefile ##
     @@ fs/xfs/Makefile: xfs-$(CONFIG_XFS_POSIX_ACL)	+= xfs_acl.o
    @@ fs/xfs/xfs_aops.c
      #include "xfs_zone_alloc.h"
      #include "xfs_rtgroup.h"
     +#include "xfs_fsverity.h"
    - #include <linux/bio-integrity.h>
      
      struct xfs_writepage_ctx {
    + 	struct iomap_writepage_ctx ctx;
     @@ fs/xfs/xfs_aops.c: xfs_map_blocks(
      	int			retries = 0;
      	int			error = 0;
    @@ fs/xfs/xfs_aops.c: xfs_vm_writepages(
     
      ## fs/xfs/xfs_fsverity.c (new) ##
     @@
    -+/* SPDX-License-Identifier: GPL-2.0 */
    ++// SPDX-License-Identifier: GPL-2.0
     +/*
     + * Copyright (C) 2026 Red Hat, Inc.
     + */
    @@ fs/xfs/xfs_fsverity.c (new)
     
      ## fs/xfs/xfs_fsverity.h (new) ##
     @@
    -+/* SPDX-License-Identifier: GPL-2.0 */
    ++// SPDX-License-Identifier: GPL-2.0
     +/*
     + * Copyright (C) 2026 Red Hat, Inc.
     + */
    @@ fs/xfs/xfs_iomap.c: xfs_read_iomap_begin(
     +	return xfs_bmbt_to_iomap(ip, iomap, &imap, flags, iomap_flags, seq);
      }
      
    - const struct iomap_ops xfs_read_iomap_ops = {
    + static DEFINE_IOMAP_ITER_NEXT(xfs_read_iomap_next, xfs_read_iomap_begin);
     
      ## fs/xfs/xfs_iomap.h ##
     @@ fs/xfs/xfs_iomap.h: xfs_iomap_set_anon_write(
    @@ fs/xfs/xfs_iomap.h: xfs_iomap_set_anon_write(
      	iomap->length = length;
     -	iomap->flags = IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY;
     +	iomap->flags = iomap_flags | IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY;
    + 	if (bdev_has_integrity_csum(iomap->bdev))
    + 		iomap->flags |= IOMAP_F_INTEGRITY;
      }
    - 
    - static inline xfs_filblks_t
17:  ee12fec4a28e ! 12:  7bb9559cd4cb xfs: use read ioend for fsverity data verification
    @@ Commit message
         data that needs verification.
     
         Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
    -    Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
     
      ## fs/xfs/xfs_aops.c ##
     @@
    + #include "xfs_zone_alloc.h"
      #include "xfs_rtgroup.h"
      #include "xfs_fsverity.h"
    - #include <linux/bio-integrity.h>
     +#include <linux/fsverity.h>
      
      struct xfs_writepage_ctx {
    @@ fs/xfs/xfs_aops.c: static const struct iomap_read_ops xfs_iomap_read_ops = {
      xfs_get_iomap_read_ops(
     -	const struct address_space	*mapping)
     +	const struct address_space	*mapping,
    -+	loff_t				position)
    ++	loff_t				pos)
      {
      	struct xfs_inode		*ip = XFS_I(mapping->host);
      
     -	if (bdev_has_integrity_csum(xfs_inode_buftarg(ip)->bt_bdev))
     +	if (bdev_has_integrity_csum(xfs_inode_buftarg(ip)->bt_bdev) ||
    -+			xfs_fsverity_is_file_data(ip, position))
    ++	    xfs_fsverity_is_file_data(ip, pos))
      		return &xfs_iomap_read_ops;
      	return &iomap_bio_read_ops;
      }
    @@ fs/xfs/xfs_file.c: xfs_dio_read_bounce_submit_io(
      static const struct iomap_dio_ops xfs_dio_read_bounce_ops = {
     
      ## fs/xfs/xfs_fsverity.c ##
    -@@ fs/xfs/xfs_fsverity.c: xfs_fsverity_metadata_offset(
    +@@
    + #include <linux/fsverity.h>
    + #include <linux/iomap.h>
    + 
    ++struct kmem_cache *xfs_fsverity_ioend_cache;
    ++
    + loff_t
    + xfs_fsverity_metadata_offset(
    + 	const struct xfs_inode	*ip)
      {
      	return round_up(i_size_read(VFS_IC(ip)), XFS_FSVERITY_START_ALIGN);
      }
    @@ fs/xfs/xfs_fsverity.c: xfs_fsverity_metadata_offset(
     
      ## fs/xfs/xfs_fsverity.h ##
     @@
    + #define __XFS_FSVERITY_H__
    + 
    + #include "xfs_platform.h"
    ++#include <linux/iomap.h>
      
      #ifdef CONFIG_FS_VERITY
      loff_t xfs_fsverity_metadata_offset(const struct xfs_inode *ip);
    @@ fs/xfs/xfs_fsverity.h
     +}
      #endif	/* CONFIG_FS_VERITY */
      
    ++struct xfs_fsverity_ioend {
    ++	struct iomap_ioend	*ioend;
    ++	struct work_struct	work;
    ++};
    ++
    ++extern struct kmem_cache *xfs_fsverity_ioend_cache;
    ++
      #endif	/* __XFS_FSVERITY_H__ */
     
      ## fs/xfs/xfs_ioend.c ##
     @@
    - #include "xfs_reflink.h"
    - #include "xfs_zone_alloc.h"
      #include "xfs_ioend.h"
    -+#include "xfs_error.h"
    -+#include "xfs_errortag.h"
    + #include "xfs_error.h"
    + #include "xfs_errortag.h"
     +#include "xfs_fsverity.h"
      #include <linux/bio-integrity.h>
     +#include <linux/fsverity.h>
     +
     +static void
     +xfs_end_fsverity_io_read(
    -+	struct work_struct	*work)
    ++	struct work_struct		*work)
     +{
    -+	struct iomap_ioend	*ioend =
    -+		container_of(work, struct iomap_ioend, io_work);
    ++	struct xfs_fsverity_ioend	*fsv_ioend =
    ++			container_of(work, struct xfs_fsverity_ioend, work);
    ++	struct iomap_ioend		*ioend = fsv_ioend->ioend;
    ++	struct bio			*bio = &ioend->io_bio;
     +
    -+	if (!ioend->io_bio.bi_status)
    -+		fsverity_verify_bio(ioend->io_vi, &ioend->io_bio);
    ++	kmem_cache_free(xfs_fsverity_ioend_cache, fsv_ioend);
     +
    -+	iomap_finish_ioends(
    -+		ioend, blk_status_to_errno(ioend->io_bio.bi_status));
    ++	if (!bio->bi_status)
    ++		fsverity_verify_bio(ioend->io_vi, bio);
    ++
    ++	iomap_finish_ioends(ioend, blk_status_to_errno(bio->bi_status));
     +}
      
    + static void
    + xfs_dio_bounce_end_io(
    +@@ fs/xfs/xfs_ioend.c: xfs_read_bounce_and_resubmit(
    + 
      static void
      xfs_end_io_read(
    - 	struct bio		*bio)
    +-	struct bio		*bio)
    ++	struct bio			*bio)
      {
    - 	struct iomap_ioend	*ioend = iomap_ioend_from_bio(bio);
    -+	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
    - 	int			error = blk_status_to_errno(bio->bi_status);
    +-	struct iomap_ioend	*ioend = iomap_ioend_from_bio(bio);
    +-	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
    +-	struct xfs_mount	*mp = ip->i_mount;
    +-	int			error = blk_status_to_errno(bio->bi_status);
    ++	struct iomap_ioend		*ioend = iomap_ioend_from_bio(bio);
    ++	struct xfs_inode		*ip = XFS_I(ioend->io_inode);
    ++	struct xfs_mount		*mp = ip->i_mount;
    ++	int				error =
    ++			blk_status_to_errno(bio->bi_status);
    ++	struct xfs_fsverity_ioend	*fsv_ioend;
    + 
    + 	if (!error && (ioend->io_flags & IOMAP_IOEND_INTEGRITY)) {
    + 		error = iomap_ioend_integrity_verify(ioend);
    +@@ fs/xfs/xfs_ioend.c: xfs_end_io_read(
    + 		}
    + 	}
      
     +	/*
     +	 * If we have fsverity and block device integrity attached to this bio,
    @@ fs/xfs/xfs_ioend.c
     +	if (IS_ENABLED(CONFIG_FS_VERITY) && !error && ioend->io_vi &&
     +			xfs_fsverity_is_file_data(ip, ioend->io_offset)) {
     +		if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) {
    -+			fsverity_enqueue_verify_work(&ioend->io_work);
    ++			fsv_ioend = kmem_cache_zalloc(xfs_fsverity_ioend_cache,
    ++					GFP_KERNEL);
    ++			if (!fsv_ioend) {
    ++				iomap_finish_ioends(ioend, -ENOMEM);
    ++				return;
    ++			}
    ++			fsv_ioend->ioend = ioend;
    ++			INIT_WORK(&fsv_ioend->work, xfs_end_fsverity_io_read);
    ++
    ++			fsverity_enqueue_verify_work(&fsv_ioend->work);
     +			return;
     +		}
     +
    @@ fs/xfs/xfs_ioend.c: xfs_ioend_submit_read(
     +	u16			ioend_flags,
     +	struct fsverity_info	*vi)
      {
    --	iomap_init_ioend(inode, bio, file_offset, ioend_flags);
    -+	struct iomap_ioend	*ioend;
    -+
    -+	ioend = iomap_init_ioend(inode, bio, file_offset, ioend_flags);
    + 	struct xfs_inode	*ip = XFS_I(inode);
    + 	struct xfs_mount	*mp = ip->i_mount;
    + 	struct iomap_ioend	*ioend;
    + 
    + 	ioend = iomap_init_ioend(inode, bio, file_offset, ioend_flags);
     +	ioend->io_vi = vi;
    -+	INIT_WORK(&ioend->io_work, xfs_end_fsverity_io_read);
    -+
    - 	if (ioend_flags & IOMAP_IOEND_INTEGRITY)
    - 		fs_bio_integrity_alloc(bio);
    - 	bio->bi_end_io = xfs_end_io_read;
    + 	if ((ioend_flags & IOMAP_IOEND_DIRECT) &&
    + 	    READ_ONCE(mp->m_read_bounce) == XFS_READ_BOUNCE_ALWAYS) {
    + 		iomap_bounce_read(ioend, bdev_logical_block_size(bio->bi_bdev),
     
      ## fs/xfs/xfs_ioend.h ##
     @@
    @@ fs/xfs/xfs_ioend.h: static inline bool xfs_ioend_is_append(struct iomap_ioend *i
      
      #endif /* __XFS_IOEND_H */
     
    - ## include/linux/iomap.h ##
    -@@ include/linux/iomap.h: struct iomap_ioend {
    - 	sector_t		io_sector;	/* start sector of ioend */
    - 	void			*io_private;	/* file system private data */
    - 	struct fsverity_info	*io_vi;		/* fsverity info */
    -+	struct work_struct	io_work;	/* fsverity blocking I/O */
    - 	struct bio		io_bio;		/* MUST BE LAST! */
    - };
    + ## fs/xfs/xfs_super.c ##
    +@@ fs/xfs/xfs_super.c: xfs_init_caches(void)
    + 	if (!xfs_parent_args_cache)
    + 		goto out_destroy_xmi_cache;
      
    ++#ifdef CONFIG_FS_VERITY
    ++	xfs_fsverity_ioend_cache = kmem_cache_create("xfs_fsverity_ioend",
    ++					     sizeof(struct xfs_fsverity_ioend),
    ++					     0, 0, NULL);
    ++	if (!xfs_fsverity_ioend_cache)
    ++		goto out_destroy_fsverity_ioend_cache;
    ++#endif
    ++
    + 	return 0;
    + 
    ++#ifdef CONFIG_FS_VERITY
    ++ out_destroy_fsverity_ioend_cache:
    ++	kmem_cache_destroy(xfs_fsverity_ioend_cache);
    ++#endif
    +  out_destroy_xmi_cache:
    + 	kmem_cache_destroy(xfs_xmi_cache);
    +  out_destroy_xmd_cache:
    +@@ fs/xfs/xfs_super.c: xfs_destroy_caches(void)
    + 	 * destroy caches.
    + 	 */
    + 	rcu_barrier();
    ++#ifdef CONFIG_FS_VERITY
    ++	kmem_cache_destroy(xfs_fsverity_ioend_cache);
    ++#endif
    + 	kmem_cache_destroy(xfs_parent_args_cache);
    + 	kmem_cache_destroy(xfs_xmd_cache);
    + 	kmem_cache_destroy(xfs_xmi_cache);
11:  881f5c8f286f ! 13:  37c092fa5240 xfs: use BIO_COMPLETE_IN_TASK for bounce buffered read I/Os
    @@
      ## Metadata ##
    -Author: Christoph Hellwig <hch@lst.de>
    +Author: Andrey Albershteyn <aalbersh@kernel.org>
     
      ## Commit message ##
    -    xfs: use BIO_COMPLETE_IN_TASK for bounce buffered read I/Os
    +    xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi()
     
    -    Stop using the xfs per-inode work struct for completing read bios, as
    -    unlike writes we don't want to serialize reads on a single inode as
    -    there is no exclusive resource contention for them.
    +    The __xfs_bunmapi() is used to remove all extents past EOF. Add
    +    XFS_BMAPI_UNWRITTEN to remove only unwritten extents, while leaving
    +    written ones in place. This will be used in following patch to clean up
    +    unwritten extents on fsverity inodes.
     
    -    Factor the code for kicking off a read that needs and ioend and the
    -    task context completion into a single helper so that it is split off
    -    the xfs_end_bio machinery, which is not only used for writes.
    +    Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
     
    -    Signed-off-by: Christoph Hellwig <hch@lst.de>
    -    Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
    -
    - ## fs/xfs/xfs_aops.c ##
    -@@ fs/xfs/xfs_aops.c: xfs_bio_submit_read(
    - 	const struct iomap_iter		*iter,
    - 	struct iomap_read_folio_ctx	*ctx)
    - {
    --	struct bio			*bio = ctx->read_ctx;
    + ## fs/xfs/libxfs/xfs_bmap.c ##
    +@@ fs/xfs/libxfs/xfs_bmap.c: __xfs_bunmapi(
    + 		if (del.br_startoff + del.br_blockcount > end + 1)
    + 			del.br_blockcount = end + 1 - del.br_startoff;
    + 
    ++		if ((flags & XFS_BMAPI_UNWRITTEN) &&
    ++				del.br_state != XFS_EXT_UNWRITTEN)
    ++			goto skip;
    ++
    + 		if (!isrt || (flags & XFS_BMAPI_REMAP))
    + 			goto delete;
    + 
    +@@ fs/xfs/libxfs/xfs_bmap.c: __xfs_bunmapi(
    + 			if (error)
    + 				goto error0;
    + 		}
     -
    --	/* defer read completions to the ioend workqueue */
    --	iomap_init_ioend(iter->inode, bio, ctx->read_ctx_file_offset,
    --		iomap_ioend_flags(&iter->iomap));
    --	iomap_bio_submit_read_endio(iter, ctx, xfs_end_bio);
    -+	xfs_ioend_submit_read(iter->inode, ctx->read_ctx,
    -+			ctx->read_ctx_file_offset,
    -+			iomap_ioend_flags(&iter->iomap));
    -+	ctx->read_ctx = NULL;
    - }
    - 
    - static const struct iomap_read_ops xfs_iomap_read_ops = {
    ++skip:
    + 		end = del.br_startoff - 1;
    + nodelete:
    + 		/*
     
    - ## fs/xfs/xfs_file.c ##
    -@@ fs/xfs/xfs_file.c: xfs_dio_read_bounce_submit_io(
    - 	struct bio		*bio,
    - 	loff_t			file_offset)
    - {
    --	iomap_init_ioend(iter->inode, bio, file_offset,
    --		iomap_ioend_flags(&iter->iomap) | IOMAP_IOEND_DIRECT);
    --	bio->bi_end_io = xfs_end_bio;
    --	submit_bio(bio);
    -+	xfs_ioend_submit_read(iter->inode, bio, file_offset,
    -+			iomap_ioend_flags(&iter->iomap) | IOMAP_IOEND_DIRECT);
    - }
    + ## fs/xfs/libxfs/xfs_bmap.h ##
    +@@ fs/xfs/libxfs/xfs_bmap.h: struct xfs_bmalloca {
    + /* Try to align allocations to the extent size hint */
    + #define XFS_BMAPI_EXTSZALIGN	(1u << 11)
      
    - static const struct iomap_dio_ops xfs_dio_read_bounce_ops = {
    -
    - ## fs/xfs/xfs_ioend.c ##
    -@@
    - #include "xfs_reflink.h"
    - #include "xfs_zone_alloc.h"
    - #include "xfs_ioend.h"
    -+#include <linux/bio-integrity.h>
    -+
    -+static void
    -+xfs_end_io_read(
    -+	struct bio		*bio)
    -+{
    -+	struct iomap_ioend	*ioend = iomap_ioend_from_bio(bio);
    -+	int			error = blk_status_to_errno(bio->bi_status);
    ++/* Process unwritten extents only. Used for unmapping */
    ++#define XFS_BMAPI_UNWRITTEN	(1u << 12)
     +
    -+	iomap_finish_ioends(ioend, error);
    -+}
    -+
    -+void
    -+xfs_ioend_submit_read(
    -+	struct inode		*inode,
    -+	struct bio		*bio,
    -+	loff_t			file_offset,
    -+	u16			ioend_flags)
    -+{
    -+	iomap_init_ioend(inode, bio, file_offset, ioend_flags);
    -+	if (ioend_flags & IOMAP_IOEND_INTEGRITY)
    -+		fs_bio_integrity_alloc(bio);
    -+	bio->bi_end_io = xfs_end_io_read;
    -+	bio_set_flag(bio, BIO_COMPLETE_IN_TASK);
    -+	submit_bio(bio);
    -+}
    - 
    - static void
    - xfs_ioend_put_open_zones(
    -@@ fs/xfs/xfs_ioend.c: xfs_end_io(
    - 			io_list))) {
    - 		list_del_init(&ioend->io_list);
    - 		iomap_ioend_try_merge(ioend, &tmp);
    --		if (bio_op(&ioend->io_bio) == REQ_OP_READ)
    --			iomap_finish_ioends(ioend,
    --				blk_status_to_errno(ioend->io_bio.bi_status));
    --		else
    --			xfs_end_ioend_write(ioend);
    -+		xfs_end_ioend_write(ioend);
    - 		cond_resched();
    - 	}
    - }
    -
    - ## fs/xfs/xfs_ioend.h ##
    -@@ fs/xfs/xfs_ioend.h: static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
    - }
    + #define XFS_BMAPI_FLAGS \
    + 	{ XFS_BMAPI_ENTIRE,	"ENTIRE" }, \
    + 	{ XFS_BMAPI_METADATA,	"METADATA" }, \
    +@@ fs/xfs/libxfs/xfs_bmap.h: struct xfs_bmalloca {
    + 	{ XFS_BMAPI_COWFORK,	"COWFORK" }, \
    + 	{ XFS_BMAPI_NODISCARD,	"NODISCARD" }, \
    + 	{ XFS_BMAPI_NORMAP,	"NORMAP" },\
    +-	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }
    ++	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }, \
    ++	{ XFS_BMAPI_UNWRITTEN,	"UNWRITTEN" }
      
    - void xfs_end_bio(struct bio *bio);
    -+void xfs_ioend_submit_read(struct inode *inode, struct bio *bio,
    -+		loff_t file_offset, u16 ioend_flags);
      
    - #endif /* __XFS_IOEND_H */
    + static inline int xfs_bmapi_aflag(int w)
18:  d9a489488c05 ! 14:  2560cea141e6 xfs: make xfs_free_eofblocks() work with fsverity inodes
    @@ Metadata
     Author: Andrey Albershteyn <aalbersh@kernel.org>
     
      ## Commit message ##
    -    xfs: make xfs_free_eofblocks() work with fsverity inodes
    +    xfs: don't remove written extents past EOF on fsverity inodes
     
    -    xfs_free_eofblocks() removes any preallocations and unwritten extents
    -    beyond EOF. This is undesired for fsverity as it stores metadata beyond
    -    EOF. However, while merkle tree is being built delayed preallocation and
    -    unwritten extents are used. After metadata construction is done,
    -    fsverity inodes becomes read-only and won't be changed anymore, none of
    -    these unwritten extents or preallocations in post EOF region will be
    -    used.
    +    xfs_free_eofblocks() removes all extents past EOF unless the
    +    XFS_DIFLAG_PREALLOC or XFS_DIFLAG_APPEND flags are set. This is
    +    undesired for fsverity as it stores metadata beyond EOF. However, while
    +    merkle tree is being built, delayed preallocation and unwritten extents
    +    are used. After metadata construction is done, fsverity inodes becomes
    +    read-only and won't be changed anymore, none of these unwritten extents
    +    or preallocations in post EOF region will be used.
     
    -    Add XFS_BMAPI_UNWRITTEN and change xfs_bunmapi_range to remove only
    -    unwritten extents sitting beyond EOF and set it for fsverity inodes.
    -
    -    The xfs_free_eofblocks() will be called on fsverity inode as usual.
    -    However, inodes which are undergoing merkle tree construction need to
    -    be skipped in case reclaim takes place.
    +    Let xfs_free_eofblocks() be called on fsverity inode as usual to remove
    +    anything which is not written extent. However, inodes which are
    +    undergoing merkle tree construction need to be skipped in case reclaim
    +    takes place.
     
         Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
     
    - ## fs/xfs/libxfs/xfs_bmap.c ##
    -@@ fs/xfs/libxfs/xfs_bmap.c: xfs_bmap_validate_extent(
    - 			XFS_IS_REALTIME_INODE(ip), whichfork, irec);
    - }
    - 
    --/*
    -- * Used in xfs_itruncate_extents().  This is the maximum number of extents
    -- * freed from a file in a single transaction.
    -- */
    --#define	XFS_ITRUNC_MAX_EXTENTS	2
    --
    - /*
    -  * Unmap every extent in part of an inode's fork.  We don't do any higher level
    -  * invalidation work at all.
    -+ *
    -+ * The XFS_BMAPI_UNWRITTEN could be passed to remove only unwritten extents,
    -+ * leaving out normal extents in place.
    -  */
    - int
    - xfs_bunmapi_range(
    -@@ fs/xfs/libxfs/xfs_bmap.c: xfs_bunmapi_range(
    - 	xfs_fileoff_t		startoff,
    - 	xfs_fileoff_t		endoff)
    - {
    --	xfs_filblks_t		unmap_len = endoff - startoff + 1;
    -+	xfs_filblks_t           unmap_len;
    - 	int			error = 0;
    -+	int			nimaps = 1;
    -+	int			done = 0;
    -+	struct xfs_bmbt_irec	imap;
    -+	int			read_flags =
    -+			flags & (XFS_BMAPI_ATTRFORK | XFS_BMAPI_ENTIRE);
    - 
    - 	xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
    - 
    --	while (unmap_len > 0) {
    --		ASSERT((*tpp)->t_highest_agno == NULLAGNUMBER);
    --		error = __xfs_bunmapi(*tpp, ip, startoff, &unmap_len, flags,
    --				XFS_ITRUNC_MAX_EXTENTS);
    -+	while (startoff < endoff) {
    -+		nimaps = 1;
    -+
    -+		error = xfs_bmapi_read(ip, startoff, endoff - startoff + 1,
    -+				&imap, &nimaps, read_flags);
    - 		if (error)
    - 			goto out;
    - 
    --		/* free the just unmapped extents */
    --		error = xfs_defer_finish(tpp);
    --		if (error)
    -+		if (nimaps == 0)
    - 			goto out;
    --		cond_resched();
    -+
    -+		if ((flags & XFS_BMAPI_UNWRITTEN) &&
    -+				imap.br_state != XFS_EXT_UNWRITTEN) {
    -+			startoff = imap.br_startoff + imap.br_blockcount;
    -+			continue;
    -+		}
    -+
    -+		unmap_len = min(endoff - imap.br_startoff + 1,
    -+				imap.br_blockcount);
    -+		done = 0;
    -+		while (!done) {
    -+			ASSERT((*tpp)->t_highest_agno == NULLAGNUMBER);
    -+			error = xfs_bunmapi(*tpp, ip, imap.br_startoff,
    -+					unmap_len, flags, nimaps, &done);
    -+			if (error)
    -+				goto out;
    -+
    -+			/* free the just unmapped extent */
    -+			error = xfs_defer_finish(tpp);
    -+			if (error)
    -+				goto out;
    -+			cond_resched();
    -+		}
    -+
    -+		startoff = imap.br_startoff + unmap_len;
    - 	}
    - out:
    - 	return error;
    -
    - ## fs/xfs/libxfs/xfs_bmap.h ##
    -@@ fs/xfs/libxfs/xfs_bmap.h: struct xfs_bmalloca {
    - /* Try to align allocations to the extent size hint */
    - #define XFS_BMAPI_EXTSZALIGN	(1u << 11)
    - 
    -+/* Process unwritten extents only. Used for unmapping */
    -+#define XFS_BMAPI_UNWRITTEN	(1u << 12)
    -+
    - #define XFS_BMAPI_FLAGS \
    - 	{ XFS_BMAPI_ENTIRE,	"ENTIRE" }, \
    - 	{ XFS_BMAPI_METADATA,	"METADATA" }, \
    -@@ fs/xfs/libxfs/xfs_bmap.h: struct xfs_bmalloca {
    - 	{ XFS_BMAPI_COWFORK,	"COWFORK" }, \
    - 	{ XFS_BMAPI_NODISCARD,	"NODISCARD" }, \
    - 	{ XFS_BMAPI_NORMAP,	"NORMAP" },\
    --	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }
    -+	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }, \
    -+	{ XFS_BMAPI_UNWRITTEN,	"UNWRITTEN" }
    - 
    - 
    - static inline int xfs_bmapi_aflag(int w)
    -
      ## fs/xfs/xfs_bmap_util.c ##
     @@
      #include "xfs_rtbitmap.h"
19:  6b085a40ae6f ! 15:  7bb438e1a24e xfs: add fs-verity support
    @@ fs/xfs/xfs_fsverity.c
      #include <linux/iomap.h>
     +#include <linux/pagemap.h>
      
    - loff_t
    - xfs_fsverity_metadata_offset(
    + struct kmem_cache *xfs_fsverity_ioend_cache;
    + 
     @@ fs/xfs/xfs_fsverity.c: xfs_fsverity_is_file_data(
      	return fsverity_active(VFS_IC(ip)) &&
      			offset < xfs_fsverity_metadata_offset(ip);
    @@ fs/xfs/xfs_fsverity.c: xfs_fsverity_is_file_data(
     
      ## fs/xfs/xfs_fsverity.h ##
     @@
    - #define __XFS_FSVERITY_H__
      
      #include "xfs_platform.h"
    + #include <linux/iomap.h>
     +#include <linux/fsverity.h>
      
      #ifdef CONFIG_FS_VERITY
20:  f5d3e63d22d2 = 16:  6df81706af9b xfs: initialize fs-verity on file open
21:  736650f67ab2 = 17:  055d2c228fcf xfs: add fs-verity ioctls
22:  1a1f26614e69 = 18:  1f93d36e9a35 xfs: advertise fs-verity being available on filesystem
23:  58573cc99930 = 19:  c8496df7ca01 xfs: check and repair the verity inode flag state
24:  a69dadffffe7 = 20:  6009a2eb9a39 xfs: introduce health state for corrupted fsverity metadata
25:  be7cfc3ab7c3 = 21:  ae0ee66fb13a xfs: enable ro-compat fs-verity flag
-- 
2.54.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v16 01/21] fsverity: report validation errors through fserror to fsnotify
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:27   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 02/21] fsverity: expose ensure_fsverity_info() Andrey Albershteyn
                   ` (20 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

Reported verification errors to fsnotify through recently added fserror
interface.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/verity/verify.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index 4004a1d42875..db8c350234bb 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -9,6 +9,7 @@
 
 #include <linux/bio.h>
 #include <linux/export.h>
+#include <linux/fserror.h>
 
 #define FS_VERITY_MAX_PENDING_BLOCKS 2
 
@@ -205,6 +206,8 @@ static bool verify_data_block(struct fsverity_info *vi,
 		if (memchr_inv(dblock->data, 0, params->block_size)) {
 			fsverity_err(inode,
 				     "FILE CORRUPTED!  Data past EOF is not zeroed");
+			fserror_report_data_lost(inode, data_pos,
+						 params->block_size, GFP_NOFS);
 			return false;
 		}
 		return true;
@@ -312,6 +315,7 @@ static bool verify_data_block(struct fsverity_info *vi,
 		data_pos, level - 1, params->hash_alg->name, hsize, want_hash,
 		params->hash_alg->name, hsize,
 		level == 0 ? dblock->real_hash : real_hash);
+	fserror_report_data_lost(inode, data_pos, params->block_size, GFP_NOFS);
 error:
 	for (; level > 0; level--) {
 		kunmap_local(hblocks[level - 1].addr);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 02/21] fsverity: expose ensure_fsverity_info()
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
  2026-09-18 11:15 ` [PATCH v16 01/21] fsverity: report validation errors through fserror to fsnotify Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:32   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 03/21] fsverity: pass digest size and hash of the all-zeroes block to ->write Andrey Albershteyn
                   ` (19 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

This function will be used by XFS's scrub to force fsverity activation,
therefore, to read fsverity context.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Acked-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 fs/verity/open.c         | 26 ++++++++++++++++++++++++--
 include/linux/fsverity.h |  2 ++
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/fs/verity/open.c b/fs/verity/open.c
index d0c56a7faa3b..4f06697f5bf4 100644
--- a/fs/verity/open.c
+++ b/fs/verity/open.c
@@ -347,7 +347,28 @@ int fsverity_get_descriptor(struct inode *inode,
 	return 0;
 }
 
-static int ensure_verity_info(struct inode *inode)
+/**
+ * fsverity_ensure_verity_info() - cache verity info if it's not already cached
+ * @inode: the inode for which verity info should be cached
+ *
+ * Ensure this inode has verity info attached to it, it's assumed the inode
+ * already has fsverity enabled. Read fsverity descriptor and creates verity
+ * based on that.
+ *
+ * This needs to be called at least once before any of the inode's data
+ * can be verified (and thus read at all) or the inode's fsverity digest
+ * retrieved.  fsverity_file_open() calls this already, which handles
+ * normal file accesses.  If a filesystem does any internal (i.e. not
+ * associated with a file descriptor) reads of the file's data or
+ * fsverity digest, it must call this explicitly before doing so.
+ *
+ * In case filesystem supports both fscrypt and fsverity, this should be called
+ * after fscrypt's encryption key is set up. Otherwise, the fsverity metadata is
+ * still encrypted. See fscrypt_file_open().
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int fsverity_ensure_verity_info(struct inode *inode)
 {
 	struct fsverity_info *vi = fsverity_get_info(inode), *found;
 	struct fsverity_descriptor *desc;
@@ -383,12 +404,13 @@ static int ensure_verity_info(struct inode *inode)
 	kfree(desc);
 	return err;
 }
+EXPORT_SYMBOL_GPL(fsverity_ensure_verity_info);
 
 int __fsverity_file_open(struct inode *inode, struct file *filp)
 {
 	if (filp->f_mode & FMODE_WRITE)
 		return -EPERM;
-	return ensure_verity_info(inode);
+	return fsverity_ensure_verity_info(inode);
 }
 EXPORT_SYMBOL_GPL(__fsverity_file_open);
 
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index 6c467ded9751..3c3250f6f272 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -317,6 +317,8 @@ static inline int fsverity_file_open(struct inode *inode, struct file *filp)
 	return 0;
 }
 
+int fsverity_ensure_verity_info(struct inode *inode);
+
 void fsverity_cleanup_inode(struct inode *inode);
 
 struct page *generic_read_merkle_tree_page(struct inode *inode, pgoff_t index);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 03/21] fsverity: pass digest size and hash of the all-zeroes block to ->write
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
  2026-09-18 11:15 ` [PATCH v16 01/21] fsverity: report validation errors through fserror to fsnotify Andrey Albershteyn
  2026-09-18 11:15 ` [PATCH v16 02/21] fsverity: expose ensure_fsverity_info() Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:24   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 04/21] fsverity: hoist pagecache_read from f2fs/ext4 to fsverity Andrey Albershteyn
                   ` (18 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david,
	David Sterba

Let filesystem iterate over hashes in the block and check if these are
hashes of zeroed data blocks. XFS will use this to decide if it want to
store tree block full of these hashes.

Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Acked-by: Eric Biggers <ebiggers@kernel.org>
Acked-by: David Sterba <dsterba@suse.com>
---
 fs/btrfs/verity.c        | 6 +++++-
 fs/ext4/verity.c         | 4 +++-
 fs/f2fs/verity.c         | 4 +++-
 fs/verity/enable.c       | 4 +++-
 include/linux/fsverity.h | 6 +++++-
 5 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
index 4e0ab5842274..c572acacb4d4 100644
--- a/fs/btrfs/verity.c
+++ b/fs/btrfs/verity.c
@@ -779,11 +779,15 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode,
  * @buf:	Merkle tree block to write
  * @pos:	the position of the block in the Merkle tree (in bytes)
  * @size:	the Merkle tree block size (in bytes)
+ * @zero_digest:	the hash of the all-zeroes block
+ * @digest_size:	size of zero_digest, in bytes
  *
  * Returns 0 on success or negative error code on failure
  */
 static int btrfs_write_merkle_tree_block(struct file *file, const void *buf,
-					 u64 pos, unsigned int size)
+					 u64 pos, unsigned int size,
+					 const u8 *zero_digest,
+					 unsigned int digest_size)
 {
 	struct inode *inode = file_inode(file);
 	loff_t merkle_pos = merkle_file_pos(inode);
diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
index ca61da53f313..347945ac23a4 100644
--- a/fs/ext4/verity.c
+++ b/fs/ext4/verity.c
@@ -374,7 +374,9 @@ static void ext4_readahead_merkle_tree(struct inode *inode, pgoff_t index,
 }
 
 static int ext4_write_merkle_tree_block(struct file *file, const void *buf,
-					u64 pos, unsigned int size)
+					u64 pos, unsigned int size,
+					const u8 *zero_digest,
+					unsigned int digest_size)
 {
 	pos += ext4_verity_metadata_pos(file_inode(file));
 
diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c
index 39f482515445..cc4158fc841d 100644
--- a/fs/f2fs/verity.c
+++ b/fs/f2fs/verity.c
@@ -272,7 +272,9 @@ static void f2fs_readahead_merkle_tree(struct inode *inode, pgoff_t index,
 }
 
 static int f2fs_write_merkle_tree_block(struct file *file, const void *buf,
-					u64 pos, unsigned int size)
+					u64 pos, unsigned int size,
+					const u8 *zero_digest,
+					unsigned int digest_size)
 {
 	pos += f2fs_verity_metadata_pos(file_inode(file));
 
diff --git a/fs/verity/enable.c b/fs/verity/enable.c
index 42dfed1ce0ce..ad4ff71d7dd9 100644
--- a/fs/verity/enable.c
+++ b/fs/verity/enable.c
@@ -50,7 +50,9 @@ static int write_merkle_tree_block(struct file *file, const u8 *buf,
 	int err;
 
 	err = inode->i_sb->s_vop->write_merkle_tree_block(file, buf, pos,
-							  params->block_size);
+							  params->block_size,
+							  params->zero_digest,
+							  params->digest_size);
 	if (err)
 		fsverity_err(inode, "Error %d writing Merkle tree block %lu",
 			     err, index);
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index 3c3250f6f272..9e7d946676b9 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -124,6 +124,8 @@ struct fsverity_operations {
 	 * @buf: the Merkle tree block to write
 	 * @pos: the position of the block in the Merkle tree (in bytes)
 	 * @size: the Merkle tree block size (in bytes)
+	 * @zero_digest: the hash of the all-zeroes block
+	 * @digest_size: size of zero_digest, in bytes
 	 *
 	 * This is only called between ->begin_enable_verity() and
 	 * ->end_enable_verity().
@@ -131,7 +133,9 @@ struct fsverity_operations {
 	 * Return: 0 on success, -errno on failure
 	 */
 	int (*write_merkle_tree_block)(struct file *file, const void *buf,
-				       u64 pos, unsigned int size);
+				       u64 pos, unsigned int size,
+				       const u8 *zero_digest,
+				       unsigned int digest_size);
 };
 
 #ifdef CONFIG_FS_VERITY
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 04/21] fsverity: hoist pagecache_read from f2fs/ext4 to fsverity
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (2 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 03/21] fsverity: pass digest size and hash of the all-zeroes block to ->write Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:26   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 05/21] fsverity: don't allow setting DAX file attribute on fsverity files Andrey Albershteyn
                   ` (17 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

This is the same function to read from pageache. XFS will also need
this, so move this to core fsverity.

Note that f2fs and ext4 functions diverged a bit, as ext4 operated over
folios and f2fs operated over pages. The common one will operate over
folios.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/ext4/verity.c         | 32 +++-----------------------------
 fs/f2fs/verity.c         | 30 +-----------------------------
 fs/verity/pagecache.c    | 33 +++++++++++++++++++++++++++++++++
 include/linux/fsverity.h |  2 ++
 4 files changed, 39 insertions(+), 58 deletions(-)

diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
index 347945ac23a4..ac5c133f5529 100644
--- a/fs/ext4/verity.c
+++ b/fs/ext4/verity.c
@@ -34,32 +34,6 @@ static inline loff_t ext4_verity_metadata_pos(const struct inode *inode)
 	return round_up(inode->i_size, 65536);
 }
 
-/*
- * Read some verity metadata from the inode.  __vfs_read() can't be used because
- * we need to read beyond i_size.
- */
-static int 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;
-}
-
 /*
  * Write some verity metadata to the inode for FS_IOC_ENABLE_VERITY.
  * kernel_write() can't be used because the file descriptor is readonly.
@@ -311,8 +285,8 @@ static int ext4_get_verity_descriptor_location(struct inode *inode,
 		goto bad;
 	desc_size_pos -= sizeof(desc_size_disk);
 
-	err = pagecache_read(inode, &desc_size_disk, sizeof(desc_size_disk),
-			     desc_size_pos);
+	err = fsverity_pagecache_read(inode, &desc_size_disk,
+				      sizeof(desc_size_disk), desc_size_pos);
 	if (err)
 		return err;
 	desc_size = le32_to_cpu(desc_size_disk);
@@ -352,7 +326,7 @@ static int ext4_get_verity_descriptor(struct inode *inode, void *buf,
 	if (buf_size) {
 		if (desc_size > buf_size)
 			return -ERANGE;
-		err = pagecache_read(inode, buf, desc_size, desc_pos);
+		err = fsverity_pagecache_read(inode, buf, desc_size, desc_pos);
 		if (err)
 			return err;
 	}
diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c
index cc4158fc841d..f3a4617656db 100644
--- a/fs/f2fs/verity.c
+++ b/fs/f2fs/verity.c
@@ -37,34 +37,6 @@ static inline loff_t f2fs_verity_metadata_pos(const struct inode *inode)
 	return round_up(inode->i_size, 65536);
 }
 
-/*
- * Read some verity metadata from the inode.  __vfs_read() can't be used because
- * we need to read beyond i_size.
- */
-static int pagecache_read(struct inode *inode, void *buf, size_t count,
-			  loff_t pos)
-{
-	while (count) {
-		size_t n = min_t(size_t, count,
-				 PAGE_SIZE - offset_in_page(pos));
-		struct page *page;
-
-		page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT,
-					 NULL);
-		if (IS_ERR(page))
-			return PTR_ERR(page);
-
-		memcpy_from_page(buf, page, offset_in_page(pos), n);
-
-		put_page(page);
-
-		buf += n;
-		pos += n;
-		count -= n;
-	}
-	return 0;
-}
-
 /*
  * Write some verity metadata to the inode for FS_IOC_ENABLE_VERITY.
  * kernel_write() can't be used because the file descriptor is readonly.
@@ -250,7 +222,7 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
 	if (buf_size) {
 		if (size > buf_size)
 			return -ERANGE;
-		res = pagecache_read(inode, buf, size, pos);
+		res = fsverity_pagecache_read(inode, buf, size, pos);
 		if (res)
 			return res;
 	}
diff --git a/fs/verity/pagecache.c b/fs/verity/pagecache.c
index 99f5f53eea98..9d82e6b74ba1 100644
--- a/fs/verity/pagecache.c
+++ b/fs/verity/pagecache.c
@@ -78,3 +78,36 @@ void fsverity_fill_zerohash(struct folio *folio, size_t offset, size_t len,
 				vi->tree_params.digest_size);
 }
 EXPORT_SYMBOL_GPL(fsverity_fill_zerohash);
+
+/**
+ * fsverity_pagecache_read() - read page and copy data to buffer
+ * @inode:	copy from this inode's address space
+ * @buf:	buffer to copy to
+ * @count:	number of bytes to copy
+ * @pos:	position of the folio 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);
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index 9e7d946676b9..f9433332c274 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -328,5 +328,7 @@ void fsverity_cleanup_inode(struct inode *inode);
 struct page *generic_read_merkle_tree_page(struct inode *inode, pgoff_t index);
 void generic_readahead_merkle_tree(struct inode *inode, pgoff_t index,
 				   unsigned long nr_pages);
+int fsverity_pagecache_read(struct inode *inode, void *buf, size_t count,
+			    loff_t pos);
 
 #endif	/* _LINUX_FSVERITY_H */
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 05/21] fsverity: don't allow setting DAX file attribute on fsverity files
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (3 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 04/21] fsverity: hoist pagecache_read from f2fs/ext4 to fsverity Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:28   ` sashiko-bot
  2026-09-25  4:35   ` Eric Biggers
  2026-09-18 11:15 ` [PATCH v16 06/21] fsverity: hoist statx reporting of fs-verity flag Andrey Albershteyn
                   ` (16 subsequent siblings)
  21 siblings, 2 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

When fsverity is enabled on the file, with FS_IOC_ENABLE_VERITY ioctl(),
it checks if file has DAX enabled and fails if that's true. However, the
opposite case is not checked.

Note, that the only other filesystem supporting DAX and fsverity is
ext4, and ext4 does check for this case.

Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
---
 fs/file_attr.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/fs/file_attr.c b/fs/file_attr.c
index bfb00d256dd5..391edd1d0ce3 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -235,10 +235,15 @@ static int fileattr_set_prepare(struct inode *inode,
 	/*
 	 * It is only valid to set the DAX flag on regular files and
 	 * directories on filesystems.
+	 *
+	 * DAX and fsverity are incompatible.
 	 */
-	if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
-	    !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
-		return -EINVAL;
+	if (fa->fsx_xflags & FS_XFLAG_DAX) {
+		if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
+			return -EINVAL;
+		if (old_ma->fsx_xflags & FS_XFLAG_VERITY)
+			return -EINVAL;
+	}
 
 	/* Extent size hints of zero turn off the flags. */
 	if (fa->fsx_extsize == 0)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 06/21] fsverity: hoist statx reporting of fs-verity flag
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (4 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 05/21] fsverity: don't allow setting DAX file attribute on fsverity files Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:28   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 07/21] xfs: introduce fsverity on-disk changes Andrey Albershteyn
                   ` (15 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david,
	stable

All filesystems, supporting fsverity, report this status by checking
inode flag. Also, BTRFS was missing stat->attributes_mask, which is
fixed now.

Fixes: 146054090b08 ("btrfs: initial fsverity support")
Cc: stable@vger.kernel.org
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Acked-by: Eric Biggers <ebiggers@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
---
 fs/btrfs/inode.c | 3 ---
 fs/ext4/inode.c  | 5 +----
 fs/f2fs/file.c   | 5 +----
 fs/stat.c        | 6 +++++-
 4 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 93ef3cec191e..c34d0198b988 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8236,7 +8236,6 @@ static int btrfs_getattr(struct mnt_idmap *idmap,
 	struct inode *inode = d_inode(path->dentry);
 	u32 blocksize = btrfs_sb(inode->i_sb)->sectorsize;
 	u32 bi_flags = BTRFS_I(inode)->flags;
-	u32 bi_ro_flags = BTRFS_I(inode)->ro_flags;
 
 	stat->result_mask |= STATX_BTIME;
 	stat->btime.tv_sec = BTRFS_I(inode)->i_otime_sec;
@@ -8249,8 +8248,6 @@ static int btrfs_getattr(struct mnt_idmap *idmap,
 		stat->attributes |= STATX_ATTR_IMMUTABLE;
 	if (bi_flags & BTRFS_INODE_NODUMP)
 		stat->attributes |= STATX_ATTR_NODUMP;
-	if (bi_ro_flags & BTRFS_INODE_RO_VERITY)
-		stat->attributes |= STATX_ATTR_VERITY;
 
 	stat->attributes_mask |= (STATX_ATTR_APPEND |
 				  STATX_ATTR_COMPRESSED |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 26f0f9714f03..206c6245c619 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -6318,15 +6318,12 @@ int ext4_getattr(struct mnt_idmap *idmap, const struct path *path,
 		stat->attributes |= STATX_ATTR_IMMUTABLE;
 	if (flags & EXT4_NODUMP_FL)
 		stat->attributes |= STATX_ATTR_NODUMP;
-	if (flags & EXT4_VERITY_FL)
-		stat->attributes |= STATX_ATTR_VERITY;
 
 	stat->attributes_mask |= (STATX_ATTR_APPEND |
 				  STATX_ATTR_COMPRESSED |
 				  STATX_ATTR_ENCRYPTED |
 				  STATX_ATTR_IMMUTABLE |
-				  STATX_ATTR_NODUMP |
-				  STATX_ATTR_VERITY);
+				  STATX_ATTR_NODUMP);
 
 	generic_fillattr(idmap, request_mask, inode, stat);
 	return 0;
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index edc352569e87..dc1cc0965532 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1075,15 +1075,12 @@ int f2fs_getattr(struct mnt_idmap *idmap, const struct path *path,
 		stat->attributes |= STATX_ATTR_IMMUTABLE;
 	if (flags & F2FS_NODUMP_FL)
 		stat->attributes |= STATX_ATTR_NODUMP;
-	if (IS_VERITY(inode))
-		stat->attributes |= STATX_ATTR_VERITY;
 
 	stat->attributes_mask |= (STATX_ATTR_COMPRESSED |
 				  STATX_ATTR_APPEND |
 				  STATX_ATTR_ENCRYPTED |
 				  STATX_ATTR_IMMUTABLE |
-				  STATX_ATTR_NODUMP |
-				  STATX_ATTR_VERITY);
+				  STATX_ATTR_NODUMP);
 
 	generic_fillattr(idmap, request_mask, inode, stat);
 
diff --git a/fs/stat.c b/fs/stat.c
index c461c3054234..e03954c36b33 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -203,8 +203,12 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
 	if (IS_DAX(inode))
 		stat->attributes |= STATX_ATTR_DAX;
 
+	if (IS_VERITY(inode))
+		stat->attributes |= STATX_ATTR_VERITY;
+
 	stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT |
-				  STATX_ATTR_DAX);
+				  STATX_ATTR_DAX |
+				  STATX_ATTR_VERITY);
 
 	idmap = mnt_idmap(path->mnt);
 	if (inode->i_op->getattr) {
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 07/21] xfs: introduce fsverity on-disk changes
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (5 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 06/21] fsverity: hoist statx reporting of fs-verity flag Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:34   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 08/21] xfs: don't allow to enable DAX on fs-verity sealed inode Andrey Albershteyn
                   ` (14 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

Introduce XFS_DIFLAG2_VERITY for inodes with fsverity. This flag
indicates that inode has fs-verity enabled (i.e. descriptor exist,
tree is built and file is read-only).

Introduce XFS_SB_FEAT_RO_COMPAT_VERITY for filesystems having
fsverity inodes. As on-disk changes applies to fsverity inodes only, let
older kernels read-only access. This will be enabled in the further
patch after full fsverity support.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/libxfs/xfs_format.h     | 30 +++++++++++++++++++++++++++++-
 fs/xfs/libxfs/xfs_inode_buf.c  |  8 ++++++++
 fs/xfs/libxfs/xfs_inode_util.c |  5 ++++-
 fs/xfs/libxfs/xfs_sb.c         |  2 ++
 fs/xfs/xfs_iops.c              |  5 ++++-
 fs/xfs/xfs_mount.h             |  2 ++
 6 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index dd0ed046fbe9..fc196d9e12dd 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -374,6 +374,7 @@ xfs_sb_has_compat_feature(
 #define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)		/* reverse map btree */
 #define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)		/* reflinked files */
 #define XFS_SB_FEAT_RO_COMPAT_INOBTCNT (1 << 3)		/* inobt block counts */
+#define XFS_SB_FEAT_RO_COMPAT_VERITY   (1 << 4)		/* fs-verity */
 #define XFS_SB_FEAT_RO_COMPAT_ALL \
 		(XFS_SB_FEAT_RO_COMPAT_FINOBT | \
 		 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
@@ -1230,16 +1231,21 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)
  */
 #define XFS_DIFLAG2_METADATA_BIT	5
 
+/* inodes sealed with fs-verity */
+#define XFS_DIFLAG2_VERITY_BIT		6
+
 #define XFS_DIFLAG2_DAX		(1ULL << XFS_DIFLAG2_DAX_BIT)
 #define XFS_DIFLAG2_REFLINK	(1ULL << XFS_DIFLAG2_REFLINK_BIT)
 #define XFS_DIFLAG2_COWEXTSIZE	(1ULL << XFS_DIFLAG2_COWEXTSIZE_BIT)
 #define XFS_DIFLAG2_BIGTIME	(1ULL << XFS_DIFLAG2_BIGTIME_BIT)
 #define XFS_DIFLAG2_NREXT64	(1ULL << XFS_DIFLAG2_NREXT64_BIT)
 #define XFS_DIFLAG2_METADATA	(1ULL << XFS_DIFLAG2_METADATA_BIT)
+#define XFS_DIFLAG2_VERITY	(1ULL << XFS_DIFLAG2_VERITY_BIT)
 
 #define XFS_DIFLAG2_ANY \
 	(XFS_DIFLAG2_DAX | XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE | \
-	 XFS_DIFLAG2_BIGTIME | XFS_DIFLAG2_NREXT64 | XFS_DIFLAG2_METADATA)
+	 XFS_DIFLAG2_BIGTIME | XFS_DIFLAG2_NREXT64 | XFS_DIFLAG2_METADATA | \
+	 XFS_DIFLAG2_VERITY)
 
 static inline bool xfs_dinode_has_bigtime(const struct xfs_dinode *dip)
 {
@@ -2027,4 +2033,26 @@ struct xfs_acl {
 #define SGI_ACL_FILE_SIZE	(sizeof(SGI_ACL_FILE)-1)
 #define SGI_ACL_DEFAULT_SIZE	(sizeof(SGI_ACL_DEFAULT)-1)
 
+/*
+ * At maximum of 8 levels with 128 hashes per block (32 bytes SHA-256) maximum
+ * tree size is ((128^8 − 1)/(128 − 1)) = 567*10^12 blocks. This should fit in
+ * 53 bits address space.
+ *
+ * At this Merkle tree size we can cover 295EB large file. This is much larger
+ * than the currently supported file size.
+ *
+ * For sha512 the largest file we can cover ends at 1 << 50 offset, this is also
+ * good.
+ */
+#define XFS_FSVERITY_LARGEST_FILE	((loff_t)1ULL << 53)
+
+/*
+ * Alignment of the fsverity metadata placement. This is largest supported PAGE
+ * SIZE for fsverity. This is used to space out data and metadata in page cache.
+ * The spacing is necessary for non-exposure of metadata to userspace and
+ * correct merkle tree synthesis in the iomap.
+ */
+#define XFS_FSVERITY_START_ALIGN	(65536)
+
+
 #endif /* __XFS_FORMAT_H__ */
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index e4c3f7b24e95..b4cc673adc8a 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -761,6 +761,14 @@ xfs_dinode_verify(
 	    !xfs_has_rtreflink(mp))
 		return __this_address;
 
+	/* only regular files can have fsverity */
+	if (flags2 & XFS_DIFLAG2_VERITY) {
+		if (!xfs_has_verity(mp))
+			return __this_address;
+		if (!S_ISREG(mode))
+			return __this_address;
+	}
+
 	if (xfs_has_zoned(mp) &&
 	    dip->di_metatype == cpu_to_be16(XFS_METAFILE_RTRMAP)) {
 		if (be32_to_cpu(dip->di_used_blocks) > mp->m_sb.sb_rgextents)
diff --git a/fs/xfs/libxfs/xfs_inode_util.c b/fs/xfs/libxfs/xfs_inode_util.c
index 258ac3d0d486..5a516bd31c31 100644
--- a/fs/xfs/libxfs/xfs_inode_util.c
+++ b/fs/xfs/libxfs/xfs_inode_util.c
@@ -74,7 +74,8 @@ xfs_flags2diflags2(
 	uint64_t		di_flags2 =
 		(ip->i_diflags2 & (XFS_DIFLAG2_REFLINK |
 				   XFS_DIFLAG2_BIGTIME |
-				   XFS_DIFLAG2_NREXT64));
+				   XFS_DIFLAG2_NREXT64 |
+				   XFS_DIFLAG2_VERITY));
 
 	if (xflags & FS_XFLAG_DAX)
 		di_flags2 |= XFS_DIFLAG2_DAX;
@@ -126,6 +127,8 @@ xfs_ip2xflags(
 			flags |= FS_XFLAG_DAX;
 		if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE)
 			flags |= FS_XFLAG_COWEXTSIZE;
+		if (ip->i_diflags2 & XFS_DIFLAG2_VERITY)
+			flags |= FS_XFLAG_VERITY;
 	}
 
 	if (xfs_inode_has_attr_fork(ip))
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index f0341adbb879..985c7c6a09f2 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -165,6 +165,8 @@ xfs_sb_version_to_features(
 		features |= XFS_FEAT_REFLINK;
 	if (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_INOBTCNT)
 		features |= XFS_FEAT_INOBTCNT;
+	if (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_VERITY)
+		features |= XFS_FEAT_VERITY;
 	if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_FTYPE)
 		features |= XFS_FEAT_FTYPE;
 	if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_SPINODES)
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index d1306e723899..c7ecbb3d258a 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -1397,12 +1397,15 @@ xfs_diflags_to_iflags(
 		flags |= S_NOATIME;
 	if (init && xfs_inode_should_enable_dax(ip))
 		flags |= S_DAX;
+	if (xflags & FS_XFLAG_VERITY)
+		flags |= S_VERITY;
 
 	/*
 	 * S_DAX can only be set during inode initialization and is never set by
 	 * the VFS, so we cannot mask off S_DAX in i_flags.
 	 */
-	inode->i_flags &= ~(S_IMMUTABLE | S_APPEND | S_SYNC | S_NOATIME);
+	inode->i_flags &=
+		~(S_IMMUTABLE | S_APPEND | S_SYNC | S_NOATIME | S_VERITY);
 	inode->i_flags |= flags;
 }
 
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 894ff2f4ecbd..60cb54c1a5ec 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -403,6 +403,7 @@ typedef struct xfs_mount {
 #define XFS_FEAT_EXCHANGE_RANGE	(1ULL << 27)	/* exchange range */
 #define XFS_FEAT_METADIR	(1ULL << 28)	/* metadata directory tree */
 #define XFS_FEAT_ZONED		(1ULL << 29)	/* zoned RT device */
+#define XFS_FEAT_VERITY		(1ULL << 30)	/* fs-verity */
 
 /* Mount features */
 #define XFS_FEAT_NOLIFETIME	(1ULL << 47)	/* disable lifetime hints */
@@ -460,6 +461,7 @@ __XFS_HAS_FEAT(exchange_range, EXCHANGE_RANGE)
 __XFS_HAS_FEAT(metadir, METADIR)
 __XFS_HAS_FEAT(zoned, ZONED)
 __XFS_HAS_FEAT(nolifetime, NOLIFETIME)
+__XFS_HAS_FEAT(verity, VERITY)
 
 static inline bool xfs_has_rtgroups(const struct xfs_mount *mp)
 {
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 08/21] xfs: don't allow to enable DAX on fs-verity sealed inode
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (6 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 07/21] xfs: introduce fsverity on-disk changes Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:27   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 09/21] xfs: disable direct read path for fs-verity files Andrey Albershteyn
                   ` (13 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

fs-verity doesn't support DAX. Forbid filesystem to enable DAX on
inodes which already have fs-verity enabled. The opposite is checked
when fs-verity is enabled, it won't be enabled if DAX is.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/xfs_iops.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index c7ecbb3d258a..b07ca2a33c64 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -1369,6 +1369,8 @@ xfs_inode_should_enable_dax(
 		return false;
 	if (!xfs_inode_supports_dax(ip))
 		return false;
+	if (ip->i_diflags2 & XFS_DIFLAG2_VERITY)
+		return false;
 	if (xfs_has_dax_always(ip->i_mount))
 		return true;
 	if (ip->i_diflags2 & XFS_DIFLAG2_DAX)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 09/21] xfs: disable direct read path for fs-verity files
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (7 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 08/21] xfs: don't allow to enable DAX on fs-verity sealed inode Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:26   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 10/21] xfs: don't report dio_mem_align and dio_offset_align for fsverity files Andrey Albershteyn
                   ` (12 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

The direct path is not supported on verity files. Attempts to use direct
I/O path on such files should fall back to buffered I/O path.

Add a fall back to buffered I/O at two place, in a common fast path and
latter when lock is acquired. The second check prevents TOCTOU issue
with reading fsverity_active() status and resetting IOCB_DIRECT flag.

If one thread saw fsverity_active() to be false, and then second thread
acquired XFS_IOLOCK_EXCL and enabled fsverity. The first thread will go
through the DIO path.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_file.c | 61 +++++++++++++++++++++++++++++++----------------
 1 file changed, 41 insertions(+), 20 deletions(-)

diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index dd3782e9af93..07abf6e8783f 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -38,6 +38,7 @@
 #include <linux/mount.h>
 #include <linux/filelock.h>
 #include <linux/bio-integrity.h>
+#include <linux/fsverity.h>
 
 static const struct vm_operations_struct xfs_file_vm_ops;
 
@@ -232,6 +233,25 @@ static const struct iomap_dio_ops xfs_dio_read_bounce_ops = {
 	.bio_set	= &iomap_ioend_bioset,
 };
 
+STATIC ssize_t
+xfs_file_buffered_read(
+	struct kiocb		*iocb,
+	struct iov_iter		*to)
+{
+	struct xfs_inode	*ip = XFS_I(file_inode(iocb->ki_filp));
+	ssize_t			ret;
+
+	trace_xfs_file_buffered_read(iocb, to);
+
+	ret = xfs_ilock_iocb(iocb, XFS_IOLOCK_SHARED);
+	if (ret)
+		return ret;
+	ret = generic_file_read_iter(iocb, to);
+	xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+
+	return ret;
+}
+
 STATIC ssize_t
 xfs_file_dio_read(
 	struct kiocb		*iocb,
@@ -250,6 +270,17 @@ xfs_file_dio_read(
 	ret = xfs_ilock_iocb(iocb, XFS_IOLOCK_SHARED);
 	if (ret)
 		return ret;
+
+	/*
+	 * Re-check verity status after acquiring lock. This prevents TOCTOU in
+	 * xfs_file_read_iter() while falling back from DIO to buffered I/O as
+	 * now we are holding a lock
+	 */
+	if (fsverity_active(VFS_I(ip))) {
+		xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+		iocb->ki_flags &= ~IOCB_DIRECT;
+		return xfs_file_buffered_read(iocb, to);
+	}
 	if (mapping_stable_writes(iocb->ki_filp->f_mapping)) {
 		ret = iomap_dio_rw(iocb, to, &xfs_read_iomap_ops,
 				&xfs_dio_read_bounce_ops, 0, NULL, 0);
@@ -269,7 +300,8 @@ xfs_file_dax_read(
 	struct kiocb		*iocb,
 	struct iov_iter		*to)
 {
-	struct xfs_inode	*ip = XFS_I(iocb->ki_filp->f_mapping->host);
+	struct inode		*inode = iocb->ki_filp->f_mapping->host;
+	struct xfs_inode	*ip = XFS_I(inode);
 	ssize_t			ret = 0;
 
 	trace_xfs_file_dax_read(iocb, to);
@@ -287,25 +319,6 @@ xfs_file_dax_read(
 	return ret;
 }
 
-STATIC ssize_t
-xfs_file_buffered_read(
-	struct kiocb		*iocb,
-	struct iov_iter		*to)
-{
-	struct xfs_inode	*ip = XFS_I(file_inode(iocb->ki_filp));
-	ssize_t			ret;
-
-	trace_xfs_file_buffered_read(iocb, to);
-
-	ret = xfs_ilock_iocb(iocb, XFS_IOLOCK_SHARED);
-	if (ret)
-		return ret;
-	ret = generic_file_read_iter(iocb, to);
-	xfs_iunlock(ip, XFS_IOLOCK_SHARED);
-
-	return ret;
-}
-
 STATIC ssize_t
 xfs_file_read_iter(
 	struct kiocb		*iocb,
@@ -320,6 +333,14 @@ xfs_file_read_iter(
 	if (xfs_is_shutdown(mp))
 		return -EIO;
 
+	/*
+	 * In case fs-verity is enabled, we also fallback to the buffered read
+	 * from the direct read path. Therefore, IOCB_DIRECT is set and need to
+	 * be cleared (see generic_file_read_iter())
+	 */
+	if (fsverity_active(inode))
+		iocb->ki_flags &= ~IOCB_DIRECT;
+
 	if (IS_DAX(inode))
 		ret = xfs_file_dax_read(iocb, to);
 	else if (iocb->ki_flags & IOCB_DIRECT)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 10/21] xfs: don't report dio_mem_align and dio_offset_align for fsverity files
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (8 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 09/21] xfs: disable direct read path for fs-verity files Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:26   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 11/21] xfs: handle fsverity I/O in write/read path Andrey Albershteyn
                   ` (11 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

Sashiko.dev reported that while fsverity files falls back to the
buffered IO for Direct I/O, they should not report non-zero values in
dio_mem_align and dio_offset_align, meaning it's not supported.

The STATX_DIOALIGN | STATX_DIO_READ_ALIGN flags are used to explicitly
report if DIO is supported or not. They can not indicate that fallback
to the buffered IO is used in this case. The zero alignment values also
mean that DIO is not supported on this file, see statx(2).

Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Acked-by: Eric Biggers <ebiggers@kernel.org>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
---
 fs/xfs/xfs_iops.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index b07ca2a33c64..d9c25c06cc60 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -35,6 +35,7 @@
 #include <linux/security.h>
 #include <linux/iversion.h>
 #include <linux/fiemap.h>
+#include <linux/fsverity.h>
 
 /*
  * Directories have different lock order w.r.t. mmap_lock compared to regular
@@ -579,6 +580,10 @@ xfs_report_dioalign(
 	struct block_device	*bdev = target->bt_bdev;
 
 	stat->result_mask |= STATX_DIOALIGN | STATX_DIO_READ_ALIGN;
+
+	if (fsverity_active(VFS_I(ip)))
+		return;
+
 	stat->dio_mem_align = bdev_dma_alignment(bdev) + 1;
 
 	/*
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 11/21] xfs: handle fsverity I/O in write/read path
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (9 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 10/21] xfs: don't report dio_mem_align and dio_offset_align for fsverity files Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:38   ` sashiko-bot
  2026-09-22  7:15   ` Christoph Hellwig
  2026-09-18 11:15 ` [PATCH v16 12/21] xfs: use read ioend for fsverity data verification Andrey Albershteyn
                   ` (10 subsequent siblings)
  21 siblings, 2 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

For write/writeback set IOMAP_F_FSVERITY flag telling iomap to not
update inode size and to not skip folios beyond EOF.

Initiate fsverity writeback with IOMAP_F_FSVERITY set to tell iomap
should not skip folio that is dirty beyond EOF.

In read path let iomap know that we are reading fsverity metadata. So,
treat holes in the tree as request to synthesize tree blocks and hole
after descriptor as end of the fsverity region.

Introduce a new inode flag meaning that merkle tree is being build on
the inode.

Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/Makefile          |  1 +
 fs/xfs/libxfs/xfs_bmap.c | 11 +++++++++++
 fs/xfs/xfs_aops.c        | 36 +++++++++++++++++++++++++++++++-----
 fs/xfs/xfs_fsverity.c    | 22 ++++++++++++++++++++++
 fs/xfs/xfs_fsverity.h    | 20 ++++++++++++++++++++
 fs/xfs/xfs_inode.h       |  6 ++++++
 fs/xfs/xfs_iomap.c       | 31 ++++++++++++++++++++++++-------
 fs/xfs/xfs_iomap.h       |  5 +++--
 8 files changed, 118 insertions(+), 14 deletions(-)
 create mode 100644 fs/xfs/xfs_fsverity.c
 create mode 100644 fs/xfs/xfs_fsverity.h

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 399a207f2d0e..dd712c521862 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -150,6 +150,7 @@ xfs-$(CONFIG_XFS_POSIX_ACL)	+= xfs_acl.o
 xfs-$(CONFIG_SYSCTL)		+= xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)		+= xfs_ioctl32.o
 xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)	+= xfs_pnfs.o
+xfs-$(CONFIG_FS_VERITY)		+= xfs_fsverity.o
 
 # notify failure
 ifeq ($(CONFIG_MEMORY_FAILURE),y)
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index d64defeda645..cc48f6e20e80 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -41,6 +41,8 @@
 #include "xfs_inode_util.h"
 #include "xfs_rtgroup.h"
 #include "xfs_zone_alloc.h"
+#include "xfs_fsverity.h"
+#include <linux/fsverity.h>
 
 struct kmem_cache		*xfs_bmap_intent_cache;
 
@@ -4402,6 +4404,10 @@ xfs_bmapi_convert_one_delalloc(
 	 * the extent.  Just return the real extent at this offset.
 	 */
 	if (!isnullstartblock(bma.got.br_startblock)) {
+		if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION) &&
+		    XFS_FSB_TO_B(mp, bma.got.br_startoff) >=
+			    xfs_fsverity_metadata_offset(ip))
+			flags |= IOMAP_F_FSVERITY;
 		xfs_bmbt_to_iomap(ip, iomap, &bma.got, 0, flags,
 				xfs_iomap_inode_sequence(ip, flags));
 		if (seq)
@@ -4449,6 +4455,11 @@ xfs_bmapi_convert_one_delalloc(
 	XFS_STATS_ADD(mp, xs_xstrat_bytes, XFS_FSB_TO_B(mp, bma.length));
 	XFS_STATS_INC(mp, xs_xstrat_quick);
 
+	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION) &&
+	    XFS_FSB_TO_B(mp, bma.got.br_startoff) >=
+		    xfs_fsverity_metadata_offset(ip))
+		flags |= IOMAP_F_FSVERITY;
+
 	ASSERT(!isnullstartblock(bma.got.br_startblock));
 	xfs_bmbt_to_iomap(ip, iomap, &bma.got, 0, flags,
 				xfs_iomap_inode_sequence(ip, flags));
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index c30e688cfc9f..19640e4e3ed1 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -23,6 +23,7 @@
 #include "xfs_ioend.h"
 #include "xfs_zone_alloc.h"
 #include "xfs_rtgroup.h"
+#include "xfs_fsverity.h"
 
 struct xfs_writepage_ctx {
 	struct iomap_writepage_ctx ctx;
@@ -172,12 +173,16 @@ xfs_map_blocks(
 	int			retries = 0;
 	int			error = 0;
 	unsigned int		*seq;
+	unsigned int		iomap_flags = 0;
 
 	if (xfs_is_shutdown(mp))
 		return -EIO;
 
 	XFS_ERRORTAG_DELAY(mp, XFS_ERRTAG_WB_DELAY_MS);
 
+	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
+		iomap_flags |= IOMAP_F_FSVERITY;
+
 	/*
 	 * COW fork blocks can overlap data fork blocks even if the blocks
 	 * aren't shared.  COW I/O always takes precedent, so we must always
@@ -265,7 +270,8 @@ xfs_map_blocks(
 	    isnullstartblock(imap.br_startblock))
 		goto allocate_blocks;
 
-	xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0, 0, XFS_WPC(wpc)->data_seq);
+	xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0, iomap_flags,
+			  XFS_WPC(wpc)->data_seq);
 	trace_xfs_map_blocks_found(ip, offset, count, whichfork, &imap);
 	return 0;
 allocate_blocks:
@@ -412,12 +418,16 @@ xfs_zoned_map_blocks(
 	xfs_filblks_t		count_fsb;
 	struct xfs_bmbt_irec	imap, del;
 	struct xfs_iext_cursor	icur;
+	u16			iomap_flags = 0;
 
 	if (xfs_is_shutdown(mp))
 		return -EIO;
 
 	XFS_ERRORTAG_DELAY(mp, XFS_ERRTAG_WB_DELAY_MS);
 
+	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
+		iomap_flags |= IOMAP_F_FSVERITY;
+
 	/*
 	 * All dirty data must be covered by delalloc extents.  But truncate can
 	 * remove delalloc extents underneath us or reduce their size.
@@ -441,7 +451,7 @@ xfs_zoned_map_blocks(
 		imap.br_startblock = HOLESTARTBLOCK;
 		imap.br_state = XFS_EXT_NORM;
 		xfs_iunlock(ip, XFS_ILOCK_EXCL);
-		xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0, 0, 0);
+		xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0, iomap_flags, 0);
 		return 0;
 	}
 	end_fsb = min(end_fsb, imap.br_startoff + imap.br_blockcount);
@@ -454,7 +464,7 @@ xfs_zoned_map_blocks(
 	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 
 	xfs_iomap_set_anon_write(ip, &wpc->iomap, offset,
-			XFS_FSB_TO_B(mp, count_fsb));
+			XFS_FSB_TO_B(mp, count_fsb), iomap_flags);
 	trace_xfs_zoned_map_blocks(ip, offset, wpc->iomap.length);
 	return 0;
 }
@@ -507,6 +517,22 @@ static const struct iomap_writeback_ops xfs_zoned_writeback_ops = {
 	.writeback_submit	= xfs_zoned_writeback_submit,
 };
 
+static int
+xfs_iomap_writepages(
+	struct xfs_inode		*ip,
+	struct iomap_writepage_ctx	*ctx)
+{
+	/*
+	 * Writeback does not work for folios past EOF, let it know that
+	 * I/O happens for fsverity metadata and this restriction need
+	 * to be skipped
+	 */
+	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
+		ctx->iomap.flags |= IOMAP_F_FSVERITY;
+
+	return iomap_writepages(ctx);
+}
+
 STATIC int
 xfs_vm_writepages(
 	struct address_space	*mapping,
@@ -526,7 +552,7 @@ xfs_vm_writepages(
 		};
 		int				error;
 
-		error = iomap_writepages(&xc.ctx);
+		error = xfs_iomap_writepages(ip, &xc.ctx);
 		if (xc.open_zone)
 			xfs_open_zone_put(xc.open_zone);
 		return error;
@@ -539,7 +565,7 @@ xfs_vm_writepages(
 			},
 		};
 
-		return iomap_writepages(&wpc.ctx);
+		return xfs_iomap_writepages(ip, &wpc.ctx);
 	}
 }
 
diff --git a/fs/xfs/xfs_fsverity.c b/fs/xfs/xfs_fsverity.c
new file mode 100644
index 000000000000..e30021c22683
--- /dev/null
+++ b/fs/xfs/xfs_fsverity.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Red Hat, Inc.
+ */
+#include "xfs_platform.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_fsverity.h"
+#include <linux/fsverity.h>
+#include <linux/iomap.h>
+
+loff_t
+xfs_fsverity_metadata_offset(
+	const struct xfs_inode	*ip)
+{
+	return round_up(i_size_read(VFS_IC(ip)), XFS_FSVERITY_START_ALIGN);
+}
diff --git a/fs/xfs/xfs_fsverity.h b/fs/xfs/xfs_fsverity.h
new file mode 100644
index 000000000000..c2ab5af89370
--- /dev/null
+++ b/fs/xfs/xfs_fsverity.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Red Hat, Inc.
+ */
+#ifndef __XFS_FSVERITY_H__
+#define __XFS_FSVERITY_H__
+
+#include "xfs_platform.h"
+
+#ifdef CONFIG_FS_VERITY
+loff_t xfs_fsverity_metadata_offset(const struct xfs_inode *ip);
+#else
+static inline loff_t xfs_fsverity_metadata_offset(const struct xfs_inode *ip)
+{
+	WARN_ON_ONCE(1);
+	return ULLONG_MAX;
+}
+#endif	/* CONFIG_FS_VERITY */
+
+#endif	/* __XFS_FSVERITY_H__ */
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index 1602027cd0aa..af5ced6997e8 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -419,6 +419,12 @@ static inline bool xfs_inode_can_sw_atomic_write(const struct xfs_inode *ip)
  */
 #define XFS_IREMAPPING		(1U << 15)
 
+/*
+ * fs-verity's Merkle tree is under construction. The file is read-only, the
+ * only writes happening are for the fsverity metadata.
+ */
+#define XFS_VERITY_CONSTRUCTION	(1U << 16)
+
 /* All inode state flags related to inode reclaim. */
 #define XFS_ALL_IRECLAIM_FLAGS	(XFS_IRECLAIMABLE | \
 				 XFS_IRECLAIM | \
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 7c6238fed61e..fa11d830dfc0 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -32,6 +32,8 @@
 #include "xfs_rtbitmap.h"
 #include "xfs_icache.h"
 #include "xfs_zone_alloc.h"
+#include "xfs_fsverity.h"
+#include <linux/fsverity.h>
 
 #define XFS_ALLOC_ALIGN(mp, off) \
 	(((off) >> mp->m_allocsize_log) << mp->m_allocsize_log)
@@ -883,6 +885,9 @@ xfs_direct_write_iomap_begin(
 	if (flags & IOMAP_ATOMIC)
 		iomap_flags |= IOMAP_F_ATOMIC_BIO;
 
+	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
+		iomap_flags |= IOMAP_F_FSVERITY;
+
 	/*
 	 * COW writes may allocate delalloc space or convert unwritten COW
 	 * extents, so we need to make sure to take the lock exclusively here.
@@ -1083,7 +1088,7 @@ xfs_zoned_direct_write_iomap_begin(
 			return error;
 	}
 
-	xfs_iomap_set_anon_write(ip, iomap, offset, length);
+	xfs_iomap_set_anon_write(ip, iomap, offset, length, 0);
 	return 0;
 }
 
@@ -1595,7 +1600,8 @@ xfs_zoned_buffered_write_iomap_begin(
 	loff_t			count,
 	unsigned		flags,
 	struct iomap		*iomap,
-	struct iomap		*srcmap)
+	struct iomap		*srcmap,
+	u16			iomap_flags)
 {
 	struct iomap_iter	*iter =
 		container_of(iomap, struct iomap_iter, iomap);
@@ -1605,7 +1611,6 @@ xfs_zoned_buffered_write_iomap_begin(
 	struct xfs_mount	*mp = ip->i_mount;
 	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
 	xfs_fileoff_t		end_fsb = xfs_iomap_end_fsb(mp, offset, count);
-	u16			iomap_flags = IOMAP_F_SHARED;
 	unsigned int		lockmode = XFS_ILOCK_EXCL;
 	xfs_filblks_t		count_fsb;
 	xfs_extlen_t		indlen;
@@ -1668,7 +1673,8 @@ xfs_zoned_buffered_write_iomap_begin(
 				smap.br_startoff + smap.br_blockcount);
 			xfs_trim_extent(&smap, offset_fsb,
 					end_fsb - offset_fsb);
-			error = xfs_bmbt_to_iomap(ip, srcmap, &smap, flags, 0,
+			error = xfs_bmbt_to_iomap(ip, srcmap, &smap, flags,
+					iomap_flags,
 					xfs_iomap_inode_sequence(ip, 0));
 			if (error)
 				goto out_unlock;
@@ -1678,6 +1684,8 @@ xfs_zoned_buffered_write_iomap_begin(
 	if (!ip->i_cowfp)
 		xfs_ifork_init_cow(ip);
 
+	iomap_flags |= IOMAP_F_SHARED;
+
 	if (!xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &got))
 		got.br_startoff = end_fsb;
 	if (got.br_startoff <= offset_fsb) {
@@ -1809,9 +1817,12 @@ xfs_buffered_write_iomap_begin(
 	if (xfs_is_shutdown(mp))
 		return -EIO;
 
+	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
+		iomap_flags |= IOMAP_F_FSVERITY;
+
 	if (xfs_is_zoned_inode(ip))
 		return xfs_zoned_buffered_write_iomap_begin(inode, offset,
-				count, flags, iomap, srcmap);
+				count, flags, iomap, srcmap, iomap_flags);
 
 	/* we can't use delayed allocations when using extent size hints */
 	if (xfs_get_extsz_hint(ip))
@@ -2199,12 +2210,17 @@ xfs_read_iomap_begin(
 	bool			shared = false;
 	unsigned int		lockmode = XFS_ILOCK_SHARED;
 	u64			seq;
+	unsigned int		iomap_flags = 0;
 
 	ASSERT(!(flags & (IOMAP_WRITE | IOMAP_ZERO)));
 
 	if (xfs_is_shutdown(mp))
 		return -EIO;
 
+	if (fsverity_active(inode) &&
+	    (offset >= xfs_fsverity_metadata_offset(ip)))
+		iomap_flags |= IOMAP_F_FSVERITY;
+
 	error = xfs_ilock_for_iomap(ip, flags, &lockmode);
 	if (error)
 		return error;
@@ -2218,8 +2234,9 @@ xfs_read_iomap_begin(
 	if (error)
 		return error;
 	trace_xfs_iomap_found(ip, offset, length, XFS_DATA_FORK, &imap);
-	return xfs_bmbt_to_iomap(ip, iomap, &imap, flags,
-				 shared ? IOMAP_F_SHARED : 0, seq);
+	iomap_flags |= shared ? IOMAP_F_SHARED : 0;
+
+	return xfs_bmbt_to_iomap(ip, iomap, &imap, flags, iomap_flags, seq);
 }
 
 static DEFINE_IOMAP_ITER_NEXT(xfs_read_iomap_next, xfs_read_iomap_begin);
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
index f2520a9b3a13..d3ca0f2afc9f 100644
--- a/fs/xfs/xfs_iomap.h
+++ b/fs/xfs/xfs_iomap.h
@@ -34,13 +34,14 @@ xfs_iomap_set_anon_write(
 	struct xfs_inode		*ip,
 	struct iomap			*iomap,
 	loff_t				offset,
-	loff_t				length)
+	loff_t				length,
+	u16				iomap_flags)
 {
 	iomap->type = IOMAP_MAPPED;
 	iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev;
 	iomap->offset = offset;
 	iomap->length = length;
-	iomap->flags = IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY;
+	iomap->flags = iomap_flags | IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY;
 	if (bdev_has_integrity_csum(iomap->bdev))
 		iomap->flags |= IOMAP_F_INTEGRITY;
 }
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 12/21] xfs: use read ioend for fsverity data verification
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (10 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 11/21] xfs: handle fsverity I/O in write/read path Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:36   ` sashiko-bot
                     ` (2 more replies)
  2026-09-18 11:15 ` [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi() Andrey Albershteyn
                   ` (9 subsequent siblings)
  21 siblings, 3 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

Use read ioends for fsverity verification. Do not issue fsverity
metadata I/O through the same workqueue due to risk of a deadlock by a
filled workqueue.

Pass fsverity_info from iomap context down to the ioend as hashtable
lookups are expensive.

Add a simple helper to check that this is not fsverity metadata but file
data that needs verification.

Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/xfs_aops.c     | 13 +++++----
 fs/xfs/xfs_file.c     |  3 +-
 fs/xfs/xfs_fsverity.c | 11 ++++++++
 fs/xfs/xfs_fsverity.h | 14 ++++++++++
 fs/xfs/xfs_ioend.c    | 65 +++++++++++++++++++++++++++++++++++++++----
 fs/xfs/xfs_ioend.h    |  4 ++-
 fs/xfs/xfs_super.c    | 15 ++++++++++
 7 files changed, 112 insertions(+), 13 deletions(-)

diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 19640e4e3ed1..a36f840884b4 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -24,6 +24,7 @@
 #include "xfs_zone_alloc.h"
 #include "xfs_rtgroup.h"
 #include "xfs_fsverity.h"
+#include <linux/fsverity.h>
 
 struct xfs_writepage_ctx {
 	struct iomap_writepage_ctx ctx;
@@ -611,7 +612,7 @@ xfs_bio_submit_read(
 {
 	xfs_ioend_submit_read(iter->inode, ctx->read_ctx,
 			ctx->read_ctx_file_offset,
-			iomap_ioend_flags(&iter->iomap));
+			iomap_ioend_flags(&iter->iomap), ctx->vi);
 	ctx->read_ctx = NULL;
 }
 
@@ -623,11 +624,13 @@ static const struct iomap_read_ops xfs_iomap_read_ops = {
 
 static inline const struct iomap_read_ops *
 xfs_get_iomap_read_ops(
-	const struct address_space	*mapping)
+	const struct address_space	*mapping,
+	loff_t				pos)
 {
 	struct xfs_inode		*ip = XFS_I(mapping->host);
 
-	if (bdev_has_integrity_csum(xfs_inode_buftarg(ip)->bt_bdev))
+	if (bdev_has_integrity_csum(xfs_inode_buftarg(ip)->bt_bdev) ||
+	    xfs_fsverity_is_file_data(ip, pos))
 		return &xfs_iomap_read_ops;
 	return &iomap_bio_read_ops;
 }
@@ -639,7 +642,7 @@ xfs_vm_read_folio(
 {
 	struct iomap_read_folio_ctx	ctx = { .cur_folio = folio };
 
-	ctx.ops = xfs_get_iomap_read_ops(folio->mapping);
+	ctx.ops = xfs_get_iomap_read_ops(folio->mapping, folio_pos(folio));
 	iomap_read_folio(&xfs_read_iomap_ops, &ctx, NULL);
 	return 0;
 }
@@ -650,7 +653,7 @@ xfs_vm_readahead(
 {
 	struct iomap_read_folio_ctx	ctx = { .rac = rac };
 
-	ctx.ops = xfs_get_iomap_read_ops(rac->mapping),
+	ctx.ops = xfs_get_iomap_read_ops(rac->mapping, readahead_pos(rac));
 	iomap_readahead(&xfs_read_iomap_ops, &ctx, NULL);
 }
 
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 07abf6e8783f..578ca6fb8292 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -225,7 +225,8 @@ xfs_dio_read_bounce_submit_io(
 	loff_t			file_offset)
 {
 	xfs_ioend_submit_read(iter->inode, bio, file_offset,
-			iomap_ioend_flags(&iter->iomap) | IOMAP_IOEND_DIRECT);
+			iomap_ioend_flags(&iter->iomap) | IOMAP_IOEND_DIRECT,
+			NULL);
 }
 
 static const struct iomap_dio_ops xfs_dio_read_bounce_ops = {
diff --git a/fs/xfs/xfs_fsverity.c b/fs/xfs/xfs_fsverity.c
index e30021c22683..525855fdd30d 100644
--- a/fs/xfs/xfs_fsverity.c
+++ b/fs/xfs/xfs_fsverity.c
@@ -14,9 +14,20 @@
 #include <linux/fsverity.h>
 #include <linux/iomap.h>
 
+struct kmem_cache *xfs_fsverity_ioend_cache;
+
 loff_t
 xfs_fsverity_metadata_offset(
 	const struct xfs_inode	*ip)
 {
 	return round_up(i_size_read(VFS_IC(ip)), XFS_FSVERITY_START_ALIGN);
 }
+
+bool
+xfs_fsverity_is_file_data(
+	const struct xfs_inode	*ip,
+	loff_t			offset)
+{
+	return fsverity_active(VFS_IC(ip)) &&
+			offset < xfs_fsverity_metadata_offset(ip);
+}
diff --git a/fs/xfs/xfs_fsverity.h b/fs/xfs/xfs_fsverity.h
index c2ab5af89370..836e3a4eb194 100644
--- a/fs/xfs/xfs_fsverity.h
+++ b/fs/xfs/xfs_fsverity.h
@@ -6,15 +6,29 @@
 #define __XFS_FSVERITY_H__
 
 #include "xfs_platform.h"
+#include <linux/iomap.h>
 
 #ifdef CONFIG_FS_VERITY
 loff_t xfs_fsverity_metadata_offset(const struct xfs_inode *ip);
+bool xfs_fsverity_is_file_data(const struct xfs_inode *ip, loff_t offset);
 #else
 static inline loff_t xfs_fsverity_metadata_offset(const struct xfs_inode *ip)
 {
 	WARN_ON_ONCE(1);
 	return ULLONG_MAX;
 }
+static inline bool xfs_fsverity_is_file_data(const struct xfs_inode *ip,
+					    loff_t offset)
+{
+	return false;
+}
 #endif	/* CONFIG_FS_VERITY */
 
+struct xfs_fsverity_ioend {
+	struct iomap_ioend	*ioend;
+	struct work_struct	work;
+};
+
+extern struct kmem_cache *xfs_fsverity_ioend_cache;
+
 #endif	/* __XFS_FSVERITY_H__ */
diff --git a/fs/xfs/xfs_ioend.c b/fs/xfs/xfs_ioend.c
index e70be5b86f0b..2f46855b052f 100644
--- a/fs/xfs/xfs_ioend.c
+++ b/fs/xfs/xfs_ioend.c
@@ -18,7 +18,26 @@
 #include "xfs_ioend.h"
 #include "xfs_error.h"
 #include "xfs_errortag.h"
+#include "xfs_fsverity.h"
 #include <linux/bio-integrity.h>
+#include <linux/fsverity.h>
+
+static void
+xfs_end_fsverity_io_read(
+	struct work_struct		*work)
+{
+	struct xfs_fsverity_ioend	*fsv_ioend =
+			container_of(work, struct xfs_fsverity_ioend, work);
+	struct iomap_ioend		*ioend = fsv_ioend->ioend;
+	struct bio			*bio = &ioend->io_bio;
+
+	kmem_cache_free(xfs_fsverity_ioend_cache, fsv_ioend);
+
+	if (!bio->bi_status)
+		fsverity_verify_bio(ioend->io_vi, bio);
+
+	iomap_finish_ioends(ioend, blk_status_to_errno(bio->bi_status));
+}
 
 static void
 xfs_dio_bounce_end_io(
@@ -93,12 +112,14 @@ xfs_read_bounce_and_resubmit(
 
 static void
 xfs_end_io_read(
-	struct bio		*bio)
+	struct bio			*bio)
 {
-	struct iomap_ioend	*ioend = iomap_ioend_from_bio(bio);
-	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
-	struct xfs_mount	*mp = ip->i_mount;
-	int			error = blk_status_to_errno(bio->bi_status);
+	struct iomap_ioend		*ioend = iomap_ioend_from_bio(bio);
+	struct xfs_inode		*ip = XFS_I(ioend->io_inode);
+	struct xfs_mount		*mp = ip->i_mount;
+	int				error =
+			blk_status_to_errno(bio->bi_status);
+	struct xfs_fsverity_ioend	*fsv_ioend;
 
 	if (!error && (ioend->io_flags & IOMAP_IOEND_INTEGRITY)) {
 		error = iomap_ioend_integrity_verify(ioend);
@@ -117,6 +138,36 @@ xfs_end_io_read(
 		}
 	}
 
+	/*
+	 * If we have fsverity and block device integrity attached to this bio,
+	 * we need to run fsverity verification of data folios from a separate
+	 * fsverity workqueue. This is necessary to avoid deadlocking due to
+	 * fsverity issuing more reads of fsverity metadata which would be
+	 * processed by the same worker in the BIO completion workqueue.
+	 *
+	 * Without block device integrity, fsverity metadata IO will not use
+	 * ioends for completion.
+	 */
+	if (IS_ENABLED(CONFIG_FS_VERITY) && !error && ioend->io_vi &&
+			xfs_fsverity_is_file_data(ip, ioend->io_offset)) {
+		if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) {
+			fsv_ioend = kmem_cache_zalloc(xfs_fsverity_ioend_cache,
+					GFP_KERNEL);
+			if (!fsv_ioend) {
+				iomap_finish_ioends(ioend, -ENOMEM);
+				return;
+			}
+			fsv_ioend->ioend = ioend;
+			INIT_WORK(&fsv_ioend->work, xfs_end_fsverity_io_read);
+
+			fsverity_enqueue_verify_work(&fsv_ioend->work);
+			return;
+		}
+
+		fsverity_verify_bio(ioend->io_vi, &ioend->io_bio);
+		error = blk_status_to_errno(ioend->io_bio.bi_status);
+	}
+
 	iomap_finish_ioends(ioend, error);
 }
 
@@ -125,13 +176,15 @@ xfs_ioend_submit_read(
 	struct inode		*inode,
 	struct bio		*bio,
 	loff_t			file_offset,
-	u16			ioend_flags)
+	u16			ioend_flags,
+	struct fsverity_info	*vi)
 {
 	struct xfs_inode	*ip = XFS_I(inode);
 	struct xfs_mount	*mp = ip->i_mount;
 	struct iomap_ioend	*ioend;
 
 	ioend = iomap_init_ioend(inode, bio, file_offset, ioend_flags);
+	ioend->io_vi = vi;
 	if ((ioend_flags & IOMAP_IOEND_DIRECT) &&
 	    READ_ONCE(mp->m_read_bounce) == XFS_READ_BOUNCE_ALWAYS) {
 		iomap_bounce_read(ioend, bdev_logical_block_size(bio->bi_bdev),
diff --git a/fs/xfs/xfs_ioend.h b/fs/xfs/xfs_ioend.h
index 7c2a1ea3e6ed..992c248a693a 100644
--- a/fs/xfs/xfs_ioend.h
+++ b/fs/xfs/xfs_ioend.h
@@ -2,6 +2,8 @@
 #ifndef __XFS_IOEND_H
 #define __XFS_IOEND_H
 
+#include <linux/fsverity.h>
+
 /*
  * Fast and loose check if this write could update the on-disk inode size.
  */
@@ -13,6 +15,6 @@ static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
 
 void xfs_end_bio(struct bio *bio);
 void xfs_ioend_submit_read(struct inode *inode, struct bio *bio,
-		loff_t file_offset, u16 ioend_flags);
+		loff_t file_offset, u16 ioend_flags, struct fsverity_info *vi);
 
 #endif /* __XFS_IOEND_H */
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index fce1d2905c94..aca50130625e 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -2516,8 +2516,20 @@ xfs_init_caches(void)
 	if (!xfs_parent_args_cache)
 		goto out_destroy_xmi_cache;
 
+#ifdef CONFIG_FS_VERITY
+	xfs_fsverity_ioend_cache = kmem_cache_create("xfs_fsverity_ioend",
+					     sizeof(struct xfs_fsverity_ioend),
+					     0, 0, NULL);
+	if (!xfs_fsverity_ioend_cache)
+		goto out_destroy_fsverity_ioend_cache;
+#endif
+
 	return 0;
 
+#ifdef CONFIG_FS_VERITY
+ out_destroy_fsverity_ioend_cache:
+	kmem_cache_destroy(xfs_fsverity_ioend_cache);
+#endif
  out_destroy_xmi_cache:
 	kmem_cache_destroy(xfs_xmi_cache);
  out_destroy_xmd_cache:
@@ -2580,6 +2592,9 @@ xfs_destroy_caches(void)
 	 * destroy caches.
 	 */
 	rcu_barrier();
+#ifdef CONFIG_FS_VERITY
+	kmem_cache_destroy(xfs_fsverity_ioend_cache);
+#endif
 	kmem_cache_destroy(xfs_parent_args_cache);
 	kmem_cache_destroy(xfs_xmd_cache);
 	kmem_cache_destroy(xfs_xmi_cache);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi()
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (11 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 12/21] xfs: use read ioend for fsverity data verification Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:32   ` sashiko-bot
  2026-09-22  4:34   ` Darrick J. Wong
  2026-09-18 11:15 ` [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes Andrey Albershteyn
                   ` (8 subsequent siblings)
  21 siblings, 2 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

The __xfs_bunmapi() is used to remove all extents past EOF. Add
XFS_BMAPI_UNWRITTEN to remove only unwritten extents, while leaving
written ones in place. This will be used in following patch to clean up
unwritten extents on fsverity inodes.

Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/libxfs/xfs_bmap.c | 6 +++++-
 fs/xfs/libxfs/xfs_bmap.h | 6 +++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index cc48f6e20e80..7ba76017ae56 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -5280,6 +5280,10 @@ __xfs_bunmapi(
 		if (del.br_startoff + del.br_blockcount > end + 1)
 			del.br_blockcount = end + 1 - del.br_startoff;
 
+		if ((flags & XFS_BMAPI_UNWRITTEN) &&
+				del.br_state != XFS_EXT_UNWRITTEN)
+			goto skip;
+
 		if (!isrt || (flags & XFS_BMAPI_REMAP))
 			goto delete;
 
@@ -5414,7 +5418,7 @@ __xfs_bunmapi(
 			if (error)
 				goto error0;
 		}
-
+skip:
 		end = del.br_startoff - 1;
 nodelete:
 		/*
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index d5f2729305fa..0f36431d9936 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -90,6 +90,9 @@ struct xfs_bmalloca {
 /* Try to align allocations to the extent size hint */
 #define XFS_BMAPI_EXTSZALIGN	(1u << 11)
 
+/* Process unwritten extents only. Used for unmapping */
+#define XFS_BMAPI_UNWRITTEN	(1u << 12)
+
 #define XFS_BMAPI_FLAGS \
 	{ XFS_BMAPI_ENTIRE,	"ENTIRE" }, \
 	{ XFS_BMAPI_METADATA,	"METADATA" }, \
@@ -102,7 +105,8 @@ struct xfs_bmalloca {
 	{ XFS_BMAPI_COWFORK,	"COWFORK" }, \
 	{ XFS_BMAPI_NODISCARD,	"NODISCARD" }, \
 	{ XFS_BMAPI_NORMAP,	"NORMAP" },\
-	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }
+	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }, \
+	{ XFS_BMAPI_UNWRITTEN,	"UNWRITTEN" }
 
 
 static inline int xfs_bmapi_aflag(int w)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (12 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi() Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:41   ` sashiko-bot
  2026-09-22  4:37   ` Darrick J. Wong
  2026-09-18 11:15 ` [PATCH v16 15/21] xfs: add fs-verity support Andrey Albershteyn
                   ` (7 subsequent siblings)
  21 siblings, 2 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

xfs_free_eofblocks() removes all extents past EOF unless the
XFS_DIFLAG_PREALLOC or XFS_DIFLAG_APPEND flags are set. This is
undesired for fsverity as it stores metadata beyond EOF. However, while
merkle tree is being built, delayed preallocation and unwritten extents
are used. After metadata construction is done, fsverity inodes becomes
read-only and won't be changed anymore, none of these unwritten extents
or preallocations in post EOF region will be used.

Let xfs_free_eofblocks() be called on fsverity inode as usual to remove
anything which is not written extent. However, inodes which are
undergoing merkle tree construction need to be skipped in case reclaim
takes place.

Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/xfs_bmap_util.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 268d159339d0..7fd951992557 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -31,6 +31,7 @@
 #include "xfs_rtbitmap.h"
 #include "xfs_rtgroup.h"
 #include "xfs_zone_alloc.h"
+#include <linux/fsverity.h>
 
 /* Kernel only BMAP related definitions and functions */
 
@@ -553,6 +554,13 @@ xfs_can_free_eofblocks(
 	if (last_fsb <= end_fsb)
 		return false;
 
+	/*
+	 * Don't clean fsverity inodes which have merkle tree being built, the
+	 * merkle tree is written beyond EOF
+	 */
+	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
+		return false;
+
 	/*
 	 * Check if there is an post-EOF extent to free.  If there are any
 	 * delalloc blocks attached to the inode (data fork delalloc
@@ -579,6 +587,9 @@ xfs_free_eofblocks(
 	struct xfs_trans	*tp;
 	struct xfs_mount	*mp = ip->i_mount;
 	int			error;
+	int			bmapi_flags = XFS_BMAPI_NODISCARD;
+	bool			has_verity =
+			ip->i_diflags2 & XFS_DIFLAG2_VERITY;
 
 	/* Attach the dquots to the inode up front. */
 	error = xfs_qm_dqattach(ip);
@@ -593,15 +604,20 @@ xfs_free_eofblocks(
 	 *
 	 * Note that this means we also leave speculative preallocations in
 	 * place for preallocated files.
+	 *
+	 * Clean up delalloc reservations for fsverity too as those won't be
+	 * used
 	 */
-	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) {
+	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND) ||
+			has_verity) {
 		if (ip->i_delayed_blks) {
 			xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK,
 				round_up(XFS_ISIZE(ip), mp->m_sb.sb_blocksize),
 				LLONG_MAX, NULL);
 		}
 		xfs_inode_clear_eofblocks_tag(ip);
-		return 0;
+		if (!has_verity)
+			return 0;
 	}
 
 	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
@@ -613,6 +629,9 @@ xfs_free_eofblocks(
 	xfs_ilock(ip, XFS_ILOCK_EXCL);
 	xfs_trans_ijoin(tp, ip, 0);
 
+	if (has_verity)
+		bmapi_flags |= XFS_BMAPI_UNWRITTEN;
+
 	/*
 	 * Do not update the on-disk file size.  If we update the on-disk file
 	 * size and then the system crashes before the contents of the file are
@@ -620,7 +639,7 @@ xfs_free_eofblocks(
 	 * bug).
 	 */
 	error = xfs_itruncate_extents_flags(&tp, ip, XFS_DATA_FORK,
-				XFS_ISIZE(ip), XFS_BMAPI_NODISCARD);
+				XFS_ISIZE(ip), bmapi_flags);
 	if (error)
 		goto err_cancel;
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (13 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:43   ` sashiko-bot
                     ` (2 more replies)
  2026-09-18 11:15 ` [PATCH v16 16/21] xfs: initialize fs-verity on file open Andrey Albershteyn
                   ` (6 subsequent siblings)
  21 siblings, 3 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

Add integration with fs-verity. XFS stores fs-verity descriptor and
Merkle tree in the inode data fork at first block aligned to 64k past
EOF.

The Merkle tree reading/writing is done through iomap interface. The
data itself is read to the inode's page cache. When XFS reads from this
region iomap doesn't call into fsverity to verify it against Merkle
tree. For data, verification is done at ioend completion in a workqueue.

When fs-verity is enabled on an inode, the XFS_IVERITY_CONSTRUCTION
flag is set meaning that the Merkle tree is being build. The
initialization ends with storing of verity descriptor and setting
inode on-disk flag (XFS_DIFLAG2_VERITY). Lastly, the
XFS_IVERITY_CONSTRUCTION is dropped and I_VERITY is set on inode.

The descriptor is stored in a new block aligned to 64k after the last
Merkle tree block. The size of the descriptor is stored at the end of
the last descriptor block (descriptor can be multiple blocks).

Pro-actively remove any unwritten extents as we use last extent to
locate descriptor.

Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_fsverity.c | 363 +++++++++++++++++++++++++++++++++++++++++-
 fs/xfs/xfs_fsverity.h |   2 +
 fs/xfs/xfs_message.c  |   4 +
 fs/xfs/xfs_message.h  |   1 +
 fs/xfs/xfs_mount.h    |   2 +
 fs/xfs/xfs_super.c    |   7 +
 6 files changed, 378 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/xfs_fsverity.c b/fs/xfs/xfs_fsverity.c
index 525855fdd30d..e62c9a5d0e1d 100644
--- a/fs/xfs/xfs_fsverity.c
+++ b/fs/xfs/xfs_fsverity.c
@@ -6,13 +6,22 @@
 #include "xfs_fs.h"
 #include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log_format.h"
 #include "xfs_trans_resv.h"
 #include "xfs_mount.h"
 #include "xfs_inode.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_trace.h"
+#include "xfs_quota.h"
 #include "xfs_fsverity.h"
+#include "xfs_iomap.h"
+#include "xfs_error.h"
+#include "xfs_health.h"
+#include "xfs_bmap_util.h"
+#include "xfs_icache.h"
 #include <linux/fsverity.h>
 #include <linux/iomap.h>
+#include <linux/pagemap.h>
 
 struct kmem_cache *xfs_fsverity_ioend_cache;
 
@@ -31,3 +40,355 @@ xfs_fsverity_is_file_data(
 	return fsverity_active(VFS_IC(ip)) &&
 			offset < xfs_fsverity_metadata_offset(ip);
 }
+
+/*
+ * Retrieve the verity descriptor.
+ */
+static int
+xfs_fsverity_get_descriptor(
+	struct inode		*inode,
+	void			*buf,
+	size_t			buf_size)
+{
+	struct xfs_inode	*ip = XFS_I(inode);
+	struct xfs_mount	*mp = ip->i_mount;
+	__be32			d_desc_size;
+	u32			desc_size;
+	u64			desc_size_pos;
+	int			error;
+	u64			desc_pos;
+	struct xfs_bmbt_irec	rec;
+	int			is_empty;
+	uint32_t		blocksize = i_blocksize(VFS_I(ip));
+	xfs_fileoff_t		last_block_offset;
+
+	ASSERT(inode->i_flags & S_VERITY);
+	xfs_ilock(ip, XFS_ILOCK_SHARED);
+	error = xfs_bmap_last_extent(NULL, ip, XFS_DATA_FORK, &rec, &is_empty);
+	xfs_iunlock(ip, XFS_ILOCK_SHARED);
+	if (error)
+		return error;
+
+	if (is_empty)
+		return -ENODATA;
+
+	last_block_offset =
+		XFS_FSB_TO_B(mp, rec.br_startoff + rec.br_blockcount);
+	if (last_block_offset <= xfs_fsverity_metadata_offset(ip))
+		return -ENODATA;
+
+	desc_size_pos = last_block_offset - sizeof(__be32);
+	error = fsverity_pagecache_read(inode, (char *)&d_desc_size,
+			sizeof(d_desc_size), desc_size_pos);
+	if (error)
+		return error;
+
+	desc_size = be32_to_cpu(d_desc_size);
+	if (XFS_IS_CORRUPT(mp, desc_size > FS_VERITY_MAX_DESCRIPTOR_SIZE))
+		return -ERANGE;
+	if (XFS_IS_CORRUPT(mp, desc_size > desc_size_pos))
+		return -ERANGE;
+
+	if (!buf_size)
+		return desc_size;
+
+	if (XFS_IS_CORRUPT(mp, desc_size > buf_size))
+		return -ERANGE;
+
+	desc_pos = round_down(desc_size_pos - desc_size, blocksize);
+	if (desc_pos < xfs_fsverity_metadata_offset(ip))
+		return -ERANGE;
+
+	error = fsverity_pagecache_read(inode, buf, desc_size, desc_pos);
+	if (error)
+		return error;
+
+	return desc_size;
+}
+
+static int
+xfs_fsverity_write_descriptor(
+	struct file		*file,
+	const void		*desc,
+	u32			desc_size,
+	u64			merkle_tree_size)
+{
+	int			error;
+	struct inode		*inode = file_inode(file);
+	struct xfs_inode	*ip = XFS_I(inode);
+	unsigned int		blksize = ip->i_mount->m_sb.sb_blocksize;
+	u64			tree_last_block =
+			xfs_fsverity_metadata_offset(ip) + merkle_tree_size;
+	u64			desc_pos =
+			round_up(tree_last_block, XFS_FSVERITY_START_ALIGN);
+	u64			desc_end = desc_pos + desc_size;
+	__be32			desc_size_disk = cpu_to_be32(desc_size);
+	u64			desc_size_pos =
+			round_up(desc_end + sizeof(desc_size_disk), blksize) -
+			sizeof(desc_size_disk);
+
+	error = iomap_fsverity_write(file, desc_size_pos, sizeof(__be32),
+			(const void *)&desc_size_disk,
+			&xfs_buffered_write_iomap_ops,
+			&xfs_iomap_write_ops);
+	if (error)
+		return error;
+
+	return iomap_fsverity_write(file, desc_pos, desc_size, desc,
+			&xfs_buffered_write_iomap_ops,
+			&xfs_iomap_write_ops);
+}
+
+/*
+ * Try to remove all the fsverity metadata after a failed enablement.
+ */
+static int
+xfs_fsverity_delete_metadata(
+	struct xfs_inode	*ip)
+{
+	struct xfs_trans	*tp;
+	struct xfs_mount	*mp = ip->i_mount;
+	int			error;
+
+	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
+	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
+	if (error) {
+		xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);
+		return error;
+	}
+
+	xfs_ilock(ip, XFS_ILOCK_EXCL);
+	xfs_trans_ijoin(tp, ip, 0);
+
+	truncate_inode_pages(VFS_I(ip)->i_mapping, XFS_ISIZE(ip));
+
+	/*
+	 * We remove post EOF data, no need to update i_size as fsverity
+	 * didn't move i_size in the first place
+	 */
+	error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, XFS_ISIZE(ip));
+	if (error)
+		goto err_cancel;
+
+	error = xfs_trans_commit(tp);
+	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_ILOCK_EXCL);
+	return error;
+err_cancel:
+	xfs_trans_cancel(tp);
+	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_ILOCK_EXCL);
+	return error;
+}
+
+
+/*
+ * Prepare to enable fsverity by clearing old metadata.
+ */
+static int
+xfs_fsverity_begin_enable(
+	struct file		*filp)
+{
+	struct inode		*inode = file_inode(filp);
+	struct xfs_inode	*ip = XFS_I(inode);
+	int			error;
+
+	xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
+
+	if (IS_DAX(inode) || ip->i_diflags2 & XFS_DIFLAG2_DAX)
+		return -EINVAL;
+
+	if (inode->i_size > XFS_FSVERITY_LARGEST_FILE)
+		return -EFBIG;
+
+	/*
+	 * Flush pagecache before building Merkle tree. Inode is locked and no
+	 * further writes will happen to the file except fsverity metadata
+	 */
+	error = filemap_write_and_wait(inode->i_mapping);
+	if (error)
+		return error;
+
+	if (xfs_iflags_test_and_set(ip, XFS_VERITY_CONSTRUCTION))
+		return -EBUSY;
+
+	error = xfs_qm_dqattach(ip);
+	if (error)
+		goto out_clear;
+
+	error = xfs_fsverity_delete_metadata(ip);
+	if (error)
+		goto out_clear;
+	return error;
+out_clear:
+	xfs_iflags_clear(ip, XFS_VERITY_CONSTRUCTION);
+	return error;
+}
+
+/*
+ * Complete (or fail) the process of enabling fsverity.
+ */
+static int
+xfs_fsverity_end_enable(
+	struct file		*file,
+	const void		*desc,
+	size_t			desc_size,
+	u64			merkle_tree_size)
+{
+	struct inode		*inode = file_inode(file);
+	struct xfs_inode	*ip = XFS_I(inode);
+	struct xfs_mount	*mp = ip->i_mount;
+	struct xfs_trans	*tp;
+	int			error = 0;
+	loff_t			range_start = xfs_fsverity_metadata_offset(ip);
+
+	xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
+
+	/* fs-verity failed, just cleanup */
+	if (desc == NULL) {
+		error = xfs_fsverity_delete_metadata(ip);
+		goto out;
+	}
+
+	error = xfs_fsverity_write_descriptor(file, desc, desc_size,
+			merkle_tree_size);
+	if (error)
+		goto out;
+
+	/*
+	 * Wait for Merkle tree get written to disk before setting on-disk inode
+	 * flag and clearing XFS_VERITY_CONSTRUCTION
+	 */
+	error = filemap_write_and_wait_range(inode->i_mapping, range_start,
+			LLONG_MAX);
+	if (error)
+		goto out;
+
+	/*
+	 * Set fsverity inode flag
+	 */
+	error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_ichange,
+			0, 0, false, &tp);
+	if (error)
+		goto out;
+
+	/*
+	 * Ensure that we've persisted the verity information before we enable
+	 * it on the inode and tell the caller we have sealed the inode.
+	 */
+	ip->i_diflags2 |= XFS_DIFLAG2_VERITY;
+
+	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+	xfs_trans_set_sync(tp);
+
+	error = xfs_trans_commit(tp);
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
+	if (error)
+		goto out;
+
+	inode_set_flags(inode, S_VERITY, S_VERITY);
+
+	/*
+	 * Proactively remove unwritten extents left by COW preallocations and
+	 * write preallocation in the merkle tree holes and past descriptor,
+	 * and any delayed preallocations
+	 */
+	error = xfs_free_eofblocks(ip);
+out:
+	if (error) {
+		int	error2;
+
+		error2 = xfs_fsverity_delete_metadata(ip);
+		if (error2)
+			xfs_alert(ip->i_mount,
+"ino 0x%llx failed to clean up new fsverity metadata, err %d",
+					I_INO(ip), error2);
+	}
+
+	xfs_iflags_clear(ip, XFS_VERITY_CONSTRUCTION);
+	return error;
+}
+
+/*
+ * Retrieve a merkle tree block.
+ */
+static struct page *
+xfs_fsverity_read_merkle(
+	struct inode		*inode,
+	pgoff_t			index)
+{
+	index += xfs_fsverity_metadata_offset(XFS_I(inode)) >> PAGE_SHIFT;
+
+	return generic_read_merkle_tree_page(inode, index);
+}
+
+/*
+ * Retrieve a merkle tree block.
+ */
+static void
+xfs_fsverity_readahead_merkle_tree(
+	struct inode		*inode,
+	pgoff_t			index,
+	unsigned long		nr_pages)
+{
+	index += xfs_fsverity_metadata_offset(XFS_I(inode)) >> PAGE_SHIFT;
+
+	generic_readahead_merkle_tree(inode, index, nr_pages);
+}
+
+/*
+ * Write a merkle tree block.
+ */
+static int
+xfs_fsverity_write_merkle(
+	struct file		*file,
+	const void		*buf,
+	u64			pos,
+	unsigned int		size,
+	const u8		*zero_digest,
+	unsigned int		digest_size)
+{
+	struct inode		*inode = file_inode(file);
+	struct xfs_inode	*ip = XFS_I(inode);
+	loff_t			position = pos +
+		xfs_fsverity_metadata_offset(ip);
+
+	if (position + size > inode->i_sb->s_maxbytes)
+		return -EFBIG;
+
+	/*
+	 * If this is a block full of hashes of zeroed blocks, don't bother
+	 * storing the block. We can synthesize them later.
+	 *
+	 * However, do this only in case Merkle tree block == fs block size.
+	 * Iomap synthesizes these blocks based on holes in the merkle tree. We
+	 * won't be able to tell if something need to be synthesizes for the
+	 * range in the fs block. For example, for 4k filesystem block
+	 *
+	 *	[ 1k | zero hashes | zero hashes | 1k ]
+	 *
+	 * Iomap won't know about these empty blocks.
+	 */
+	if (size == ip->i_mount->m_sb.sb_blocksize &&
+			/*
+			 * First digest is zero_digest
+			 */
+			memcmp(buf, zero_digest, digest_size) == 0 &&
+			/*
+			 * Every digest is same as previous, thus all are
+			 * zero_digest
+			 */
+			memcmp(buf + digest_size, buf, size - digest_size) == 0)
+		return 0;
+
+	return iomap_fsverity_write(file, position, size, buf,
+			&xfs_buffered_write_iomap_ops,
+			&xfs_iomap_write_ops);
+}
+
+const struct fsverity_operations xfs_fsverity_ops = {
+	.begin_enable_verity		= xfs_fsverity_begin_enable,
+	.end_enable_verity		= xfs_fsverity_end_enable,
+	.get_verity_descriptor		= xfs_fsverity_get_descriptor,
+	.read_merkle_tree_page		= xfs_fsverity_read_merkle,
+	.readahead_merkle_tree		= xfs_fsverity_readahead_merkle_tree,
+	.write_merkle_tree_block	= xfs_fsverity_write_merkle,
+};
diff --git a/fs/xfs/xfs_fsverity.h b/fs/xfs/xfs_fsverity.h
index 836e3a4eb194..9290dbf576f8 100644
--- a/fs/xfs/xfs_fsverity.h
+++ b/fs/xfs/xfs_fsverity.h
@@ -7,8 +7,10 @@
 
 #include "xfs_platform.h"
 #include <linux/iomap.h>
+#include <linux/fsverity.h>
 
 #ifdef CONFIG_FS_VERITY
+extern const struct fsverity_operations xfs_fsverity_ops;
 loff_t xfs_fsverity_metadata_offset(const struct xfs_inode *ip);
 bool xfs_fsverity_is_file_data(const struct xfs_inode *ip, loff_t offset);
 #else
diff --git a/fs/xfs/xfs_message.c b/fs/xfs/xfs_message.c
index 0243e509a468..44b13d46049c 100644
--- a/fs/xfs/xfs_message.c
+++ b/fs/xfs/xfs_message.c
@@ -149,6 +149,10 @@ xfs_warn_experimental(
 			.opstate	= XFS_OPSTATE_WARNED_LARP,
 			.name		= "logged extended attributes",
 		},
+		[XFS_EXPERIMENTAL_FSVERITY] = {
+			.opstate	= XFS_OPSTATE_WARNED_FSVERITY,
+			.name		= "fsverity",
+		},
 	};
 	ASSERT(feat >= 0 && feat < XFS_EXPERIMENTAL_MAX);
 	BUILD_BUG_ON(ARRAY_SIZE(features) != XFS_EXPERIMENTAL_MAX);
diff --git a/fs/xfs/xfs_message.h b/fs/xfs/xfs_message.h
index 811b885f41c3..364fcc7e42b0 100644
--- a/fs/xfs/xfs_message.h
+++ b/fs/xfs/xfs_message.h
@@ -93,6 +93,7 @@ void xfs_buf_alert_ratelimited(struct xfs_buf *bp, const char *rlmsg,
 enum xfs_experimental_feat {
 	XFS_EXPERIMENTAL_SHRINK,
 	XFS_EXPERIMENTAL_LARP,
+	XFS_EXPERIMENTAL_FSVERITY,
 
 	XFS_EXPERIMENTAL_MAX,
 };
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 60cb54c1a5ec..a3074f060928 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -599,6 +599,8 @@ __XFS_HAS_FEAT(nouuid, NOUUID)
 #define XFS_OPSTATE_RESUMING_QUOTAON	18
 /* (Zoned) GC is in progress */
 #define XFS_OPSTATE_ZONEGC_RUNNING	20
+/* Kernel has logged a warning about fsverity support */
+#define XFS_OPSTATE_WARNED_FSVERITY	21
 
 #define __XFS_IS_OPSTATE(name, NAME) \
 static inline bool xfs_is_ ## name (struct xfs_mount *mp) \
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index aca50130625e..c5075474cd8c 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -30,6 +30,7 @@
 #include "xfs_filestream.h"
 #include "xfs_quota.h"
 #include "xfs_sysfs.h"
+#include "xfs_fsverity.h"
 #include "xfs_ondisk.h"
 #include "xfs_rmap_item.h"
 #include "xfs_refcount_item.h"
@@ -1736,6 +1737,9 @@ xfs_fs_fill_super(
 	sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ;
 #endif
 	sb->s_op = &xfs_super_operations;
+#ifdef CONFIG_FS_VERITY
+	sb->s_vop = &xfs_fsverity_ops;
+#endif
 
 	/*
 	 * Delay mount work if the debug hook is set. This is debug
@@ -1988,6 +1992,9 @@ xfs_fs_fill_super(
 	if (error)
 		goto out_filestream_unmount;
 
+	if (xfs_has_verity(mp))
+		xfs_warn_experimental(mp, XFS_EXPERIMENTAL_FSVERITY);
+
 	root = igrab(VFS_I(mp->m_rootip));
 	if (!root) {
 		error = -ENOENT;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 16/21] xfs: initialize fs-verity on file open
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (14 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 15/21] xfs: add fs-verity support Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:33   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 17/21] xfs: add fs-verity ioctls Andrey Albershteyn
                   ` (5 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

fs-verity will read and attach metadata (not the tree itself) from
a disk for those inodes which already have fs-verity enabled.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/xfs_file.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 578ca6fb8292..e13390539f04 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -1794,11 +1794,18 @@ xfs_file_open(
 	struct inode	*inode,
 	struct file	*file)
 {
+	int		error;
+
 	if (xfs_is_shutdown(XFS_M(inode->i_sb)))
 		return -EIO;
 	file->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT;
 	if (xfs_get_atomic_write_min(XFS_I(inode)) > 0)
 		file->f_mode |= FMODE_CAN_ATOMIC_WRITE;
+
+	error = fsverity_file_open(inode, file);
+	if (error)
+		return error;
+
 	return generic_file_open(inode, file);
 }
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 17/21] xfs: add fs-verity ioctls
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (15 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 16/21] xfs: initialize fs-verity on file open Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:31   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 18/21] xfs: advertise fs-verity being available on filesystem Andrey Albershteyn
                   ` (4 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

Add fs-verity ioctls to enable, dump metadata (descriptor and Merkle
tree pages) and obtain file's digest.

[djwong: remove unnecessary casting]

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/xfs_ioctl.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 96ca3e480cb9..ac4c36f381b8 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -49,6 +49,7 @@
 
 #include <linux/mount.h>
 #include <linux/fileattr.h>
+#include <linux/fsverity.h>
 
 /* Return 0 on success or positive error */
 int
@@ -1466,6 +1467,19 @@ xfs_file_ioctl(
 	case XFS_IOC_VERIFY_MEDIA:
 		return xfs_ioc_verify_media(filp, arg);
 
+	case FS_IOC_ENABLE_VERITY:
+		if (!xfs_has_verity(mp))
+			return -EOPNOTSUPP;
+		return fsverity_ioctl_enable(filp, arg);
+	case FS_IOC_MEASURE_VERITY:
+		if (!xfs_has_verity(mp))
+			return -EOPNOTSUPP;
+		return fsverity_ioctl_measure(filp, arg);
+	case FS_IOC_READ_VERITY_METADATA:
+		if (!xfs_has_verity(mp))
+			return -EOPNOTSUPP;
+		return fsverity_ioctl_read_metadata(filp, arg);
+
 	default:
 		return -ENOTTY;
 	}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 18/21] xfs: advertise fs-verity being available on filesystem
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (16 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 17/21] xfs: add fs-verity ioctls Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:30   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 19/21] xfs: check and repair the verity inode flag state Andrey Albershteyn
                   ` (3 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david,
	Andrey Albershteyn

From: "Darrick J. Wong" <djwong@kernel.org>

Advertise that this filesystem supports fsverity.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/libxfs/xfs_fs.h | 1 +
 fs/xfs/libxfs/xfs_sb.c | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 185f09f327c0..c80133784419 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -250,6 +250,7 @@ typedef struct xfs_fsop_resblks {
 #define XFS_FSOP_GEOM_FLAGS_PARENT	(1 << 25) /* linux parent pointers */
 #define XFS_FSOP_GEOM_FLAGS_METADIR	(1 << 26) /* metadata directories */
 #define XFS_FSOP_GEOM_FLAGS_ZONED	(1 << 27) /* zoned rt device */
+#define XFS_FSOP_GEOM_FLAGS_VERITY	(1 << 28) /* fs-verity */
 
 /*
  * Minimum and maximum sizes need for growth checks.
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index 985c7c6a09f2..fd06c3d6cc11 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -1587,6 +1587,8 @@ xfs_fs_geometry(
 		geo->flags |= XFS_FSOP_GEOM_FLAGS_METADIR;
 	if (xfs_has_zoned(mp))
 		geo->flags |= XFS_FSOP_GEOM_FLAGS_ZONED;
+	if (xfs_has_verity(mp))
+		geo->flags |= XFS_FSOP_GEOM_FLAGS_VERITY;
 	geo->rtsectsize = sbp->sb_blocksize;
 	geo->dirblocksize = xfs_dir2_dirblock_bytes(sbp);
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 19/21] xfs: check and repair the verity inode flag state
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (17 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 18/21] xfs: advertise fs-verity being available on filesystem Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:40   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 20/21] xfs: introduce health state for corrupted fsverity metadata Andrey Albershteyn
                   ` (2 subsequent siblings)
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

From: "Darrick J. Wong" <djwong@kernel.org>

If an inode has the incore verity iflag set, make sure that we can
actually activate fsverity on that inode.  If activation fails due to
a fsverity metadata validation error, clear the flag.  The usage model
for fsverity requires that any program that cares about verity state is
required to call statx/getflags to check that the flag is set after
opening the file, so clearing the flag will not compromise that model.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/scrub/common.c       | 55 +++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h       |  2 ++
 fs/xfs/scrub/inode.c        |  7 +++++
 fs/xfs/scrub/inode_repair.c | 36 ++++++++++++++++++++++++
 4 files changed, 100 insertions(+)

diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index e5233e31abb7..02b68bf4c512 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -45,6 +45,8 @@
 #include "scrub/health.h"
 #include "scrub/tempfile.h"
 
+#include <linux/fsverity.h>
+
 /* Common code for the metadata scrubbers. */
 
 /*
@@ -1754,3 +1756,56 @@ xchk_inode_count_blocks(
 	return xfs_bmap_count_blocks(sc->tp, sc->ip, whichfork, nextents,
 			count);
 }
+
+/*
+ * If this inode has S_VERITY set on it, read the verity info. If the reading
+ * fails with anything other than ENOMEM, the file is corrupt, which we can
+ * detect later with fsverity_active.
+ *
+ * Callers must hold the IOLOCK and must not hold the ILOCK of sc->ip because
+ * activation reads inode data.
+ */
+int
+xchk_inode_setup_verity(
+	struct xfs_scrub	*sc)
+{
+	int			error;
+
+	if (!fsverity_active(VFS_I(sc->ip)))
+		return 0;
+
+	error = fsverity_ensure_verity_info(VFS_I(sc->ip));
+	switch (error) {
+	case 0:
+		/* fsverity is active */
+		break;
+	case -ENODATA:
+	case -EMSGSIZE:
+	case -EINVAL:
+	case -EFSCORRUPTED:
+	case -EFBIG:
+	case -ERANGE:
+	case -EBADMSG:
+		/*
+		 * The nonzero errno codes above are the error codes that can
+		 * be returned from fsverity on metadata validation errors.
+		 */
+		return 0;
+	default:
+		/* runtime errors */
+		return error;
+	}
+
+	return 0;
+}
+
+/*
+ * Is this a verity file that failed to activate?  Callers must have tried to
+ * activate fsverity via xchk_inode_setup_verity.
+ */
+bool
+xchk_inode_verity_broken(
+	struct xfs_inode	*ip)
+{
+	return fsverity_active(VFS_I(ip)) && !fsverity_get_info(VFS_I(ip));
+}
diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h
index f0f073a93413..676b4afd0be4 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -267,6 +267,8 @@ int xchk_inode_is_allocated(struct xfs_scrub *sc, xfs_agino_t agino,
 		bool *inuse);
 int xchk_inode_count_blocks(struct xfs_scrub *sc, int whichfork,
 		xfs_extnum_t *nextents, xfs_filblks_t *count);
+int xchk_inode_setup_verity(struct xfs_scrub *sc);
+bool xchk_inode_verity_broken(struct xfs_inode *ip);
 
 bool xchk_inode_is_dirtree_root(const struct xfs_inode *ip);
 bool xchk_inode_is_sb_rooted(const struct xfs_inode *ip);
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 65b13e311916..d1cdd6b445d0 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -36,6 +36,10 @@ xchk_prepare_iscrub(
 
 	xchk_ilock(sc, XFS_IOLOCK_EXCL);
 
+	error = xchk_inode_setup_verity(sc);
+	if (error)
+		return error;
+
 	error = xchk_trans_alloc(sc, 0);
 	if (error)
 		return error;
@@ -833,6 +837,9 @@ xchk_inode(
 	if (S_ISREG(VFS_I(sc->ip)->i_mode))
 		xchk_inode_check_reflink_iflag(sc, I_INO(sc->ip));
 
+	if (xchk_inode_verity_broken(sc->ip))
+		xchk_ino_set_corrupt(sc, sc->sm->sm_ino);
+
 	xchk_inode_check_unlinked(sc);
 
 	xchk_inode_xref(sc, I_INO(sc->ip), &di);
diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c
index 8bc508336aa5..6c0b7130e227 100644
--- a/fs/xfs/scrub/inode_repair.c
+++ b/fs/xfs/scrub/inode_repair.c
@@ -573,6 +573,8 @@ xrep_dinode_flags(
 		dip->di_nrext64_pad = 0;
 	else if (dip->di_version >= 3)
 		dip->di_v3_pad = 0;
+	if (!xfs_has_verity(mp) || !S_ISREG(mode))
+		flags2 &= ~XFS_DIFLAG2_VERITY;
 
 	if (flags2 & XFS_DIFLAG2_METADATA) {
 		xfs_failaddr_t	fa;
@@ -1617,6 +1619,10 @@ xrep_dinode_core(
 	if (iget_error)
 		return iget_error;
 
+	error = xchk_inode_setup_verity(sc);
+	if (error)
+		return error;
+
 	error = xchk_trans_alloc(sc, 0);
 	if (error)
 		return error;
@@ -2035,6 +2041,27 @@ xrep_inode_unlinked(
 	return 0;
 }
 
+/*
+ * If this file is a fsverity file, xchk_prepare_iscrub or xrep_dinode_core
+ * should have activated it.  If it's still not active, then there's something
+ * wrong with the verity descriptor and we should turn it off.
+ */
+STATIC int
+xrep_inode_verity(
+	struct xfs_scrub	*sc)
+{
+	struct inode		*inode = VFS_I(sc->ip);
+
+	if (xchk_inode_verity_broken(sc->ip)) {
+		sc->ip->i_diflags2 &= ~XFS_DIFLAG2_VERITY;
+		inode_set_flags(inode, 0, S_VERITY);
+
+		xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
+	}
+
+	return 0;
+}
+
 /* Repair an inode's fields. */
 int
 xrep_inode(
@@ -2084,6 +2111,15 @@ xrep_inode(
 			return error;
 	}
 
+	/*
+	 * Disable fsverity if it cannot be activated.  Activation failure
+	 * prohibits the file from being opened, so there cannot be another
+	 * program with an open fd to what it thinks is a verity file.
+	 */
+	error = xrep_inode_verity(sc);
+	if (error)
+		return error;
+
 	/* Reconnect incore unlinked list */
 	error = xrep_inode_unlinked(sc);
 	if (error)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 20/21] xfs: introduce health state for corrupted fsverity metadata
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (18 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 19/21] xfs: check and repair the verity inode flag state Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:34   ` sashiko-bot
  2026-09-18 11:15 ` [PATCH v16 21/21] xfs: enable ro-compat fs-verity flag Andrey Albershteyn
  2026-09-22  4:51 ` [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Darrick J. Wong
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

Report corrupted fsverity descriptor through health system.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/libxfs/xfs_fs.h     |  1 +
 fs/xfs/libxfs/xfs_health.h |  4 +++-
 fs/xfs/xfs_fsverity.c      | 25 +++++++++++++++++++------
 fs/xfs/xfs_health.c        |  1 +
 4 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index c80133784419..84d62b7506a9 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -422,6 +422,7 @@ struct xfs_bulkstat {
 #define XFS_BS_SICK_SYMLINK	(1 << 6)  /* symbolic link remote target */
 #define XFS_BS_SICK_PARENT	(1 << 7)  /* parent pointers */
 #define XFS_BS_SICK_DIRTREE	(1 << 8)  /* directory tree structure */
+#define XFS_BS_SICK_FSVERITY	(1 << 9)  /* fsverity metadata */
 
 /*
  * Project quota id helpers (previously projid was 16bit only
diff --git a/fs/xfs/libxfs/xfs_health.h b/fs/xfs/libxfs/xfs_health.h
index 1d45cf5789e8..932b447190da 100644
--- a/fs/xfs/libxfs/xfs_health.h
+++ b/fs/xfs/libxfs/xfs_health.h
@@ -104,6 +104,7 @@ struct xfs_rtgroup;
 /* Don't propagate sick status to ag health summary during inactivation */
 #define XFS_SICK_INO_FORGET	(1 << 12)
 #define XFS_SICK_INO_DIRTREE	(1 << 13)  /* directory tree structure */
+#define XFS_SICK_INO_FSVERITY	(1 << 14)  /* fsverity metadata */
 
 /* Primary evidence of health problems in a given group. */
 #define XFS_SICK_FS_PRIMARY	(XFS_SICK_FS_COUNTERS | \
@@ -140,7 +141,8 @@ struct xfs_rtgroup;
 				 XFS_SICK_INO_XATTR | \
 				 XFS_SICK_INO_SYMLINK | \
 				 XFS_SICK_INO_PARENT | \
-				 XFS_SICK_INO_DIRTREE)
+				 XFS_SICK_INO_DIRTREE | \
+				 XFS_SICK_INO_FSVERITY)
 
 #define XFS_SICK_INO_ZAPPED	(XFS_SICK_INO_BMBTD_ZAPPED | \
 				 XFS_SICK_INO_BMBTA_ZAPPED | \
diff --git a/fs/xfs/xfs_fsverity.c b/fs/xfs/xfs_fsverity.c
index e62c9a5d0e1d..18a2cdf95427 100644
--- a/fs/xfs/xfs_fsverity.c
+++ b/fs/xfs/xfs_fsverity.c
@@ -69,13 +69,17 @@ xfs_fsverity_get_descriptor(
 	if (error)
 		return error;
 
-	if (is_empty)
+	if (is_empty) {
+		xfs_inode_mark_sick(XFS_I(inode), XFS_SICK_INO_FSVERITY);
 		return -ENODATA;
+	}
 
 	last_block_offset =
 		XFS_FSB_TO_B(mp, rec.br_startoff + rec.br_blockcount);
-	if (last_block_offset <= xfs_fsverity_metadata_offset(ip))
+	if (last_block_offset <= xfs_fsverity_metadata_offset(ip)) {
+		xfs_inode_mark_sick(XFS_I(inode), XFS_SICK_INO_FSVERITY);
 		return -ENODATA;
+	}
 
 	desc_size_pos = last_block_offset - sizeof(__be32);
 	error = fsverity_pagecache_read(inode, (char *)&d_desc_size,
@@ -84,20 +88,29 @@ xfs_fsverity_get_descriptor(
 		return error;
 
 	desc_size = be32_to_cpu(d_desc_size);
-	if (XFS_IS_CORRUPT(mp, desc_size > FS_VERITY_MAX_DESCRIPTOR_SIZE))
+	if (XFS_IS_CORRUPT(mp, desc_size > FS_VERITY_MAX_DESCRIPTOR_SIZE)) {
+		xfs_inode_mark_sick(XFS_I(inode), XFS_SICK_INO_FSVERITY);
 		return -ERANGE;
-	if (XFS_IS_CORRUPT(mp, desc_size > desc_size_pos))
+	}
+
+	if (XFS_IS_CORRUPT(mp, desc_size > desc_size_pos)) {
+		xfs_inode_mark_sick(XFS_I(inode), XFS_SICK_INO_FSVERITY);
 		return -ERANGE;
+	}
 
 	if (!buf_size)
 		return desc_size;
 
-	if (XFS_IS_CORRUPT(mp, desc_size > buf_size))
+	if (XFS_IS_CORRUPT(mp, desc_size > buf_size)) {
+		xfs_inode_mark_sick(XFS_I(inode), XFS_SICK_INO_FSVERITY);
 		return -ERANGE;
+	}
 
 	desc_pos = round_down(desc_size_pos - desc_size, blocksize);
-	if (desc_pos < xfs_fsverity_metadata_offset(ip))
+	if (desc_pos < xfs_fsverity_metadata_offset(ip)) {
+		xfs_inode_mark_sick(XFS_I(inode), XFS_SICK_INO_FSVERITY);
 		return -ERANGE;
+	}
 
 	error = fsverity_pagecache_read(inode, buf, desc_size, desc_pos);
 	if (error)
diff --git a/fs/xfs/xfs_health.c b/fs/xfs/xfs_health.c
index 239b843e83d4..be66760fb120 100644
--- a/fs/xfs/xfs_health.c
+++ b/fs/xfs/xfs_health.c
@@ -625,6 +625,7 @@ static const struct ioctl_sick_map ino_map[] = {
 	{ XFS_SICK_INO_DIR_ZAPPED,	XFS_BS_SICK_DIR },
 	{ XFS_SICK_INO_SYMLINK_ZAPPED,	XFS_BS_SICK_SYMLINK },
 	{ XFS_SICK_INO_DIRTREE,	XFS_BS_SICK_DIRTREE },
+	{ XFS_SICK_INO_FSVERITY,	XFS_BS_SICK_FSVERITY },
 };
 
 /* Fill out bulkstat health info. */
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH v16 21/21] xfs: enable ro-compat fs-verity flag
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (19 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 20/21] xfs: introduce health state for corrupted fsverity metadata Andrey Albershteyn
@ 2026-09-18 11:15 ` Andrey Albershteyn
  2026-09-18 11:44   ` sashiko-bot
  2026-09-22  4:51 ` [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Darrick J. Wong
  21 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-18 11:15 UTC (permalink / raw)
  To: djwong, ebiggers, hch, Carlos Maiolino
  Cc: Andrey Albershteyn, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

Finalize fs-verity integration in XFS by making kernel fs-verity
aware with ro-compat flag.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: add spaces]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
 fs/xfs/libxfs/xfs_format.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index fc196d9e12dd..8be1b101b196 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -378,8 +378,9 @@ xfs_sb_has_compat_feature(
 #define XFS_SB_FEAT_RO_COMPAT_ALL \
 		(XFS_SB_FEAT_RO_COMPAT_FINOBT | \
 		 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
-		 XFS_SB_FEAT_RO_COMPAT_REFLINK| \
-		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT)
+		 XFS_SB_FEAT_RO_COMPAT_REFLINK | \
+		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT | \
+		 XFS_SB_FEAT_RO_COMPAT_VERITY)
 #define XFS_SB_FEAT_RO_COMPAT_UNKNOWN	~XFS_SB_FEAT_RO_COMPAT_ALL
 static inline bool
 xfs_sb_has_ro_compat_feature(
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 03/21] fsverity: pass digest size and hash of the all-zeroes block to ->write
  2026-09-18 11:15 ` [PATCH v16 03/21] fsverity: pass digest size and hash of the all-zeroes block to ->write Andrey Albershteyn
@ 2026-09-18 11:24   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:24 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: dsterba, tytso, linux-ext4

> Let filesystem iterate over hashes in the block and check if these are
> hashes of zeroed data blocks. XFS will use this to decide if it want to
> store tree block full of these hashes.
> 
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=3


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 10/21] xfs: don't report dio_mem_align and dio_offset_align for fsverity files
  2026-09-18 11:15 ` [PATCH v16 10/21] xfs: don't report dio_mem_align and dio_offset_align for fsverity files Andrey Albershteyn
@ 2026-09-18 11:26   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:26 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: tytso, linux-ext4, dsterba

> Sashiko.dev reported that while fsverity files falls back to the
> buffered IO for Direct I/O, they should not report non-zero values in
> dio_mem_align and dio_offset_align, meaning it's not supported.
> 
> The STATX_DIOALIGN | STATX_DIO_READ_ALIGN flags are used to explicitly
> report if DIO is supported or not. They can not indicate that fallback
> to the buffered IO is used in this case. The zero alignment values also
> mean that DIO is not supported on this file, see statx(2).
> 
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=10


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 09/21] xfs: disable direct read path for fs-verity files
  2026-09-18 11:15 ` [PATCH v16 09/21] xfs: disable direct read path for fs-verity files Andrey Albershteyn
@ 2026-09-18 11:26   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:26 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: dsterba, linux-ext4, tytso

> The direct path is not supported on verity files. Attempts to use direct
> I/O path on such files should fall back to buffered I/O path.
> 
> Add a fall back to buffered I/O at two place, in a common fast path and
> latter when lock is acquired. The second check prevents TOCTOU issue
> with reading fsverity_active() status and resetting IOCB_DIRECT flag.
> 
> If one thread saw fsverity_active() to be false, and then second thread
> acquired XFS_IOLOCK_EXCL and enabled fsverity. The first thread will go
> through the DIO path.
> 
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=9


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 04/21] fsverity: hoist pagecache_read from f2fs/ext4 to fsverity
  2026-09-18 11:15 ` [PATCH v16 04/21] fsverity: hoist pagecache_read from f2fs/ext4 to fsverity Andrey Albershteyn
@ 2026-09-18 11:26   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:26 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: tytso, linux-ext4, dsterba

> This is the same function to read from pageache. XFS will also need
> this, so move this to core fsverity.
> 
> Note that f2fs and ext4 functions diverged a bit, as ext4 operated over
> folios and f2fs operated over pages. The common one will operate over
> folios.
> 
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Acked-by: Eric Biggers <ebiggers@kernel.org>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=4


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 01/21] fsverity: report validation errors through fserror to fsnotify
  2026-09-18 11:15 ` [PATCH v16 01/21] fsverity: report validation errors through fserror to fsnotify Andrey Albershteyn
@ 2026-09-18 11:27   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:27 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: tytso, linux-ext4, dsterba

> Reported verification errors to fsnotify through recently added fserror
> interface.
> 
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Acked-by: Eric Biggers <ebiggers@kernel.org>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=1


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 08/21] xfs: don't allow to enable DAX on fs-verity sealed inode
  2026-09-18 11:15 ` [PATCH v16 08/21] xfs: don't allow to enable DAX on fs-verity sealed inode Andrey Albershteyn
@ 2026-09-18 11:27   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:27 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: tytso, dsterba, linux-ext4

> fs-verity doesn't support DAX. Forbid filesystem to enable DAX on
> inodes which already have fs-verity enabled. The opposite is checked
> when fs-verity is enabled, it won't be enabled if DAX is.
> 
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=8


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 06/21] fsverity: hoist statx reporting of fs-verity flag
  2026-09-18 11:15 ` [PATCH v16 06/21] fsverity: hoist statx reporting of fs-verity flag Andrey Albershteyn
@ 2026-09-18 11:28   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:28 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: tytso, linux-ext4, dsterba

> All filesystems, supporting fsverity, report this status by checking
> inode flag. Also, BTRFS was missing stat->attributes_mask, which is
> fixed now.
> 
> Fixes: 146054090b08 ("btrfs: initial fsverity support")
> Cc: stable@vger.kernel.org
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=6


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 05/21] fsverity: don't allow setting DAX file attribute on fsverity files
  2026-09-18 11:15 ` [PATCH v16 05/21] fsverity: don't allow setting DAX file attribute on fsverity files Andrey Albershteyn
@ 2026-09-18 11:28   ` sashiko-bot
  2026-09-25  4:35   ` Eric Biggers
  1 sibling, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:28 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: linux-ext4, dsterba, tytso

> When fsverity is enabled on the file, with FS_IOC_ENABLE_VERITY ioctl(),
> it checks if file has DAX enabled and fails if that's true. However, the
> opposite case is not checked.
> 
> Note, that the only other filesystem supporting DAX and fsverity is
> ext4, and ext4 does check for this case.
> 
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=5


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 18/21] xfs: advertise fs-verity being available on filesystem
  2026-09-18 11:15 ` [PATCH v16 18/21] xfs: advertise fs-verity being available on filesystem Andrey Albershteyn
@ 2026-09-18 11:30   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:30 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: dsterba, tytso, linux-ext4

> From: "Darrick J. Wong" <djwong@kernel.org>
> 
> Advertise that this filesystem supports fsverity.
> 
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Andrey Albershteyn <aalbersh@redhat.com>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=18


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 17/21] xfs: add fs-verity ioctls
  2026-09-18 11:15 ` [PATCH v16 17/21] xfs: add fs-verity ioctls Andrey Albershteyn
@ 2026-09-18 11:31   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:31 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: linux-ext4, dsterba, tytso

> Add fs-verity ioctls to enable, dump metadata (descriptor and Merkle
> tree pages) and obtain file's digest.
> 
> [djwong: remove unnecessary casting]
> 
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=17


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 02/21] fsverity: expose ensure_fsverity_info()
  2026-09-18 11:15 ` [PATCH v16 02/21] fsverity: expose ensure_fsverity_info() Andrey Albershteyn
@ 2026-09-18 11:32   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:32 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: dsterba, linux-ext4, tytso

> This function will be used by XFS's scrub to force fsverity activation,
> therefore, to read fsverity context.
> 
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Acked-by: Eric Biggers <ebiggers@kernel.org>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=2


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi()
  2026-09-18 11:15 ` [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi() Andrey Albershteyn
@ 2026-09-18 11:32   ` sashiko-bot
  2026-09-22  4:49     ` Darrick J. Wong
  2026-09-22  4:34   ` Darrick J. Wong
  1 sibling, 1 reply; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:32 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: linux-ext4, tytso, dsterba

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] xfs: XFS_BMAPI_UNWRITTEN corrupts data on realtime files
--

commit 03f3992d52956762d29c7c4c4908adeac76b8662
Author: Andrey Albershteyn <aalbersh@kernel.org>
xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi()

The __xfs_bunmapi() function is used to remove all extents past EOF. This
patch adds XFS_BMAPI_UNWRITTEN to remove only unwritten extents, leaving
written ones in place. This will be used to clean up unwritten extents on
fsverity inodes.

> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index ba6a9611f6f9d..69364abbfd011 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -5279,6 +5279,10 @@ __xfs_bunmapi(
>  		if (del.br_startoff + del.br_blockcount > end + 1)
>  			del.br_blockcount = end + 1 - del.br_startoff;
>  
> +		if ((flags & XFS_BMAPI_UNWRITTEN) &&
> +				del.br_state != XFS_EXT_UNWRITTEN)
> +			goto skip;
> +
>  		if (!isrt || (flags & XFS_BMAPI_REMAP))
>  			goto delete;
>  

[Severity: Critical]
Does introducing the XFS_BMAPI_UNWRITTEN flag cause data corruption on
realtime files due to how __xfs_bunmapi() handles unwritten extents?

If a user creates a realtime file, writes data so that EOF is not aligned
to the realtime extent size, and preallocates space after EOF (creating an
unwritten extent), subsequent calls to clean up with XFS_BMAPI_UNWRITTEN
(such as enabling fs-verity) will process the front-misaligned unwritten
extent.

Because __xfs_bunmapi() falls through to the legacy realtime misalignment
logic for unwritten extents:

			} else if (del.br_state == XFS_EXT_UNWRITTEN) {
				struct xfs_bmbt_irec	prev;
				...
				/*
				 * This one is already unwritten.
				 * It must have a written left neighbor.
				 * Unwrite the killed part of that one and
				 * try again.
				 */
				...
				prev.br_state = XFS_EXT_UNWRITTEN;
				error = xfs_bmap_add_extent_unwritten_real(tp,
						ip, whichfork, &icur, &cur,
						&prev, &logflags);

Could this legacy truncation logic forcefully unwrite the written left
neighbor (the valid user data before EOF) to form a full realtime extent,
effectively destroying the mapping to valid user data?

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=13

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 16/21] xfs: initialize fs-verity on file open
  2026-09-18 11:15 ` [PATCH v16 16/21] xfs: initialize fs-verity on file open Andrey Albershteyn
@ 2026-09-18 11:33   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:33 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: tytso, dsterba, linux-ext4

> fs-verity will read and attach metadata (not the tree itself) from
> a disk for those inodes which already have fs-verity enabled.
> 
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=16


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 07/21] xfs: introduce fsverity on-disk changes
  2026-09-18 11:15 ` [PATCH v16 07/21] xfs: introduce fsverity on-disk changes Andrey Albershteyn
@ 2026-09-18 11:34   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:34 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: linux-ext4, dsterba, tytso

> Introduce XFS_DIFLAG2_VERITY for inodes with fsverity. This flag
> indicates that inode has fs-verity enabled (i.e. descriptor exist,
> tree is built and file is read-only).
> 
> Introduce XFS_SB_FEAT_RO_COMPAT_VERITY for filesystems having
> fsverity inodes. As on-disk changes applies to fsverity inodes only, let
> older kernels read-only access. This will be enabled in the further
> patch after full fsverity support.
> 
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=7


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 20/21] xfs: introduce health state for corrupted fsverity metadata
  2026-09-18 11:15 ` [PATCH v16 20/21] xfs: introduce health state for corrupted fsverity metadata Andrey Albershteyn
@ 2026-09-18 11:34   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:34 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: dsterba, tytso, linux-ext4

> Report corrupted fsverity descriptor through health system.
> 
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=20


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 12/21] xfs: use read ioend for fsverity data verification
  2026-09-18 11:15 ` [PATCH v16 12/21] xfs: use read ioend for fsverity data verification Andrey Albershteyn
@ 2026-09-18 11:36   ` sashiko-bot
  2026-09-22  4:29   ` Darrick J. Wong
  2026-09-22  7:18   ` Christoph Hellwig
  2 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:36 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: linux-ext4, dsterba, tytso

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] xfs: memory reclaim deadlock and forward progress violation in xfs_end_io_read()
- [High] xfs: synchronous Merkle tree read stalls bio completion in xfs_end_io_read()
- [Medium] xfs: memory leak of xfs_parent_args_cache on init error path
--

commit 798d972634a53a99bff867f7211b6d47638362a9
Author: Andrey Albershteyn <aalbersh@kernel.org>

xfs: use read ioend for fsverity data verification
    
This commit uses read ioends for fsverity verification instead of using
the same workqueue for fsverity metadata IO. It passes fsverity_info from
the iomap context down to the ioend, and adds a helper to check if file
data needs verification.

> diff --git a/fs/xfs/xfs_ioend.c b/fs/xfs/xfs_ioend.c
> index e70be5b86f0b7..2f46855b052fd 100644
> --- a/fs/xfs/xfs_ioend.c
> +++ b/fs/xfs/xfs_ioend.c

[ ... ]

> @@ -117,6 +138,36 @@ xfs_end_io_read(
>  		}
>  	}
>  
> +	/*
> +	 * If we have fsverity and block device integrity attached to this bio,
> +	 * we need to run fsverity verification of data folios from a separate
> +	 * fsverity workqueue. This is necessary to avoid deadlocking due to
> +	 * fsverity issuing more reads of fsverity metadata which would be
> +	 * processed by the same worker in the BIO completion workqueue.
> +	 *
> +	 * Without block device integrity, fsverity metadata IO will not use
> +	 * ioends for completion.
> +	 */
> +	if (IS_ENABLED(CONFIG_FS_VERITY) && !error && ioend->io_vi &&
> +			xfs_fsverity_is_file_data(ip, ioend->io_offset)) {
> +		if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) {
> +			fsv_ioend = kmem_cache_zalloc(xfs_fsverity_ioend_cache,
> +					GFP_KERNEL);

[Severity: High]
Does this allocation risk a deadlock? xfs_end_io_read() executes in a block IO
completion context (like task_work or the block layer bio_complete_wq). Using
GFP_KERNEL here allows direct memory reclaim, which can block waiting for
dirty pages to be written back and their IO completions processed. Waiting
inside an IO completion worker could create a cyclical deadlock. Should this
use a mempool with GFP_NOIO or GFP_NOWAIT to ensure forward progress?

> +			if (!fsv_ioend) {
> +				iomap_finish_ioends(ioend, -ENOMEM);
> +				return;
> +			}

[Severity: High]
Could failing the IO with -ENOMEM break forward progress guarantees under
memory pressure? It seems that memory allocations in the block IO completion
path need to be backed by a mempool to avoid spurious IO failures when
memory is tight.

> +			fsv_ioend->ioend = ioend;
> +			INIT_WORK(&fsv_ioend->work, xfs_end_fsverity_io_read);
> +
> +			fsverity_enqueue_verify_work(&fsv_ioend->work);
> +			return;
> +		}
> +
> +		fsverity_verify_bio(ioend->io_vi, &ioend->io_bio);

[Severity: High]
Can this synchronous verification stall bio completion? When
fsverity_verify_bio() requires Merkle tree blocks that aren't in the page
cache, it synchronously reads them from disk by calling read_mapping_folio()
and waits via wait_on_folio_locked_killable().

Since xfs_end_io_read() is executing in a bio completion context, blocking
here to wait for another disk IO can stall all other async IO completions
queued behind it. Should this verification always be deferred to a dedicated
workqueue?

> +		error = blk_status_to_errno(ioend->io_bio.bi_status);
> +	}
>  
>  	iomap_finish_ioends(ioend, error);
>  }

> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index 5a06132aa3846..f34c9645767d5 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c

[ ... ]

> @@ -2516,8 +2516,20 @@ xfs_init_caches(void)
>  	if (!xfs_parent_args_cache)
>  		goto out_destroy_xmi_cache;
>  
> +#ifdef CONFIG_FS_VERITY
> +	xfs_fsverity_ioend_cache = kmem_cache_create("xfs_fsverity_ioend",
> +					     sizeof(struct xfs_fsverity_ioend),
> +					     0, 0, NULL);
> +	if (!xfs_fsverity_ioend_cache)
> +		goto out_destroy_fsverity_ioend_cache;
> +#endif
> +
>  	return 0;
>  
> +#ifdef CONFIG_FS_VERITY
> + out_destroy_fsverity_ioend_cache:
> +	kmem_cache_destroy(xfs_fsverity_ioend_cache);
> +#endif
>   out_destroy_xmi_cache:

[Severity: Medium]
If the allocation of xfs_fsverity_ioend_cache fails, the error path jumps to
out_destroy_fsverity_ioend_cache. Does this permanently leak the memory
allocated for xfs_parent_args_cache? The error path seems to fall through
to out_destroy_xmi_cache, entirely skipping the destruction of the parent
args cache.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=12

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 11/21] xfs: handle fsverity I/O in write/read path
  2026-09-18 11:15 ` [PATCH v16 11/21] xfs: handle fsverity I/O in write/read path Andrey Albershteyn
@ 2026-09-18 11:38   ` sashiko-bot
  2026-09-22  7:15   ` Christoph Hellwig
  1 sibling, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:38 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: tytso, dsterba, linux-ext4

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] xfs: global IOMAP_F_FSVERITY in writeback triggers WARN_ON_ONCE and skips zeroing
- [High] xfs: missing IOMAP_F_FSVERITY in srcmap causes Merkle tree data corruption during COW
- [High] xfs: extent merging in writeback drops IOMAP_F_FSVERITY, permanently leaking dirty folios
--

commit 921462565b838ec412711bced36094baa0229a3e
Author: Andrey Albershteyn <aalbersh@kernel.org>
Date:   Fri Sep 18 11:15:23 2026 +0000

    xfs: handle fsverity I/O in write/read path

    For write/writeback set IOMAP_F_FSVERITY flag telling iomap to not
    update inode size and to not skip folios beyond EOF. Initiate fsverity
    writeback with IOMAP_F_FSVERITY set to tell iomap should not skip folio
    that is dirty beyond EOF.

> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index ae91f63455c5d..ba6a9611f6f9d 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
[ ... ]
> @@ -4401,6 +4403,10 @@ xfs_bmapi_convert_one_delalloc(
>  	 * the extent.  Just return the real extent at this offset.
>  	 */
>  	if (!isnullstartblock(bma.got.br_startblock)) {
> +		if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION) &&
> +		    XFS_FSB_TO_B(mp, bma.got.br_startoff) >=
> +			    xfs_fsverity_metadata_offset(ip))
> +			flags |= IOMAP_F_FSVERITY;

[Severity: High]
Does checking the potentially merged extent's start offset instead of the
requested offset cause the flag to be wrongly omitted?

If the file size is exactly aligned to the filesystem block size, the
Merkle tree begins immediately after the file data without a padding hole.
When xfs_bmapi_allocate() converts the Merkle tree delalloc extent to a real
extent, the block allocator might place it contiguously with the existing
data extent and automatically merge them. This shifts bma.got.br_startoff
back to 0.

This would cause the check to evaluate to false and IOMAP_F_FSVERITY to not
be set. Without the verity flag, iomap_writeback_handle_eof() processes the
Merkle tree folios as invalid past-EOF folios and returns false, completely
aborting writeback without clearing the dirty state. The Merkle tree folios
are permanently leaked as dirty memory, preventing filesystem unmount and
causing data loss of the Merkle tree. 

Could this use the requested offset instead of the potentially merged
extent's start offset?

>  		xfs_bmbt_to_iomap(ip, iomap, &bma.got, 0, flags,
>  				xfs_iomap_inode_sequence(ip, flags));
>  		if (seq)
[ ... ]
> @@ -4448,6 +4454,11 @@ xfs_bmapi_convert_one_delalloc(
>  	XFS_STATS_ADD(mp, xs_xstrat_bytes, XFS_FSB_TO_B(mp, bma.length));
>  	XFS_STATS_INC(mp, xs_xstrat_quick);
>  
> +	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION) &&
> +	    XFS_FSB_TO_B(mp, bma.got.br_startoff) >=
> +		    xfs_fsverity_metadata_offset(ip))
> +		flags |= IOMAP_F_FSVERITY;
> +

[Severity: High]
Does this check have the same issue with merged extents causing the flag
to be dropped as mentioned above?

>  	ASSERT(!isnullstartblock(bma.got.br_startblock));
>  	xfs_bmbt_to_iomap(ip, iomap, &bma.got, 0, flags,
>  				xfs_iomap_inode_sequence(ip, flags));
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index c30e688cfc9f6..19640e4e3ed12 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
[ ... ]
> @@ -172,12 +173,16 @@ xfs_map_blocks(
>  	int			retries = 0;
>  	int			error = 0;
>  	unsigned int		*seq;
> +	unsigned int		iomap_flags = 0;
>  
>  	if (xfs_is_shutdown(mp))
>  		return -EIO;
>  
>  	XFS_ERRORTAG_DELAY(mp, XFS_ERRTAG_WB_DELAY_MS);
>  
> +	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
> +		iomap_flags |= IOMAP_F_FSVERITY;
> +

[Severity: High]
Does unconditionally setting IOMAP_F_FSVERITY here, without checking if the
mapped offset is actually within the metadata region, contaminate writeback
for data folios?

If a large folio is allocated that spans across both the file size and the
verity metadata offset, the folio covers both data and metadata. During
writeback of this dirtied folio, iomap_writeback_handle_eof() sees the
IOMAP_F_FSVERITY flag set unconditionally. Because folio_pos(folio) < isize,
it triggers a WARN_ON_ONCE and returns early, entirely skipping the
folio_zero_segment() call required to zero the padding hole between the
file size and the Merkle tree. This leaks uninitialized pagecache memory to
disk and causes a kernel DoS via the splat.

>  	/*
>  	 * COW fork blocks can overlap data fork blocks even if the blocks
>  	 * aren't shared.  COW I/O always takes precedent, so we must always
[ ... ]
> @@ -507,6 +517,22 @@ static const struct iomap_writeback_ops xfs_zoned_writeback_ops = {
>  	.writeback_submit	= xfs_zoned_writeback_submit,
>  };
>  
> +static int
> +xfs_iomap_writepages(
> +	struct xfs_inode		*ip,
> +	struct iomap_writepage_ctx	*ctx)
> +{
> +	/*
> +	 * Writeback does not work for folios past EOF, let it know that
> +	 * I/O happens for fsverity metadata and this restriction need
> +	 * to be skipped
> +	 */
> +	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
> +		ctx->iomap.flags |= IOMAP_F_FSVERITY;

[Severity: High]
Can this unconditional assignment also trigger the same kernel splat and
skip tail-zeroing for large folios spanning EOF?

> +
> +	return iomap_writepages(ctx);
> +}
> +
>  STATIC int
>  xfs_vm_writepages(
[ ... ]
> diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> index 7c6238fed61e4..fa11d830dfc09 100644
> --- a/fs/xfs/xfs_iomap.c
> +++ b/fs/xfs/xfs_iomap.c
[ ... ]
> @@ -1809,9 +1817,12 @@ xfs_buffered_write_iomap_begin(
>  	if (xfs_is_shutdown(mp))
>  		return -EIO;
>  
> +	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
> +		iomap_flags |= IOMAP_F_FSVERITY;
> +

[Severity: High]
Does this code omit passing the IOMAP_F_FSVERITY flag to the srcmap when
a COW extent is mapped for a write?

Later in this function (and similarly in xfs_direct_write_iomap_begin()),
xfs_bmbt_to_iomap() is called to populate the srcmap:

	if (imap.br_startblock != HOLESTARTBLOCK) {
		seq = xfs_iomap_inode_sequence(ip, 0);
		error = xfs_bmbt_to_iomap(ip, srcmap, &imap, flags, 0, seq);
		if (error)
			goto out_unlock;
	}

Since 0 is passed instead of iomap_flags, IOMAP_F_FSVERITY is missing
from the srcmap. If always_cow is enabled on the mount, writes to the Merkle
tree will allocate in the COW fork. A subsequent partial block write to the
same block requires a read-modify-write cycle. Because srcmap->flags lacks
IOMAP_F_FSVERITY, iomap_block_needs_zeroing() incorrectly zeroes the block
(thinking it's past EOF) instead of reading the existing Merkle tree data
from disk. This permanently corrupts the fs-verity metadata.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=11

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 19/21] xfs: check and repair the verity inode flag state
  2026-09-18 11:15 ` [PATCH v16 19/21] xfs: check and repair the verity inode flag state Andrey Albershteyn
@ 2026-09-18 11:40   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:40 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: dsterba, linux-ext4, tytso

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] xfs: scrub silently removes fs-verity protection on capability mismatch
- [High] xfs: TOCTOU race in fsverity open triggers kernel panic
--

commit f41f7f55dbc0aa68b4ef7ee729b6b2e8b291d72f
Author: Darrick J. Wong <djwong@kernel.org>
Subject: xfs: check and repair the verity inode flag state

If an inode has the incore verity iflag set, make sure that we can
actually activate fsverity on that inode.  If activation fails due to
a fsverity metadata validation error, clear the flag.  The usage model
for fsverity requires that any program that cares about verity state is
required to call statx/getflags to check that the flag is set after
opening the file, so clearing the flag will not compromise that model.

> diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
> index e5233e31abb75..02b68bf4c5127 100644
> --- a/fs/xfs/scrub/common.c
> +++ b/fs/xfs/scrub/common.c
[ ... ]
> @@ -1754,3 +1756,56 @@ xchk_inode_count_blocks(
> +int
> +xchk_inode_setup_verity(
> +	struct xfs_scrub	*sc)
> +{
> +	int			error;
> +
> +	if (!fsverity_active(VFS_I(sc->ip)))
> +		return 0;
> +
> +	error = fsverity_ensure_verity_info(VFS_I(sc->ip));
> +	switch (error) {
> +	case 0:
> +		/* fsverity is active */
> +		break;
> +	case -ENODATA:
> +	case -EMSGSIZE:
> +	case -EINVAL:
> +	case -EFSCORRUPTED:
> +	case -EFBIG:
> +	case -ERANGE:
> +	case -EBADMSG:
> +		/*
> +		 * The nonzero errno codes above are the error codes that can
> +		 * be returned from fsverity on metadata validation errors.
> +		 */
> +		return 0;

[Severity: High]
Does this code silently remove fs-verity protection on capability mismatches?

The switch statement in xchk_inode_setup_verity() returns 0 for compatibility
mismatch errors like -EINVAL and -EFBIG from fsverity_ensure_verity_info().
This treats capability/compatibility mismatch errors as metadata corruption,
allowing xchk_inode_verity_broken() to flag the inode as corrupt because
fsverity_get_info() failed.

As a result, running xfs_scrub on a valid fsverity file system mounted on a
platform with different capabilities (e.g., 64K block size mounted on a 4K
page system, or lacking a specific hash algorithm) will silently and
permanently strip the file's verity protection.

> +	default:
> +		/* runtime errors */
> +		return error;
> +	}
> +
> +	return 0;
> +}
[ ... ]
> diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c
> index abfcab86928e6..b43b39662b687 100644
> --- a/fs/xfs/scrub/inode_repair.c
> +++ b/fs/xfs/scrub/inode_repair.c
[ ... ]
> @@ -2035,6 +2041,27 @@ xrep_inode_unlinked(
> +STATIC int
> +xrep_inode_verity(
> +	struct xfs_scrub	*sc)
> +{
> +	struct inode		*inode = VFS_I(sc->ip);
> +
> +	if (xchk_inode_verity_broken(sc->ip)) {
> +		sc->ip->i_diflags2 &= ~XFS_DIFLAG2_VERITY;
> +		inode_set_flags(inode, 0, S_VERITY);

[Severity: High]
Can this code trigger a race condition and a kernel panic?

When xrep_inode_verity() clears S_VERITY from the inode here locklessly, it
can race with the VFS open path. If Thread A opens an fsverity file and
fsverity_file_open() locklessly checks IS_VERITY(inode) successfully, Thread B
could concurrently clear S_VERITY during scrub here.

When Thread A proceeds to xfs_fsverity_get_descriptor(), it will execute
ASSERT(inode->i_flags & S_VERITY) before taking the XFS ILOCK, causing a
panic on debug kernels because Thread B just cleared the flag.

Additionally, this is where the security protection is permanently deleted
from the inode based on the assumption in xchk_inode_setup_verity() regarding
compatibility mismatch errors.

> +
> +		xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
> +	}
> +
> +	return 0;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=19

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes
  2026-09-18 11:15 ` [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes Andrey Albershteyn
@ 2026-09-18 11:41   ` sashiko-bot
  2026-09-22  4:37   ` Darrick J. Wong
  1 sibling, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:41 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: dsterba, tytso, linux-ext4

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] xfs: unconditional transaction storm on fsverity inode eviction
- [Medium] xfs: permanent unwritten extent leak due to premature tag clearing
--

commit 6b94de2dc949e4cfdd882209710ec5c6ad8019cf
Author: Andrey Albershteyn <aalbersh@kernel.org>

xfs: don't remove written extents past EOF on fsverity inodes

This commit updates the EOF blocks cleanup logic to handle fsverity inodes.
It skips the removal of written extents past EOF for fully constructed
fsverity files. It also cleans up delayed preallocations and unwritten
extents used during Merkle tree construction.

> diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> index 268d159339d06..7fd951992557d 100644
> --- a/fs/xfs/xfs_bmap_util.c
> +++ b/fs/xfs/xfs_bmap_util.c
[ ... ]
> @@ -553,6 +554,13 @@ xfs_can_free_eofblocks(
>  	if (last_fsb <= end_fsb)
>  		return false;
>  
> +	/*
> +	 * Don't clean fsverity inodes which have merkle tree being built, the
> +	 * merkle tree is written beyond EOF
> +	 */
> +	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
> +		return false;
> +

[Severity: High]
Looking at xfs_can_free_eofblocks() in fs/xfs/xfs_bmap_util.c, does the
subsequent lookup unconditionally find the written Merkle tree extents past
EOF for fully constructed fsverity files?

Because the Merkle tree is stored as written extents past EOF,
xfs_iext_lookup_extent() will find these blocks and return true.
This triggers xfs_free_eofblocks() on every cache eviction. Any
unprivileged user reading fsverity files could cause natural VFS
cache churn, potentially triggering an expensive transaction path
on every eviction.

[ ... ]
> @@ -593,15 +604,20 @@ xfs_free_eofblocks(
>  	 *
>  	 * Note that this means we also leave speculative preallocations in
>  	 * place for preallocated files.
> +	 *
> +	 * Clean up delalloc reservations for fsverity too as those won't be
> +	 * used
>  	 */
> -	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) {
> +	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND) ||
> +			has_verity) {
>  		if (ip->i_delayed_blks) {
>  			xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK,
>  				round_up(XFS_ISIZE(ip), mp->m_sb.sb_blocksize),
>  				LLONG_MAX, NULL);
>  		}
>  		xfs_inode_clear_eofblocks_tag(ip);
> -		return 0;
> +		if (!has_verity)
> +			return 0;

[Severity: Medium]
Does clearing the EOF blocks tag here in xfs_free_eofblocks() permanently
leak unwritten extents if the subsequent transaction or truncation fails?

If xfs_trans_alloc() fails or xfs_itruncate_extents_flags() encounters an
error (for example, due to memory pressure during extent split in
xfs_bunmapi_range), the code jumps to err_cancel without restoring the
XFS_IEOFBLOCKS tag. Because the radix tree tag is missing, the background
blockgc worker will never revisit this inode to retry the cleanup.

>  	}
>  
>  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> @@ -613,6 +629,9 @@ xfs_free_eofblocks(
>  	xfs_ilock(ip, XFS_ILOCK_EXCL);
>  	xfs_trans_ijoin(tp, ip, 0);
>  
> +	if (has_verity)
> +		bmapi_flags |= XFS_BMAPI_UNWRITTEN;
> +
>  	/*
>  	 * Do not update the on-disk file size.  If we update the on-disk file
>  	 * size and then the system crashes before the contents of the file are
> @@ -620,7 +639,7 @@ xfs_free_eofblocks(
>  	 * bug).
>  	 */
>  	error = xfs_itruncate_extents_flags(&tp, ip, XFS_DATA_FORK,
> -				XFS_ISIZE(ip), XFS_BMAPI_NODISCARD);
> +				XFS_ISIZE(ip), bmapi_flags);

[Severity: High]
While no blocks are unmapped here (since the Merkle tree is written,
not unwritten), does xfs_itruncate_extents_flags() unconditionally
dirty the inode core via xfs_trans_log_inode() and commit the
transaction?

Coupled with xfs_can_free_eofblocks() unconditionally returning true
for fsverity files, this appears to cause severe write amplification
and unnecessary journal traffic every time a fully constructed,
read-only fsverity file is evicted from the VFS cache.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=14

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-18 11:15 ` [PATCH v16 15/21] xfs: add fs-verity support Andrey Albershteyn
@ 2026-09-18 11:43   ` sashiko-bot
  2026-09-22  4:50     ` Darrick J. Wong
  2026-09-22  4:45   ` Darrick J. Wong
  2026-09-22  7:22   ` Christoph Hellwig
  2 siblings, 1 reply; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:43 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: linux-ext4, dsterba, tytso

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] xfs: AB-BA lock inversion in xfs_fsverity_delete_metadata()
- [High] xfs: verity metadata destroyed after commit on xfs_free_eofblocks failure
- [Low] xfs: duplicate xfs_fsverity_delete_metadata call on cleanup failure
--

commit 2cf0e89721e69aa45ac23d60792bafc833b86f51
Author: Andrey Albershteyn <aalbersh@kernel.org>

xfs: add fs-verity support

This commit integrates fs-verity with XFS, storing the Merkle tree and 
descriptor in the data fork past EOF. It sets up the enablement process
and handles read and write operations for the verity metadata.

> --- a/fs/xfs/xfs_fsverity.c
> +++ b/fs/xfs/xfs_fsverity.c
[ ... ]
> +static int
> +xfs_fsverity_delete_metadata(
> +	struct xfs_inode	*ip)
> +{
> +	struct xfs_trans	*tp;
> +	struct xfs_mount	*mp = ip->i_mount;
> +	int			error;
> +
> +	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
> +	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> +	if (error) {
> +		xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);
> +		return error;
> +	}
> +
> +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> +	xfs_trans_ijoin(tp, ip, 0);
> +
> +	truncate_inode_pages(VFS_I(ip)->i_mapping, XFS_ISIZE(ip));

[Severity: High]
Does this sequence create a lock inversion between the page/folio locks and
the XFS_ILOCK?

XFS lock ordering rules require folio locks to be acquired before the log space
reservation (xfs_trans_alloc) and the XFS_ILOCK. Because truncate_inode_pages
sequentially locks folios, calling it while holding XFS_ILOCK_EXCL could cause
an AB-BA deadlock if a background thread (like writeback) has locked a folio
and is concurrently trying to acquire the XFS_ILOCK for block mapping.

Could truncate_inode_pages be moved outside the transaction and before
acquiring XFS_ILOCK_EXCL?

[ ... ]
> +static int
> +xfs_fsverity_end_enable(
> +	struct file		*file,
> +	const void		*desc,
> +	size_t			desc_size,
> +	u64			merkle_tree_size)
> +{
> +	struct inode		*inode = file_inode(file);
> +	struct xfs_inode	*ip = XFS_I(inode);
> +	struct xfs_mount	*mp = ip->i_mount;
> +	struct xfs_trans	*tp;
> +	int			error = 0;
> +	loff_t			range_start = xfs_fsverity_metadata_offset(ip);
> +
> +	xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
> +
> +	/* fs-verity failed, just cleanup */
> +	if (desc == NULL) {
> +		error = xfs_fsverity_delete_metadata(ip);
> +		goto out;
> +	}
[ ... ]
> +	/*
> +	 * Ensure that we've persisted the verity information before we enable
> +	 * it on the inode and tell the caller we have sealed the inode.
> +	 */
> +	ip->i_diflags2 |= XFS_DIFLAG2_VERITY;
> +
> +	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
> +	xfs_trans_set_sync(tp);
> +
> +	error = xfs_trans_commit(tp);
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +	if (error)
> +		goto out;
> +
> +	inode_set_flags(inode, S_VERITY, S_VERITY);
> +
> +	/*
> +	 * Proactively remove unwritten extents left by COW preallocations and
> +	 * write preallocation in the merkle tree holes and past descriptor,
> +	 * and any delayed preallocations
> +	 */
> +	error = xfs_free_eofblocks(ip);
> +out:
> +	if (error) {
> +		int	error2;
> +
> +		error2 = xfs_fsverity_delete_metadata(ip);

[Severity: Low]
Is it intentional to call xfs_fsverity_delete_metadata a second time if the
initial early cleanup fails?

If desc is NULL at the start of the function, xfs_fsverity_delete_metadata is
called. If that call returns an error, it jumps to the out label. Because
error is preserved, this block executes and calls xfs_fsverity_delete_metadata
again with the exact same failed state.

[Severity: High]
Can a failure in xfs_free_eofblocks cause the metadata of a fully committed
verity inode to be destroyed?

If the verity enablement is successful, the transaction is committed via
xfs_trans_commit and the inode is permanently marked with XFS_DIFLAG2_VERITY.
If the subsequent post-commit optimization call to xfs_free_eofblocks fails
(for example, due to memory constraints or space issues), it returns an error,
which leads directly to this unconditional cleanup path.

This deletes the Merkle tree and descriptor extents. Because the on-disk flag
was already committed, the file remains verity-enabled but loses its metadata.
This would permanently fail verity validation and lock the user out of their
file.

Should this cleanup be skipped if the transaction has already been committed?

> +		if (error2)
> +			xfs_alert(ip->i_mount,
> +"ino 0x%llx failed to clean up new fsverity metadata, err %d",
> +					I_INO(ip), error2);
> +	}
> +
> +	xfs_iflags_clear(ip, XFS_VERITY_CONSTRUCTION);
> +	return error;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=15

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 21/21] xfs: enable ro-compat fs-verity flag
  2026-09-18 11:15 ` [PATCH v16 21/21] xfs: enable ro-compat fs-verity flag Andrey Albershteyn
@ 2026-09-18 11:44   ` sashiko-bot
  0 siblings, 0 replies; 67+ messages in thread
From: sashiko-bot @ 2026-09-18 11:44 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: dsterba, tytso, linux-ext4

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] xfs: fsverity enablement failure bricks inode due to un-cleared XFS_DIFLAG2_VERITY
--

commit 3c41daf7bf4b1b9da289ed99189425637499f418
Author: Andrey Albershteyn <aalbersh@kernel.org>

xfs: enable ro-compat fs-verity flag

Finalize fs-verity integration in XFS by making kernel fs-verity
aware with ro-compat flag.

> diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
> index f728e7d1eb325..7190fb069d1a8 100644
> --- a/fs/xfs/libxfs/xfs_format.h
> +++ b/fs/xfs/libxfs/xfs_format.h
> @@ -378,8 +378,9 @@ xfs_sb_has_compat_feature(
>  #define XFS_SB_FEAT_RO_COMPAT_ALL \
>  		(XFS_SB_FEAT_RO_COMPAT_FINOBT | \
>  		 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
> -		 XFS_SB_FEAT_RO_COMPAT_REFLINK| \
> -		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT)
> +		 XFS_SB_FEAT_RO_COMPAT_REFLINK | \
> +		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT | \
> +		 XFS_SB_FEAT_RO_COMPAT_VERITY)

[Severity: High]
While reviewing the fsverity enablement, I noticed a potential issue in the
error handling path of xfs_fsverity_end_enable().

If xfs_free_eofblocks() fails, such as due to ENOMEM or quota limits, the error
path deletes the verity metadata but appears to leave the permanently committed
XFS_DIFLAG2_VERITY flag set:

fs/xfs/xfs_fsverity.c:xfs_fsverity_end_enable() {
[ ... ]
	ip->i_diflags2 |= XFS_DIFLAG2_VERITY;

	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
	xfs_trans_set_sync(tp);

	error = xfs_trans_commit(tp);
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
	if (error)
		goto out;

	inode_set_flags(inode, S_VERITY, S_VERITY);

	/*
	 * Proactively remove unwritten extents left by COW preallocations and
	 * write preallocation in the merkle tree holes and past descriptor,
	 * and any delayed preallocations
	 */
	error = xfs_free_eofblocks(ip);
out:
	if (error) {
		int	error2;

		error2 = xfs_fsverity_delete_metadata(ip);
[ ... ]
}

Does this leave the on-disk inode permanently marked as an fsverity file while
the metadata has been deleted?

If so, could this cause subsequent attempts to open the file to fail with
-ENODATA or -EIO when fsverity_file_open() fails to find the descriptor,
resulting in localized data loss unless an offline or online repair tool clears
the flag?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=21

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 12/21] xfs: use read ioend for fsverity data verification
  2026-09-18 11:15 ` [PATCH v16 12/21] xfs: use read ioend for fsverity data verification Andrey Albershteyn
  2026-09-18 11:36   ` sashiko-bot
@ 2026-09-22  4:29   ` Darrick J. Wong
  2026-09-22  7:18   ` Christoph Hellwig
  2 siblings, 0 replies; 67+ messages in thread
From: Darrick J. Wong @ 2026-09-22  4:29 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: ebiggers, hch, Carlos Maiolino, fsverity, linux-fsdevel,
	linux-xfs, linux-unionfs, linux-ext4, linux-f2fs-devel,
	linux-btrfs, david

On Fri, Sep 18, 2026 at 01:15:24PM +0200, Andrey Albershteyn wrote:
> Use read ioends for fsverity verification. Do not issue fsverity
> metadata I/O through the same workqueue due to risk of a deadlock by a
> filled workqueue.
> 
> Pass fsverity_info from iomap context down to the ioend as hashtable
> lookups are expensive.
> 
> Add a simple helper to check that this is not fsverity metadata but file
> data that needs verification.
> 
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>

Seems fine to me, though I would like to see hch's blessing on this
since he's written more of the read ioend workqueue completion code.

Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

--D

> ---
>  fs/xfs/xfs_aops.c     | 13 +++++----
>  fs/xfs/xfs_file.c     |  3 +-
>  fs/xfs/xfs_fsverity.c | 11 ++++++++
>  fs/xfs/xfs_fsverity.h | 14 ++++++++++
>  fs/xfs/xfs_ioend.c    | 65 +++++++++++++++++++++++++++++++++++++++----
>  fs/xfs/xfs_ioend.h    |  4 ++-
>  fs/xfs/xfs_super.c    | 15 ++++++++++
>  7 files changed, 112 insertions(+), 13 deletions(-)
> 
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index 19640e4e3ed1..a36f840884b4 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -24,6 +24,7 @@
>  #include "xfs_zone_alloc.h"
>  #include "xfs_rtgroup.h"
>  #include "xfs_fsverity.h"
> +#include <linux/fsverity.h>
>  
>  struct xfs_writepage_ctx {
>  	struct iomap_writepage_ctx ctx;
> @@ -611,7 +612,7 @@ xfs_bio_submit_read(
>  {
>  	xfs_ioend_submit_read(iter->inode, ctx->read_ctx,
>  			ctx->read_ctx_file_offset,
> -			iomap_ioend_flags(&iter->iomap));
> +			iomap_ioend_flags(&iter->iomap), ctx->vi);
>  	ctx->read_ctx = NULL;
>  }
>  
> @@ -623,11 +624,13 @@ static const struct iomap_read_ops xfs_iomap_read_ops = {
>  
>  static inline const struct iomap_read_ops *
>  xfs_get_iomap_read_ops(
> -	const struct address_space	*mapping)
> +	const struct address_space	*mapping,
> +	loff_t				pos)
>  {
>  	struct xfs_inode		*ip = XFS_I(mapping->host);
>  
> -	if (bdev_has_integrity_csum(xfs_inode_buftarg(ip)->bt_bdev))
> +	if (bdev_has_integrity_csum(xfs_inode_buftarg(ip)->bt_bdev) ||
> +	    xfs_fsverity_is_file_data(ip, pos))
>  		return &xfs_iomap_read_ops;
>  	return &iomap_bio_read_ops;
>  }
> @@ -639,7 +642,7 @@ xfs_vm_read_folio(
>  {
>  	struct iomap_read_folio_ctx	ctx = { .cur_folio = folio };
>  
> -	ctx.ops = xfs_get_iomap_read_ops(folio->mapping);
> +	ctx.ops = xfs_get_iomap_read_ops(folio->mapping, folio_pos(folio));
>  	iomap_read_folio(&xfs_read_iomap_ops, &ctx, NULL);
>  	return 0;
>  }
> @@ -650,7 +653,7 @@ xfs_vm_readahead(
>  {
>  	struct iomap_read_folio_ctx	ctx = { .rac = rac };
>  
> -	ctx.ops = xfs_get_iomap_read_ops(rac->mapping),
> +	ctx.ops = xfs_get_iomap_read_ops(rac->mapping, readahead_pos(rac));
>  	iomap_readahead(&xfs_read_iomap_ops, &ctx, NULL);
>  }
>  
> diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
> index 07abf6e8783f..578ca6fb8292 100644
> --- a/fs/xfs/xfs_file.c
> +++ b/fs/xfs/xfs_file.c
> @@ -225,7 +225,8 @@ xfs_dio_read_bounce_submit_io(
>  	loff_t			file_offset)
>  {
>  	xfs_ioend_submit_read(iter->inode, bio, file_offset,
> -			iomap_ioend_flags(&iter->iomap) | IOMAP_IOEND_DIRECT);
> +			iomap_ioend_flags(&iter->iomap) | IOMAP_IOEND_DIRECT,
> +			NULL);
>  }
>  
>  static const struct iomap_dio_ops xfs_dio_read_bounce_ops = {
> diff --git a/fs/xfs/xfs_fsverity.c b/fs/xfs/xfs_fsverity.c
> index e30021c22683..525855fdd30d 100644
> --- a/fs/xfs/xfs_fsverity.c
> +++ b/fs/xfs/xfs_fsverity.c
> @@ -14,9 +14,20 @@
>  #include <linux/fsverity.h>
>  #include <linux/iomap.h>
>  
> +struct kmem_cache *xfs_fsverity_ioend_cache;
> +
>  loff_t
>  xfs_fsverity_metadata_offset(
>  	const struct xfs_inode	*ip)
>  {
>  	return round_up(i_size_read(VFS_IC(ip)), XFS_FSVERITY_START_ALIGN);
>  }
> +
> +bool
> +xfs_fsverity_is_file_data(
> +	const struct xfs_inode	*ip,
> +	loff_t			offset)
> +{
> +	return fsverity_active(VFS_IC(ip)) &&
> +			offset < xfs_fsverity_metadata_offset(ip);
> +}
> diff --git a/fs/xfs/xfs_fsverity.h b/fs/xfs/xfs_fsverity.h
> index c2ab5af89370..836e3a4eb194 100644
> --- a/fs/xfs/xfs_fsverity.h
> +++ b/fs/xfs/xfs_fsverity.h
> @@ -6,15 +6,29 @@
>  #define __XFS_FSVERITY_H__
>  
>  #include "xfs_platform.h"
> +#include <linux/iomap.h>
>  
>  #ifdef CONFIG_FS_VERITY
>  loff_t xfs_fsverity_metadata_offset(const struct xfs_inode *ip);
> +bool xfs_fsverity_is_file_data(const struct xfs_inode *ip, loff_t offset);
>  #else
>  static inline loff_t xfs_fsverity_metadata_offset(const struct xfs_inode *ip)
>  {
>  	WARN_ON_ONCE(1);
>  	return ULLONG_MAX;
>  }
> +static inline bool xfs_fsverity_is_file_data(const struct xfs_inode *ip,
> +					    loff_t offset)
> +{
> +	return false;
> +}
>  #endif	/* CONFIG_FS_VERITY */
>  
> +struct xfs_fsverity_ioend {
> +	struct iomap_ioend	*ioend;
> +	struct work_struct	work;
> +};
> +
> +extern struct kmem_cache *xfs_fsverity_ioend_cache;
> +
>  #endif	/* __XFS_FSVERITY_H__ */
> diff --git a/fs/xfs/xfs_ioend.c b/fs/xfs/xfs_ioend.c
> index e70be5b86f0b..2f46855b052f 100644
> --- a/fs/xfs/xfs_ioend.c
> +++ b/fs/xfs/xfs_ioend.c
> @@ -18,7 +18,26 @@
>  #include "xfs_ioend.h"
>  #include "xfs_error.h"
>  #include "xfs_errortag.h"
> +#include "xfs_fsverity.h"
>  #include <linux/bio-integrity.h>
> +#include <linux/fsverity.h>
> +
> +static void
> +xfs_end_fsverity_io_read(
> +	struct work_struct		*work)
> +{
> +	struct xfs_fsverity_ioend	*fsv_ioend =
> +			container_of(work, struct xfs_fsverity_ioend, work);
> +	struct iomap_ioend		*ioend = fsv_ioend->ioend;
> +	struct bio			*bio = &ioend->io_bio;
> +
> +	kmem_cache_free(xfs_fsverity_ioend_cache, fsv_ioend);
> +
> +	if (!bio->bi_status)
> +		fsverity_verify_bio(ioend->io_vi, bio);
> +
> +	iomap_finish_ioends(ioend, blk_status_to_errno(bio->bi_status));
> +}
>  
>  static void
>  xfs_dio_bounce_end_io(
> @@ -93,12 +112,14 @@ xfs_read_bounce_and_resubmit(
>  
>  static void
>  xfs_end_io_read(
> -	struct bio		*bio)
> +	struct bio			*bio)
>  {
> -	struct iomap_ioend	*ioend = iomap_ioend_from_bio(bio);
> -	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
> -	struct xfs_mount	*mp = ip->i_mount;
> -	int			error = blk_status_to_errno(bio->bi_status);
> +	struct iomap_ioend		*ioend = iomap_ioend_from_bio(bio);
> +	struct xfs_inode		*ip = XFS_I(ioend->io_inode);
> +	struct xfs_mount		*mp = ip->i_mount;
> +	int				error =
> +			blk_status_to_errno(bio->bi_status);
> +	struct xfs_fsverity_ioend	*fsv_ioend;
>  
>  	if (!error && (ioend->io_flags & IOMAP_IOEND_INTEGRITY)) {
>  		error = iomap_ioend_integrity_verify(ioend);
> @@ -117,6 +138,36 @@ xfs_end_io_read(
>  		}
>  	}
>  
> +	/*
> +	 * If we have fsverity and block device integrity attached to this bio,
> +	 * we need to run fsverity verification of data folios from a separate
> +	 * fsverity workqueue. This is necessary to avoid deadlocking due to
> +	 * fsverity issuing more reads of fsverity metadata which would be
> +	 * processed by the same worker in the BIO completion workqueue.
> +	 *
> +	 * Without block device integrity, fsverity metadata IO will not use
> +	 * ioends for completion.
> +	 */
> +	if (IS_ENABLED(CONFIG_FS_VERITY) && !error && ioend->io_vi &&
> +			xfs_fsverity_is_file_data(ip, ioend->io_offset)) {
> +		if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) {
> +			fsv_ioend = kmem_cache_zalloc(xfs_fsverity_ioend_cache,
> +					GFP_KERNEL);
> +			if (!fsv_ioend) {
> +				iomap_finish_ioends(ioend, -ENOMEM);
> +				return;
> +			}
> +			fsv_ioend->ioend = ioend;
> +			INIT_WORK(&fsv_ioend->work, xfs_end_fsverity_io_read);
> +
> +			fsverity_enqueue_verify_work(&fsv_ioend->work);
> +			return;
> +		}
> +
> +		fsverity_verify_bio(ioend->io_vi, &ioend->io_bio);
> +		error = blk_status_to_errno(ioend->io_bio.bi_status);
> +	}
> +
>  	iomap_finish_ioends(ioend, error);
>  }
>  
> @@ -125,13 +176,15 @@ xfs_ioend_submit_read(
>  	struct inode		*inode,
>  	struct bio		*bio,
>  	loff_t			file_offset,
> -	u16			ioend_flags)
> +	u16			ioend_flags,
> +	struct fsverity_info	*vi)
>  {
>  	struct xfs_inode	*ip = XFS_I(inode);
>  	struct xfs_mount	*mp = ip->i_mount;
>  	struct iomap_ioend	*ioend;
>  
>  	ioend = iomap_init_ioend(inode, bio, file_offset, ioend_flags);
> +	ioend->io_vi = vi;
>  	if ((ioend_flags & IOMAP_IOEND_DIRECT) &&
>  	    READ_ONCE(mp->m_read_bounce) == XFS_READ_BOUNCE_ALWAYS) {
>  		iomap_bounce_read(ioend, bdev_logical_block_size(bio->bi_bdev),
> diff --git a/fs/xfs/xfs_ioend.h b/fs/xfs/xfs_ioend.h
> index 7c2a1ea3e6ed..992c248a693a 100644
> --- a/fs/xfs/xfs_ioend.h
> +++ b/fs/xfs/xfs_ioend.h
> @@ -2,6 +2,8 @@
>  #ifndef __XFS_IOEND_H
>  #define __XFS_IOEND_H
>  
> +#include <linux/fsverity.h>
> +
>  /*
>   * Fast and loose check if this write could update the on-disk inode size.
>   */
> @@ -13,6 +15,6 @@ static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
>  
>  void xfs_end_bio(struct bio *bio);
>  void xfs_ioend_submit_read(struct inode *inode, struct bio *bio,
> -		loff_t file_offset, u16 ioend_flags);
> +		loff_t file_offset, u16 ioend_flags, struct fsverity_info *vi);
>  
>  #endif /* __XFS_IOEND_H */
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index fce1d2905c94..aca50130625e 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -2516,8 +2516,20 @@ xfs_init_caches(void)
>  	if (!xfs_parent_args_cache)
>  		goto out_destroy_xmi_cache;
>  
> +#ifdef CONFIG_FS_VERITY
> +	xfs_fsverity_ioend_cache = kmem_cache_create("xfs_fsverity_ioend",
> +					     sizeof(struct xfs_fsverity_ioend),
> +					     0, 0, NULL);
> +	if (!xfs_fsverity_ioend_cache)
> +		goto out_destroy_fsverity_ioend_cache;
> +#endif
> +
>  	return 0;
>  
> +#ifdef CONFIG_FS_VERITY
> + out_destroy_fsverity_ioend_cache:
> +	kmem_cache_destroy(xfs_fsverity_ioend_cache);
> +#endif
>   out_destroy_xmi_cache:
>  	kmem_cache_destroy(xfs_xmi_cache);
>   out_destroy_xmd_cache:
> @@ -2580,6 +2592,9 @@ xfs_destroy_caches(void)
>  	 * destroy caches.
>  	 */
>  	rcu_barrier();
> +#ifdef CONFIG_FS_VERITY
> +	kmem_cache_destroy(xfs_fsverity_ioend_cache);
> +#endif
>  	kmem_cache_destroy(xfs_parent_args_cache);
>  	kmem_cache_destroy(xfs_xmd_cache);
>  	kmem_cache_destroy(xfs_xmi_cache);
> -- 
> 2.54.0
> 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi()
  2026-09-18 11:15 ` [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi() Andrey Albershteyn
  2026-09-18 11:32   ` sashiko-bot
@ 2026-09-22  4:34   ` Darrick J. Wong
  2026-09-22  7:20     ` Christoph Hellwig
  1 sibling, 1 reply; 67+ messages in thread
From: Darrick J. Wong @ 2026-09-22  4:34 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: ebiggers, hch, Carlos Maiolino, fsverity, linux-fsdevel,
	linux-xfs, linux-unionfs, linux-ext4, linux-f2fs-devel,
	linux-btrfs, david

On Fri, Sep 18, 2026 at 01:15:25PM +0200, Andrey Albershteyn wrote:
> The __xfs_bunmapi() is used to remove all extents past EOF. Add
> XFS_BMAPI_UNWRITTEN to remove only unwritten extents, while leaving
> written ones in place. This will be used in following patch to clean up
> unwritten extents on fsverity inodes.
> 
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> ---
>  fs/xfs/libxfs/xfs_bmap.c | 6 +++++-
>  fs/xfs/libxfs/xfs_bmap.h | 6 +++++-
>  2 files changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index cc48f6e20e80..7ba76017ae56 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -5280,6 +5280,10 @@ __xfs_bunmapi(
>  		if (del.br_startoff + del.br_blockcount > end + 1)
>  			del.br_blockcount = end + 1 - del.br_startoff;
>  
> +		if ((flags & XFS_BMAPI_UNWRITTEN) &&
> +				del.br_state != XFS_EXT_UNWRITTEN)

Odd indentation?

		if ((flags & XFS_BMAPI_UNWRITTEN) &&
		    del.br_state != XFS_EXT_UNWRITTEN)

/me again wonders if bunmapi ought to have its own flags instaed of
reusing *some* of bmapi.  But I concede that ought to be a different
cleanup, because figuring out which flags are for bunmapi.  I think it's
REMAP, ATTR_FORK, NODISCARD, and UNWRITTEN?

With the indentation fixed,
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

--D

> +			goto skip;
> +
>  		if (!isrt || (flags & XFS_BMAPI_REMAP))
>  			goto delete;
>  
> @@ -5414,7 +5418,7 @@ __xfs_bunmapi(
>  			if (error)
>  				goto error0;
>  		}
> -
> +skip:
>  		end = del.br_startoff - 1;
>  nodelete:
>  		/*
> diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
> index d5f2729305fa..0f36431d9936 100644
> --- a/fs/xfs/libxfs/xfs_bmap.h
> +++ b/fs/xfs/libxfs/xfs_bmap.h
> @@ -90,6 +90,9 @@ struct xfs_bmalloca {
>  /* Try to align allocations to the extent size hint */
>  #define XFS_BMAPI_EXTSZALIGN	(1u << 11)
>  
> +/* Process unwritten extents only. Used for unmapping */
> +#define XFS_BMAPI_UNWRITTEN	(1u << 12)
> +
>  #define XFS_BMAPI_FLAGS \
>  	{ XFS_BMAPI_ENTIRE,	"ENTIRE" }, \
>  	{ XFS_BMAPI_METADATA,	"METADATA" }, \
> @@ -102,7 +105,8 @@ struct xfs_bmalloca {
>  	{ XFS_BMAPI_COWFORK,	"COWFORK" }, \
>  	{ XFS_BMAPI_NODISCARD,	"NODISCARD" }, \
>  	{ XFS_BMAPI_NORMAP,	"NORMAP" },\
> -	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }
> +	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }, \
> +	{ XFS_BMAPI_UNWRITTEN,	"UNWRITTEN" }
>  
>  
>  static inline int xfs_bmapi_aflag(int w)
> -- 
> 2.54.0
> 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes
  2026-09-18 11:15 ` [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes Andrey Albershteyn
  2026-09-18 11:41   ` sashiko-bot
@ 2026-09-22  4:37   ` Darrick J. Wong
  2026-09-22  8:33     ` Andrey Albershteyn
  1 sibling, 1 reply; 67+ messages in thread
From: Darrick J. Wong @ 2026-09-22  4:37 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: ebiggers, hch, Carlos Maiolino, fsverity, linux-fsdevel,
	linux-xfs, linux-unionfs, linux-ext4, linux-f2fs-devel,
	linux-btrfs, david

On Fri, Sep 18, 2026 at 01:15:26PM +0200, Andrey Albershteyn wrote:
> xfs_free_eofblocks() removes all extents past EOF unless the
> XFS_DIFLAG_PREALLOC or XFS_DIFLAG_APPEND flags are set. This is
> undesired for fsverity as it stores metadata beyond EOF. However, while
> merkle tree is being built, delayed preallocation and unwritten extents
> are used. After metadata construction is done, fsverity inodes becomes
> read-only and won't be changed anymore, none of these unwritten extents
> or preallocations in post EOF region will be used.
> 
> Let xfs_free_eofblocks() be called on fsverity inode as usual to remove
> anything which is not written extent. However, inodes which are
> undergoing merkle tree construction need to be skipped in case reclaim
> takes place.
> 
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> ---
>  fs/xfs/xfs_bmap_util.c | 25 ++++++++++++++++++++++---
>  1 file changed, 22 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> index 268d159339d0..7fd951992557 100644
> --- a/fs/xfs/xfs_bmap_util.c
> +++ b/fs/xfs/xfs_bmap_util.c
> @@ -31,6 +31,7 @@
>  #include "xfs_rtbitmap.h"
>  #include "xfs_rtgroup.h"
>  #include "xfs_zone_alloc.h"
> +#include <linux/fsverity.h>
>  
>  /* Kernel only BMAP related definitions and functions */
>  
> @@ -553,6 +554,13 @@ xfs_can_free_eofblocks(
>  	if (last_fsb <= end_fsb)
>  		return false;
>  
> +	/*
> +	 * Don't clean fsverity inodes which have merkle tree being built, the
> +	 * merkle tree is written beyond EOF
> +	 */
> +	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
> +		return false;
> +
>  	/*
>  	 * Check if there is an post-EOF extent to free.  If there are any
>  	 * delalloc blocks attached to the inode (data fork delalloc
> @@ -579,6 +587,9 @@ xfs_free_eofblocks(
>  	struct xfs_trans	*tp;
>  	struct xfs_mount	*mp = ip->i_mount;
>  	int			error;
> +	int			bmapi_flags = XFS_BMAPI_NODISCARD;
> +	bool			has_verity =
> +			ip->i_diflags2 & XFS_DIFLAG2_VERITY;
>  
>  	/* Attach the dquots to the inode up front. */
>  	error = xfs_qm_dqattach(ip);
> @@ -593,15 +604,20 @@ xfs_free_eofblocks(
>  	 *
>  	 * Note that this means we also leave speculative preallocations in
>  	 * place for preallocated files.
> +	 *
> +	 * Clean up delalloc reservations for fsverity too as those won't be
> +	 * used
>  	 */
> -	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) {
> +	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND) ||
> +			has_verity) {
>  		if (ip->i_delayed_blks) {
>  			xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK,
>  				round_up(XFS_ISIZE(ip), mp->m_sb.sb_blocksize),
>  				LLONG_MAX, NULL);
>  		}
>  		xfs_inode_clear_eofblocks_tag(ip);
> -		return 0;
> +		if (!has_verity)
> +			return 0;
>  	}
>  
>  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> @@ -613,6 +629,9 @@ xfs_free_eofblocks(
>  	xfs_ilock(ip, XFS_ILOCK_EXCL);
>  	xfs_trans_ijoin(tp, ip, 0);
>  
> +	if (has_verity)
> +		bmapi_flags |= XFS_BMAPI_UNWRITTEN;

This ought to have a comment explaining where post-eof unwritten extents
might come from:

	/*
	 * If fs-verity fails to write the full metadata, it can leave
	 * unwritten preallocations after EOF.  Clear all that out.
	 */
	if (has_verity)
		bmapi_flags |= XFS_BMAPI_UNWRITTEN;


With that documented,
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

--D

> +
>  	/*
>  	 * Do not update the on-disk file size.  If we update the on-disk file
>  	 * size and then the system crashes before the contents of the file are
> @@ -620,7 +639,7 @@ xfs_free_eofblocks(
>  	 * bug).
>  	 */
>  	error = xfs_itruncate_extents_flags(&tp, ip, XFS_DATA_FORK,
> -				XFS_ISIZE(ip), XFS_BMAPI_NODISCARD);
> +				XFS_ISIZE(ip), bmapi_flags);
>  	if (error)
>  		goto err_cancel;
>  
> -- 
> 2.54.0
> 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-18 11:15 ` [PATCH v16 15/21] xfs: add fs-verity support Andrey Albershteyn
  2026-09-18 11:43   ` sashiko-bot
@ 2026-09-22  4:45   ` Darrick J. Wong
  2026-09-22  9:27     ` Andrey Albershteyn
  2026-09-22  7:22   ` Christoph Hellwig
  2 siblings, 1 reply; 67+ messages in thread
From: Darrick J. Wong @ 2026-09-22  4:45 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: ebiggers, hch, Carlos Maiolino, fsverity, linux-fsdevel,
	linux-xfs, linux-unionfs, linux-ext4, linux-f2fs-devel,
	linux-btrfs, david

On Fri, Sep 18, 2026 at 01:15:27PM +0200, Andrey Albershteyn wrote:
> Add integration with fs-verity. XFS stores fs-verity descriptor and
> Merkle tree in the inode data fork at first block aligned to 64k past
> EOF.
> 
> The Merkle tree reading/writing is done through iomap interface. The
> data itself is read to the inode's page cache. When XFS reads from this
> region iomap doesn't call into fsverity to verify it against Merkle
> tree. For data, verification is done at ioend completion in a workqueue.
> 
> When fs-verity is enabled on an inode, the XFS_IVERITY_CONSTRUCTION
> flag is set meaning that the Merkle tree is being build. The
> initialization ends with storing of verity descriptor and setting
> inode on-disk flag (XFS_DIFLAG2_VERITY). Lastly, the
> XFS_IVERITY_CONSTRUCTION is dropped and I_VERITY is set on inode.
> 
> The descriptor is stored in a new block aligned to 64k after the last
> Merkle tree block. The size of the descriptor is stored at the end of
> the last descriptor block (descriptor can be multiple blocks).
> 
> Pro-actively remove any unwritten extents as we use last extent to
> locate descriptor.
> 
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/xfs_fsverity.c | 363 +++++++++++++++++++++++++++++++++++++++++-
>  fs/xfs/xfs_fsverity.h |   2 +
>  fs/xfs/xfs_message.c  |   4 +
>  fs/xfs/xfs_message.h  |   1 +
>  fs/xfs/xfs_mount.h    |   2 +
>  fs/xfs/xfs_super.c    |   7 +
>  6 files changed, 378 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/xfs_fsverity.c b/fs/xfs/xfs_fsverity.c
> index 525855fdd30d..e62c9a5d0e1d 100644
> --- a/fs/xfs/xfs_fsverity.c
> +++ b/fs/xfs/xfs_fsverity.c
> @@ -6,13 +6,22 @@
>  #include "xfs_fs.h"
>  #include "xfs_shared.h"
>  #include "xfs_format.h"
> -#include "xfs_log_format.h"
>  #include "xfs_trans_resv.h"
>  #include "xfs_mount.h"
>  #include "xfs_inode.h"
> +#include "xfs_log_format.h"
> +#include "xfs_trans.h"
> +#include "xfs_trace.h"
> +#include "xfs_quota.h"
>  #include "xfs_fsverity.h"
> +#include "xfs_iomap.h"
> +#include "xfs_error.h"
> +#include "xfs_health.h"
> +#include "xfs_bmap_util.h"
> +#include "xfs_icache.h"
>  #include <linux/fsverity.h>
>  #include <linux/iomap.h>
> +#include <linux/pagemap.h>
>  
>  struct kmem_cache *xfs_fsverity_ioend_cache;
>  
> @@ -31,3 +40,355 @@ xfs_fsverity_is_file_data(
>  	return fsverity_active(VFS_IC(ip)) &&
>  			offset < xfs_fsverity_metadata_offset(ip);
>  }
> +
> +/*
> + * Retrieve the verity descriptor.
> + */
> +static int
> +xfs_fsverity_get_descriptor(
> +	struct inode		*inode,
> +	void			*buf,
> +	size_t			buf_size)
> +{
> +	struct xfs_inode	*ip = XFS_I(inode);
> +	struct xfs_mount	*mp = ip->i_mount;
> +	__be32			d_desc_size;
> +	u32			desc_size;
> +	u64			desc_size_pos;
> +	int			error;
> +	u64			desc_pos;
> +	struct xfs_bmbt_irec	rec;
> +	int			is_empty;
> +	uint32_t		blocksize = i_blocksize(VFS_I(ip));
> +	xfs_fileoff_t		last_block_offset;
> +
> +	ASSERT(inode->i_flags & S_VERITY);
> +	xfs_ilock(ip, XFS_ILOCK_SHARED);
> +	error = xfs_bmap_last_extent(NULL, ip, XFS_DATA_FORK, &rec, &is_empty);
> +	xfs_iunlock(ip, XFS_ILOCK_SHARED);
> +	if (error)
> +		return error;
> +
> +	if (is_empty)
> +		return -ENODATA;
> +
> +	last_block_offset =
> +		XFS_FSB_TO_B(mp, rec.br_startoff + rec.br_blockcount);
> +	if (last_block_offset <= xfs_fsverity_metadata_offset(ip))
> +		return -ENODATA;
> +
> +	desc_size_pos = last_block_offset - sizeof(__be32);
> +	error = fsverity_pagecache_read(inode, (char *)&d_desc_size,
> +			sizeof(d_desc_size), desc_size_pos);
> +	if (error)
> +		return error;
> +
> +	desc_size = be32_to_cpu(d_desc_size);
> +	if (XFS_IS_CORRUPT(mp, desc_size > FS_VERITY_MAX_DESCRIPTOR_SIZE))
> +		return -ERANGE;
> +	if (XFS_IS_CORRUPT(mp, desc_size > desc_size_pos))
> +		return -ERANGE;
> +
> +	if (!buf_size)
> +		return desc_size;
> +
> +	if (XFS_IS_CORRUPT(mp, desc_size > buf_size))
> +		return -ERANGE;
> +
> +	desc_pos = round_down(desc_size_pos - desc_size, blocksize);
> +	if (desc_pos < xfs_fsverity_metadata_offset(ip))
> +		return -ERANGE;
> +
> +	error = fsverity_pagecache_read(inode, buf, desc_size, desc_pos);
> +	if (error)
> +		return error;
> +
> +	return desc_size;
> +}
> +
> +static int
> +xfs_fsverity_write_descriptor(
> +	struct file		*file,
> +	const void		*desc,
> +	u32			desc_size,
> +	u64			merkle_tree_size)
> +{
> +	int			error;
> +	struct inode		*inode = file_inode(file);
> +	struct xfs_inode	*ip = XFS_I(inode);
> +	unsigned int		blksize = ip->i_mount->m_sb.sb_blocksize;
> +	u64			tree_last_block =
> +			xfs_fsverity_metadata_offset(ip) + merkle_tree_size;
> +	u64			desc_pos =
> +			round_up(tree_last_block, XFS_FSVERITY_START_ALIGN);
> +	u64			desc_end = desc_pos + desc_size;
> +	__be32			desc_size_disk = cpu_to_be32(desc_size);
> +	u64			desc_size_pos =
> +			round_up(desc_end + sizeof(desc_size_disk), blksize) -
> +			sizeof(desc_size_disk);
> +
> +	error = iomap_fsverity_write(file, desc_size_pos, sizeof(__be32),
> +			(const void *)&desc_size_disk,
> +			&xfs_buffered_write_iomap_ops,
> +			&xfs_iomap_write_ops);
> +	if (error)
> +		return error;
> +
> +	return iomap_fsverity_write(file, desc_pos, desc_size, desc,
> +			&xfs_buffered_write_iomap_ops,
> +			&xfs_iomap_write_ops);
> +}
> +
> +/*
> + * Try to remove all the fsverity metadata after a failed enablement.
> + */
> +static int
> +xfs_fsverity_delete_metadata(
> +	struct xfs_inode	*ip)
> +{
> +	struct xfs_trans	*tp;
> +	struct xfs_mount	*mp = ip->i_mount;
> +	int			error;
> +
> +	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
> +	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> +	if (error) {
> +		xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);
> +		return error;
> +	}
> +
> +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> +	xfs_trans_ijoin(tp, ip, 0);
> +
> +	truncate_inode_pages(VFS_I(ip)->i_mapping, XFS_ISIZE(ip));
> +
> +	/*
> +	 * We remove post EOF data, no need to update i_size as fsverity
> +	 * didn't move i_size in the first place
> +	 */
> +	error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, XFS_ISIZE(ip));
> +	if (error)
> +		goto err_cancel;
> +
> +	error = xfs_trans_commit(tp);
> +	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_ILOCK_EXCL);
> +	return error;
> +err_cancel:
> +	xfs_trans_cancel(tp);
> +	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_ILOCK_EXCL);
> +	return error;
> +}
> +
> +
> +/*
> + * Prepare to enable fsverity by clearing old metadata.
> + */
> +static int
> +xfs_fsverity_begin_enable(
> +	struct file		*filp)
> +{
> +	struct inode		*inode = file_inode(filp);
> +	struct xfs_inode	*ip = XFS_I(inode);
> +	int			error;
> +
> +	xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
> +
> +	if (IS_DAX(inode) || ip->i_diflags2 & XFS_DIFLAG2_DAX)
> +		return -EINVAL;
> +
> +	if (inode->i_size > XFS_FSVERITY_LARGEST_FILE)
> +		return -EFBIG;
> +
> +	/*
> +	 * Flush pagecache before building Merkle tree. Inode is locked and no
> +	 * further writes will happen to the file except fsverity metadata
> +	 */
> +	error = filemap_write_and_wait(inode->i_mapping);
> +	if (error)
> +		return error;
> +
> +	if (xfs_iflags_test_and_set(ip, XFS_VERITY_CONSTRUCTION))
> +		return -EBUSY;
> +
> +	error = xfs_qm_dqattach(ip);
> +	if (error)
> +		goto out_clear;
> +
> +	error = xfs_fsverity_delete_metadata(ip);
> +	if (error)
> +		goto out_clear;
> +	return error;
> +out_clear:
> +	xfs_iflags_clear(ip, XFS_VERITY_CONSTRUCTION);
> +	return error;
> +}
> +
> +/*
> + * Complete (or fail) the process of enabling fsverity.
> + */
> +static int
> +xfs_fsverity_end_enable(
> +	struct file		*file,
> +	const void		*desc,
> +	size_t			desc_size,
> +	u64			merkle_tree_size)
> +{
> +	struct inode		*inode = file_inode(file);
> +	struct xfs_inode	*ip = XFS_I(inode);
> +	struct xfs_mount	*mp = ip->i_mount;
> +	struct xfs_trans	*tp;
> +	int			error = 0;
> +	loff_t			range_start = xfs_fsverity_metadata_offset(ip);
> +
> +	xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
> +
> +	/* fs-verity failed, just cleanup */
> +	if (desc == NULL) {
> +		error = xfs_fsverity_delete_metadata(ip);
> +		goto out;
> +	}
> +
> +	error = xfs_fsverity_write_descriptor(file, desc, desc_size,
> +			merkle_tree_size);
> +	if (error)
> +		goto out;
> +
> +	/*
> +	 * Wait for Merkle tree get written to disk before setting on-disk inode
> +	 * flag and clearing XFS_VERITY_CONSTRUCTION
> +	 */
> +	error = filemap_write_and_wait_range(inode->i_mapping, range_start,
> +			LLONG_MAX);
> +	if (error)
> +		goto out;
> +
> +	/*
> +	 * Set fsverity inode flag
> +	 */
> +	error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_ichange,
> +			0, 0, false, &tp);
> +	if (error)
> +		goto out;
> +
> +	/*
> +	 * Ensure that we've persisted the verity information before we enable
> +	 * it on the inode and tell the caller we have sealed the inode.
> +	 */
> +	ip->i_diflags2 |= XFS_DIFLAG2_VERITY;
> +
> +	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
> +	xfs_trans_set_sync(tp);
> +
> +	error = xfs_trans_commit(tp);
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +	if (error)
> +		goto out;
> +
> +	inode_set_flags(inode, S_VERITY, S_VERITY);
> +
> +	/*
> +	 * Proactively remove unwritten extents left by COW preallocations and
> +	 * write preallocation in the merkle tree holes and past descriptor,
> +	 * and any delayed preallocations
> +	 */
> +	error = xfs_free_eofblocks(ip);
> +out:
> +	if (error) {
> +		int	error2;
> +
> +		error2 = xfs_fsverity_delete_metadata(ip);
> +		if (error2)
> +			xfs_alert(ip->i_mount,
> +"ino 0x%llx failed to clean up new fsverity metadata, err %d",
> +					I_INO(ip), error2);

We just set S_VERITY on the VFS inoe, but then xfs_free_eofblocks fails.
Should we clear S_VERITY?

Does xfs_fsverity_delete_metadata need to clear XFS_DIFLAG2_VERITY?

> +	}
> +
> +	xfs_iflags_clear(ip, XFS_VERITY_CONSTRUCTION);
> +	return error;
> +}
> +
> +/*
> + * Retrieve a merkle tree block.
> + */
> +static struct page *
> +xfs_fsverity_read_merkle(
> +	struct inode		*inode,
> +	pgoff_t			index)
> +{
> +	index += xfs_fsverity_metadata_offset(XFS_I(inode)) >> PAGE_SHIFT;
> +
> +	return generic_read_merkle_tree_page(inode, index);
> +}
> +
> +/*
> + * Retrieve a merkle tree block.
> + */
> +static void
> +xfs_fsverity_readahead_merkle_tree(
> +	struct inode		*inode,
> +	pgoff_t			index,
> +	unsigned long		nr_pages)
> +{
> +	index += xfs_fsverity_metadata_offset(XFS_I(inode)) >> PAGE_SHIFT;
> +
> +	generic_readahead_merkle_tree(inode, index, nr_pages);
> +}
> +
> +/*
> + * Write a merkle tree block.
> + */
> +static int
> +xfs_fsverity_write_merkle(
> +	struct file		*file,
> +	const void		*buf,
> +	u64			pos,
> +	unsigned int		size,
> +	const u8		*zero_digest,
> +	unsigned int		digest_size)
> +{
> +	struct inode		*inode = file_inode(file);
> +	struct xfs_inode	*ip = XFS_I(inode);
> +	loff_t			position = pos +
> +		xfs_fsverity_metadata_offset(ip);
> +
> +	if (position + size > inode->i_sb->s_maxbytes)
> +		return -EFBIG;
> +
> +	/*
> +	 * If this is a block full of hashes of zeroed blocks, don't bother
> +	 * storing the block. We can synthesize them later.
> +	 *
> +	 * However, do this only in case Merkle tree block == fs block size.
> +	 * Iomap synthesizes these blocks based on holes in the merkle tree. We
> +	 * won't be able to tell if something need to be synthesizes for the
> +	 * range in the fs block. For example, for 4k filesystem block
> +	 *
> +	 *	[ 1k | zero hashes | zero hashes | 1k ]
> +	 *
> +	 * Iomap won't know about these empty blocks.
> +	 */
> +	if (size == ip->i_mount->m_sb.sb_blocksize &&
> +			/*
> +			 * First digest is zero_digest
> +			 */
> +			memcmp(buf, zero_digest, digest_size) == 0 &&
> +			/*
> +			 * Every digest is same as previous, thus all are
> +			 * zero_digest
> +			 */
> +			memcmp(buf + digest_size, buf, size - digest_size) == 0)

Weird indentation in this very long if test.  Please just put that in a
separate helper:

static inline bool
xfs_fsverity_is_hashes_of_zeroed_blocks(...)
{
	/*
	 * If this is a block full of hashes of zeroed blocks, don't bother
	 * storing the block. We can synthesize them later.
	 *
	 * However, do this only in case Merkle tree block == fs block size.
	 * Iomap synthesizes these blocks based on holes in the merkle tree. We
	 * won't be able to tell if something need to be synthesizes for the
	 * range in the fs block. For example, for 4k filesystem block
	 *
	 *	[ 1k | zero hashes | zero hashes | 1k ]
	 *
	 * Iomap won't know about these empty blocks.
	 */
	if (size != ip->i_mount->m_sb.sb_blocksize)
		return false;
	/*
	 * First digest is zero_digest
	 */
	if (memcmp(buf, zero_digest, digest_size))
		return false;
	/*
	 * Every digest is same as previous, thus all are
	 * zero_digest
	 */
	return memcmp(buf + digest_size, buf, size - digest_size) == 0;
}

(Don't you need to compare all the hash slots in the buffer??)

Now the callsite becomes this, which I think is cleaner:

	if (xfs_fsverity_is_hashes_of_zeroed_blocks(ip, buf...))
		return 0;

--D

> +		return 0;
> +
> +	return iomap_fsverity_write(file, position, size, buf,
> +			&xfs_buffered_write_iomap_ops,
> +			&xfs_iomap_write_ops);
> +}
> +
> +const struct fsverity_operations xfs_fsverity_ops = {
> +	.begin_enable_verity		= xfs_fsverity_begin_enable,
> +	.end_enable_verity		= xfs_fsverity_end_enable,
> +	.get_verity_descriptor		= xfs_fsverity_get_descriptor,
> +	.read_merkle_tree_page		= xfs_fsverity_read_merkle,
> +	.readahead_merkle_tree		= xfs_fsverity_readahead_merkle_tree,
> +	.write_merkle_tree_block	= xfs_fsverity_write_merkle,
> +};
> diff --git a/fs/xfs/xfs_fsverity.h b/fs/xfs/xfs_fsverity.h
> index 836e3a4eb194..9290dbf576f8 100644
> --- a/fs/xfs/xfs_fsverity.h
> +++ b/fs/xfs/xfs_fsverity.h
> @@ -7,8 +7,10 @@
>  
>  #include "xfs_platform.h"
>  #include <linux/iomap.h>
> +#include <linux/fsverity.h>
>  
>  #ifdef CONFIG_FS_VERITY
> +extern const struct fsverity_operations xfs_fsverity_ops;
>  loff_t xfs_fsverity_metadata_offset(const struct xfs_inode *ip);
>  bool xfs_fsverity_is_file_data(const struct xfs_inode *ip, loff_t offset);
>  #else
> diff --git a/fs/xfs/xfs_message.c b/fs/xfs/xfs_message.c
> index 0243e509a468..44b13d46049c 100644
> --- a/fs/xfs/xfs_message.c
> +++ b/fs/xfs/xfs_message.c
> @@ -149,6 +149,10 @@ xfs_warn_experimental(
>  			.opstate	= XFS_OPSTATE_WARNED_LARP,
>  			.name		= "logged extended attributes",
>  		},
> +		[XFS_EXPERIMENTAL_FSVERITY] = {
> +			.opstate	= XFS_OPSTATE_WARNED_FSVERITY,
> +			.name		= "fsverity",
> +		},
>  	};
>  	ASSERT(feat >= 0 && feat < XFS_EXPERIMENTAL_MAX);
>  	BUILD_BUG_ON(ARRAY_SIZE(features) != XFS_EXPERIMENTAL_MAX);
> diff --git a/fs/xfs/xfs_message.h b/fs/xfs/xfs_message.h
> index 811b885f41c3..364fcc7e42b0 100644
> --- a/fs/xfs/xfs_message.h
> +++ b/fs/xfs/xfs_message.h
> @@ -93,6 +93,7 @@ void xfs_buf_alert_ratelimited(struct xfs_buf *bp, const char *rlmsg,
>  enum xfs_experimental_feat {
>  	XFS_EXPERIMENTAL_SHRINK,
>  	XFS_EXPERIMENTAL_LARP,
> +	XFS_EXPERIMENTAL_FSVERITY,
>  
>  	XFS_EXPERIMENTAL_MAX,
>  };
> diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
> index 60cb54c1a5ec..a3074f060928 100644
> --- a/fs/xfs/xfs_mount.h
> +++ b/fs/xfs/xfs_mount.h
> @@ -599,6 +599,8 @@ __XFS_HAS_FEAT(nouuid, NOUUID)
>  #define XFS_OPSTATE_RESUMING_QUOTAON	18
>  /* (Zoned) GC is in progress */
>  #define XFS_OPSTATE_ZONEGC_RUNNING	20
> +/* Kernel has logged a warning about fsverity support */
> +#define XFS_OPSTATE_WARNED_FSVERITY	21
>  
>  #define __XFS_IS_OPSTATE(name, NAME) \
>  static inline bool xfs_is_ ## name (struct xfs_mount *mp) \
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index aca50130625e..c5075474cd8c 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -30,6 +30,7 @@
>  #include "xfs_filestream.h"
>  #include "xfs_quota.h"
>  #include "xfs_sysfs.h"
> +#include "xfs_fsverity.h"
>  #include "xfs_ondisk.h"
>  #include "xfs_rmap_item.h"
>  #include "xfs_refcount_item.h"
> @@ -1736,6 +1737,9 @@ xfs_fs_fill_super(
>  	sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ;
>  #endif
>  	sb->s_op = &xfs_super_operations;
> +#ifdef CONFIG_FS_VERITY
> +	sb->s_vop = &xfs_fsverity_ops;
> +#endif
>  
>  	/*
>  	 * Delay mount work if the debug hook is set. This is debug
> @@ -1988,6 +1992,9 @@ xfs_fs_fill_super(
>  	if (error)
>  		goto out_filestream_unmount;
>  
> +	if (xfs_has_verity(mp))
> +		xfs_warn_experimental(mp, XFS_EXPERIMENTAL_FSVERITY);
> +
>  	root = igrab(VFS_I(mp->m_rootip));
>  	if (!root) {
>  		error = -ENOENT;
> -- 
> 2.54.0
> 
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi()
  2026-09-18 11:32   ` sashiko-bot
@ 2026-09-22  4:49     ` Darrick J. Wong
  0 siblings, 0 replies; 67+ messages in thread
From: Darrick J. Wong @ 2026-09-22  4:49 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Andrey Albershteyn, linux-ext4, tytso, dsterba

On Fri, Sep 18, 2026 at 11:32:12AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Critical] xfs: XFS_BMAPI_UNWRITTEN corrupts data on realtime files
> --
> 
> commit 03f3992d52956762d29c7c4c4908adeac76b8662
> Author: Andrey Albershteyn <aalbersh@kernel.org>
> xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi()
> 
> The __xfs_bunmapi() function is used to remove all extents past EOF. This
> patch adds XFS_BMAPI_UNWRITTEN to remove only unwritten extents, leaving
> written ones in place. This will be used to clean up unwritten extents on
> fsverity inodes.
> 
> > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > index ba6a9611f6f9d..69364abbfd011 100644
> > --- a/fs/xfs/libxfs/xfs_bmap.c
> > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > @@ -5279,6 +5279,10 @@ __xfs_bunmapi(
> >  		if (del.br_startoff + del.br_blockcount > end + 1)
> >  			del.br_blockcount = end + 1 - del.br_startoff;
> >  
> > +		if ((flags & XFS_BMAPI_UNWRITTEN) &&
> > +				del.br_state != XFS_EXT_UNWRITTEN)
> > +			goto skip;
> > +
> >  		if (!isrt || (flags & XFS_BMAPI_REMAP))
> >  			goto delete;
> >  
> 
> [Severity: Critical]
> Does introducing the XFS_BMAPI_UNWRITTEN flag cause data corruption on
> realtime files due to how __xfs_bunmapi() handles unwritten extents?
> 
> If a user creates a realtime file, writes data so that EOF is not aligned
> to the realtime extent size, and preallocates space after EOF (creating an
> unwritten extent), subsequent calls to clean up with XFS_BMAPI_UNWRITTEN
> (such as enabling fs-verity) will process the front-misaligned unwritten
> extent.
> 
> Because __xfs_bunmapi() falls through to the legacy realtime misalignment
> logic for unwritten extents:
> 
> 			} else if (del.br_state == XFS_EXT_UNWRITTEN) {
> 				struct xfs_bmbt_irec	prev;
> 				...
> 				/*
> 				 * This one is already unwritten.
> 				 * It must have a written left neighbor.
> 				 * Unwrite the killed part of that one and
> 				 * try again.
> 				 */
> 				...
> 				prev.br_state = XFS_EXT_UNWRITTEN;
> 				error = xfs_bmap_add_extent_unwritten_real(tp,
> 						ip, whichfork, &icur, &cur,
> 						&prev, &logflags);
> 
> Could this legacy truncation logic forcefully unwrite the written left
> neighbor (the valid user data before EOF) to form a full realtime extent,
> effectively destroying the mapping to valid user data?

Yes.  Either we don't support fsverity with rt files when rtextsize > 1,
or I guess we have to figure out something else?  Only removing full
rtextents?  Or just leaving them in place?

--D

> [ ... ]
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=13
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-18 11:43   ` sashiko-bot
@ 2026-09-22  4:50     ` Darrick J. Wong
  0 siblings, 0 replies; 67+ messages in thread
From: Darrick J. Wong @ 2026-09-22  4:50 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Andrey Albershteyn, linux-ext4, dsterba, tytso

On Fri, Sep 18, 2026 at 11:43:07AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] xfs: AB-BA lock inversion in xfs_fsverity_delete_metadata()
> - [High] xfs: verity metadata destroyed after commit on xfs_free_eofblocks failure
> - [Low] xfs: duplicate xfs_fsverity_delete_metadata call on cleanup failure
> --
> 
> commit 2cf0e89721e69aa45ac23d60792bafc833b86f51
> Author: Andrey Albershteyn <aalbersh@kernel.org>
> 
> xfs: add fs-verity support
> 
> This commit integrates fs-verity with XFS, storing the Merkle tree and 
> descriptor in the data fork past EOF. It sets up the enablement process
> and handles read and write operations for the verity metadata.
> 
> > --- a/fs/xfs/xfs_fsverity.c
> > +++ b/fs/xfs/xfs_fsverity.c
> [ ... ]
> > +static int
> > +xfs_fsverity_delete_metadata(
> > +	struct xfs_inode	*ip)
> > +{
> > +	struct xfs_trans	*tp;
> > +	struct xfs_mount	*mp = ip->i_mount;
> > +	int			error;
> > +
> > +	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
> > +	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> > +	if (error) {
> > +		xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);
> > +		return error;
> > +	}
> > +
> > +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> > +	xfs_trans_ijoin(tp, ip, 0);
> > +
> > +	truncate_inode_pages(VFS_I(ip)->i_mapping, XFS_ISIZE(ip));
> 
> [Severity: High]
> Does this sequence create a lock inversion between the page/folio locks and
> the XFS_ILOCK?
> 
> XFS lock ordering rules require folio locks to be acquired before the log space
> reservation (xfs_trans_alloc) and the XFS_ILOCK. Because truncate_inode_pages
> sequentially locks folios, calling it while holding XFS_ILOCK_EXCL could cause
> an AB-BA deadlock if a background thread (like writeback) has locked a folio
> and is concurrently trying to acquire the XFS_ILOCK for block mapping.
> 
> Could truncate_inode_pages be moved outside the transaction and before
> acquiring XFS_ILOCK_EXCL?

Yes, it should.

> 
> [ ... ]
> > +static int
> > +xfs_fsverity_end_enable(
> > +	struct file		*file,
> > +	const void		*desc,
> > +	size_t			desc_size,
> > +	u64			merkle_tree_size)
> > +{
> > +	struct inode		*inode = file_inode(file);
> > +	struct xfs_inode	*ip = XFS_I(inode);
> > +	struct xfs_mount	*mp = ip->i_mount;
> > +	struct xfs_trans	*tp;
> > +	int			error = 0;
> > +	loff_t			range_start = xfs_fsverity_metadata_offset(ip);
> > +
> > +	xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
> > +
> > +	/* fs-verity failed, just cleanup */
> > +	if (desc == NULL) {
> > +		error = xfs_fsverity_delete_metadata(ip);
> > +		goto out;
> > +	}
> [ ... ]
> > +	/*
> > +	 * Ensure that we've persisted the verity information before we enable
> > +	 * it on the inode and tell the caller we have sealed the inode.
> > +	 */
> > +	ip->i_diflags2 |= XFS_DIFLAG2_VERITY;
> > +
> > +	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
> > +	xfs_trans_set_sync(tp);
> > +
> > +	error = xfs_trans_commit(tp);
> > +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> > +	if (error)
> > +		goto out;
> > +
> > +	inode_set_flags(inode, S_VERITY, S_VERITY);
> > +
> > +	/*
> > +	 * Proactively remove unwritten extents left by COW preallocations and
> > +	 * write preallocation in the merkle tree holes and past descriptor,
> > +	 * and any delayed preallocations
> > +	 */
> > +	error = xfs_free_eofblocks(ip);
> > +out:
> > +	if (error) {
> > +		int	error2;
> > +
> > +		error2 = xfs_fsverity_delete_metadata(ip);
> 
> [Severity: Low]
> Is it intentional to call xfs_fsverity_delete_metadata a second time if the
> initial early cleanup fails?
> 
> If desc is NULL at the start of the function, xfs_fsverity_delete_metadata is
> called. If that call returns an error, it jumps to the out label. Because
> error is preserved, this block executes and calls xfs_fsverity_delete_metadata
> again with the exact same failed state.
> 
> [Severity: High]
> Can a failure in xfs_free_eofblocks cause the metadata of a fully committed
> verity inode to be destroyed?
> 
> If the verity enablement is successful, the transaction is committed via
> xfs_trans_commit and the inode is permanently marked with XFS_DIFLAG2_VERITY.
> If the subsequent post-commit optimization call to xfs_free_eofblocks fails
> (for example, due to memory constraints or space issues), it returns an error,
> which leads directly to this unconditional cleanup path.
> 
> This deletes the Merkle tree and descriptor extents. Because the on-disk flag
> was already committed, the file remains verity-enabled but loses its metadata.
> This would permanently fail verity validation and lock the user out of their
> file.
> 
> Should this cleanup be skipped if the transaction has already been committed?
> 
> > +		if (error2)
> > +			xfs_alert(ip->i_mount,
> > +"ino 0x%llx failed to clean up new fsverity metadata, err %d",
> > +					I_INO(ip), error2);
> > +	}
> > +
> > +	xfs_iflags_clear(ip, XFS_VERITY_CONSTRUCTION);
> > +	return error;
> > +}
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/20260918111539.1003439-1-aalbersh@kernel.org?part=15
> 

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree
  2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
                   ` (20 preceding siblings ...)
  2026-09-18 11:15 ` [PATCH v16 21/21] xfs: enable ro-compat fs-verity flag Andrey Albershteyn
@ 2026-09-22  4:51 ` Darrick J. Wong
  2026-09-22  9:00   ` Andrey Albershteyn
  21 siblings, 1 reply; 67+ messages in thread
From: Darrick J. Wong @ 2026-09-22  4:51 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: ebiggers, hch, Carlos Maiolino, fsverity, linux-fsdevel,
	linux-xfs, linux-unionfs, linux-ext4, linux-f2fs-devel,
	linux-btrfs, david

On Fri, Sep 18, 2026 at 01:15:12PM +0200, Andrey Albershteyn wrote:
> Hi all,
> 
> This is next revision of fsverity for XFS.
> 
> The main change from v15 is addion of kmem cache
> xfs_fsverity_ioend_cache instead of adding work_struct to iomap's ioend
> struct.

FWIW, Sashiko found at least one or two bugs, but the reports only got
echoed to the ext4 list.  Perhaps it's time to discuss allowing it to
email linux-xfs for bugs that it claims to have found in xfs code?

--D

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 11/21] xfs: handle fsverity I/O in write/read path
  2026-09-18 11:15 ` [PATCH v16 11/21] xfs: handle fsverity I/O in write/read path Andrey Albershteyn
  2026-09-18 11:38   ` sashiko-bot
@ 2026-09-22  7:15   ` Christoph Hellwig
  1 sibling, 0 replies; 67+ messages in thread
From: Christoph Hellwig @ 2026-09-22  7:15 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: djwong, ebiggers, Carlos Maiolino, fsverity, linux-fsdevel,
	linux-xfs, linux-unionfs, linux-ext4, linux-f2fs-devel,
	linux-btrfs, david

On Fri, Sep 18, 2026 at 01:15:23PM +0200, Andrey Albershteyn wrote:
> For write/writeback set IOMAP_F_FSVERITY flag telling iomap to not
> update inode size and to not skip folios beyond EOF.
> 
> Initiate fsverity writeback with IOMAP_F_FSVERITY set to tell iomap
> should not skip folio that is dirty beyond EOF.
> 
> In read path let iomap know that we are reading fsverity metadata. So,
> treat holes in the tree as request to synthesize tree blocks and hole
> after descriptor as end of the fsverity region.
> 
> Introduce a new inode flag meaning that merkle tree is being build on
> the inode.

Sashiko had some comments on this that did not sound entirely stupid
to me.


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 12/21] xfs: use read ioend for fsverity data verification
  2026-09-18 11:15 ` [PATCH v16 12/21] xfs: use read ioend for fsverity data verification Andrey Albershteyn
  2026-09-18 11:36   ` sashiko-bot
  2026-09-22  4:29   ` Darrick J. Wong
@ 2026-09-22  7:18   ` Christoph Hellwig
  2026-09-22  9:13     ` Andrey Albershteyn
  2 siblings, 1 reply; 67+ messages in thread
From: Christoph Hellwig @ 2026-09-22  7:18 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: djwong, ebiggers, Carlos Maiolino, fsverity, linux-fsdevel,
	linux-xfs, linux-unionfs, linux-ext4, linux-f2fs-devel,
	linux-btrfs, david

On Fri, Sep 18, 2026 at 01:15:24PM +0200, Andrey Albershteyn wrote:
> +	if (IS_ENABLED(CONFIG_FS_VERITY) && !error && ioend->io_vi &&
> +			xfs_fsverity_is_file_data(ip, ioend->io_offset)) {
> +		if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) {
> +			fsv_ioend = kmem_cache_zalloc(xfs_fsverity_ioend_cache,
> +					GFP_KERNEL);
> +			if (!fsv_ioend) {

This needs a mempool to be deadlock safe.

> +				iomap_finish_ioends(ioend, -ENOMEM);
> +				return;
> +			}
> +			fsv_ioend->ioend = ioend;
> +			INIT_WORK(&fsv_ioend->work, xfs_end_fsverity_io_read);
> +
> +			fsverity_enqueue_verify_work(&fsv_ioend->work);
> +			return;
> +		}
> +
> +		fsverity_verify_bio(ioend->io_vi, &ioend->io_bio);
> +		error = blk_status_to_errno(ioend->io_bio.bi_status);

And I think you always need to do the offload because fsverity can
synchronously read metadata.


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi()
  2026-09-22  4:34   ` Darrick J. Wong
@ 2026-09-22  7:20     ` Christoph Hellwig
  2026-09-22  8:32       ` Andrey Albershteyn
  0 siblings, 1 reply; 67+ messages in thread
From: Christoph Hellwig @ 2026-09-22  7:20 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Andrey Albershteyn, ebiggers, hch, Carlos Maiolino, fsverity,
	linux-fsdevel, linux-xfs, linux-unionfs, linux-ext4,
	linux-f2fs-devel, linux-btrfs, david

On Mon, Sep 21, 2026 at 09:34:02PM -0700, Darrick J. Wong wrote:
> > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > index cc48f6e20e80..7ba76017ae56 100644
> > --- a/fs/xfs/libxfs/xfs_bmap.c
> > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > @@ -5280,6 +5280,10 @@ __xfs_bunmapi(
> >  		if (del.br_startoff + del.br_blockcount > end + 1)
> >  			del.br_blockcount = end + 1 - del.br_startoff;
> >  
> > +		if ((flags & XFS_BMAPI_UNWRITTEN) &&
> > +				del.br_state != XFS_EXT_UNWRITTEN)
> 
> Odd indentation?
> 
> 		if ((flags & XFS_BMAPI_UNWRITTEN) &&
> 		    del.br_state != XFS_EXT_UNWRITTEN)

Yeah.

> /me again wonders if bunmapi ought to have its own flags instaed of
> reusing *some* of bmapi.  But I concede that ought to be a different
> cleanup, because figuring out which flags are for bunmapi.  I think it's
> REMAP, ATTR_FORK, NODISCARD, and UNWRITTEN?

I think that would create more confusion than it helps.  OTOH asserting
the valid flag would be helful.

Sashiko complains about interactions with the weird rtextent bunmap
behavior.  Should we disallow fsverity with a larger than fsb
rtextensive to not bother with this?  Btw, did you test this series
with RT classic and or zoned?


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-18 11:15 ` [PATCH v16 15/21] xfs: add fs-verity support Andrey Albershteyn
  2026-09-18 11:43   ` sashiko-bot
  2026-09-22  4:45   ` Darrick J. Wong
@ 2026-09-22  7:22   ` Christoph Hellwig
  2026-09-22  8:56     ` Andrey Albershteyn
  2 siblings, 1 reply; 67+ messages in thread
From: Christoph Hellwig @ 2026-09-22  7:22 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: djwong, ebiggers, hch, Carlos Maiolino, fsverity, linux-fsdevel,
	linux-xfs, linux-unionfs, linux-ext4, linux-f2fs-devel,
	linux-btrfs, david

On Fri, Sep 18, 2026 at 01:15:27PM +0200, Andrey Albershteyn wrote:
> +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> +	xfs_trans_ijoin(tp, ip, 0);
> +
> +	truncate_inode_pages(VFS_I(ip)->i_mapping, XFS_ISIZE(ip));

We can't call truncate_inode_pages with the ilock held.

I also don't see what this is trying to protect to start with.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi()
  2026-09-22  7:20     ` Christoph Hellwig
@ 2026-09-22  8:32       ` Andrey Albershteyn
  0 siblings, 0 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-22  8:32 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J. Wong, Andrey Albershteyn, ebiggers, hch,
	Carlos Maiolino, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

On 2026-09-22 00:20:02, Christoph Hellwig wrote:
> On Mon, Sep 21, 2026 at 09:34:02PM -0700, Darrick J. Wong wrote:
> > > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > > index cc48f6e20e80..7ba76017ae56 100644
> > > --- a/fs/xfs/libxfs/xfs_bmap.c
> > > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > > @@ -5280,6 +5280,10 @@ __xfs_bunmapi(
> > >  		if (del.br_startoff + del.br_blockcount > end + 1)
> > >  			del.br_blockcount = end + 1 - del.br_startoff;
> > >  
> > > +		if ((flags & XFS_BMAPI_UNWRITTEN) &&
> > > +				del.br_state != XFS_EXT_UNWRITTEN)
> > 
> > Odd indentation?
> > 
> > 		if ((flags & XFS_BMAPI_UNWRITTEN) &&
> > 		    del.br_state != XFS_EXT_UNWRITTEN)
> 
> Yeah.
> 
> > /me again wonders if bunmapi ought to have its own flags instaed of
> > reusing *some* of bmapi.  But I concede that ought to be a different
> > cleanup, because figuring out which flags are for bunmapi.  I think it's
> > REMAP, ATTR_FORK, NODISCARD, and UNWRITTEN?
> 
> I think that would create more confusion than it helps.  OTOH asserting
> the valid flag would be helful.
> 
> Sashiko complains about interactions with the weird rtextent bunmap
> behavior.  Should we disallow fsverity with a larger than fsb
> rtextensive to not bother with this?  Btw, did you test this series
> with RT classic and or zoned?

yeah, I will disallow it

I did many revisions back, but haven't tested recent versions, I
will test with rt for the next version

-- 
- Andrey


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes
  2026-09-22  4:37   ` Darrick J. Wong
@ 2026-09-22  8:33     ` Andrey Albershteyn
  2026-09-22 11:29       ` Andrey Albershteyn
  0 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-22  8:33 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Andrey Albershteyn, ebiggers, hch, Carlos Maiolino, fsverity,
	linux-fsdevel, linux-xfs, linux-unionfs, linux-ext4,
	linux-f2fs-devel, linux-btrfs, david

On 2026-09-21 21:37:50, Darrick J. Wong wrote:
> On Fri, Sep 18, 2026 at 01:15:26PM +0200, Andrey Albershteyn wrote:
> > xfs_free_eofblocks() removes all extents past EOF unless the
> > XFS_DIFLAG_PREALLOC or XFS_DIFLAG_APPEND flags are set. This is
> > undesired for fsverity as it stores metadata beyond EOF. However, while
> > merkle tree is being built, delayed preallocation and unwritten extents
> > are used. After metadata construction is done, fsverity inodes becomes
> > read-only and won't be changed anymore, none of these unwritten extents
> > or preallocations in post EOF region will be used.
> > 
> > Let xfs_free_eofblocks() be called on fsverity inode as usual to remove
> > anything which is not written extent. However, inodes which are
> > undergoing merkle tree construction need to be skipped in case reclaim
> > takes place.
> > 
> > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> > ---
> >  fs/xfs/xfs_bmap_util.c | 25 ++++++++++++++++++++++---
> >  1 file changed, 22 insertions(+), 3 deletions(-)
> > 
> > diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> > index 268d159339d0..7fd951992557 100644
> > --- a/fs/xfs/xfs_bmap_util.c
> > +++ b/fs/xfs/xfs_bmap_util.c
> > @@ -31,6 +31,7 @@
> >  #include "xfs_rtbitmap.h"
> >  #include "xfs_rtgroup.h"
> >  #include "xfs_zone_alloc.h"
> > +#include <linux/fsverity.h>
> >  
> >  /* Kernel only BMAP related definitions and functions */
> >  
> > @@ -553,6 +554,13 @@ xfs_can_free_eofblocks(
> >  	if (last_fsb <= end_fsb)
> >  		return false;
> >  
> > +	/*
> > +	 * Don't clean fsverity inodes which have merkle tree being built, the
> > +	 * merkle tree is written beyond EOF
> > +	 */
> > +	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
> > +		return false;
> > +
> >  	/*
> >  	 * Check if there is an post-EOF extent to free.  If there are any
> >  	 * delalloc blocks attached to the inode (data fork delalloc
> > @@ -579,6 +587,9 @@ xfs_free_eofblocks(
> >  	struct xfs_trans	*tp;
> >  	struct xfs_mount	*mp = ip->i_mount;
> >  	int			error;
> > +	int			bmapi_flags = XFS_BMAPI_NODISCARD;
> > +	bool			has_verity =
> > +			ip->i_diflags2 & XFS_DIFLAG2_VERITY;
> >  
> >  	/* Attach the dquots to the inode up front. */
> >  	error = xfs_qm_dqattach(ip);
> > @@ -593,15 +604,20 @@ xfs_free_eofblocks(
> >  	 *
> >  	 * Note that this means we also leave speculative preallocations in
> >  	 * place for preallocated files.
> > +	 *
> > +	 * Clean up delalloc reservations for fsverity too as those won't be
> > +	 * used
> >  	 */
> > -	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) {
> > +	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND) ||
> > +			has_verity) {
> >  		if (ip->i_delayed_blks) {
> >  			xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK,
> >  				round_up(XFS_ISIZE(ip), mp->m_sb.sb_blocksize),
> >  				LLONG_MAX, NULL);
> >  		}
> >  		xfs_inode_clear_eofblocks_tag(ip);
> > -		return 0;
> > +		if (!has_verity)
> > +			return 0;
> >  	}
> >  
> >  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> > @@ -613,6 +629,9 @@ xfs_free_eofblocks(
> >  	xfs_ilock(ip, XFS_ILOCK_EXCL);
> >  	xfs_trans_ijoin(tp, ip, 0);
> >  
> > +	if (has_verity)
> > +		bmapi_flags |= XFS_BMAPI_UNWRITTEN;
> 
> This ought to have a comment explaining where post-eof unwritten extents
> might come from:
> 
> 	/*
> 	 * If fs-verity fails to write the full metadata, it can leave
> 	 * unwritten preallocations after EOF.  Clear all that out.
> 	 */
> 	if (has_verity)
> 		bmapi_flags |= XFS_BMAPI_UNWRITTEN;
> 
> 
> With that documented,
> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

Sure, I will add the comment
Thanks!

-- 
- Andrey


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-22  7:22   ` Christoph Hellwig
@ 2026-09-22  8:56     ` Andrey Albershteyn
  2026-09-22 12:32       ` Christoph Hellwig
  0 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-22  8:56 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Andrey Albershteyn, djwong, ebiggers, hch, Carlos Maiolino,
	fsverity, linux-fsdevel, linux-xfs, linux-unionfs, linux-ext4,
	linux-f2fs-devel, linux-btrfs, david

On 2026-09-22 00:22:52, Christoph Hellwig wrote:
> On Fri, Sep 18, 2026 at 01:15:27PM +0200, Andrey Albershteyn wrote:
> > +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> > +	xfs_trans_ijoin(tp, ip, 0);
> > +
> > +	truncate_inode_pages(VFS_I(ip)->i_mapping, XFS_ISIZE(ip));
> 
> We can't call truncate_inode_pages with the ilock held.
> 
> I also don't see what this is trying to protect to start with.
> 

I added this to remove stale pages created post-EOF, so, writeback
doesn't try to flush them latter (but now I see that writeback will
fail to map them to extents anyway). Also, if EOF gets extended
these pages would be exposed as they are up-to-date which would be
unexpected garbage.

-- 
- Andrey


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree
  2026-09-22  4:51 ` [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Darrick J. Wong
@ 2026-09-22  9:00   ` Andrey Albershteyn
  0 siblings, 0 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-22  9:00 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Andrey Albershteyn, ebiggers, hch, Carlos Maiolino, fsverity,
	linux-fsdevel, linux-xfs, linux-unionfs, linux-ext4,
	linux-f2fs-devel, linux-btrfs, david

On 2026-09-21 21:51:44, Darrick J. Wong wrote:
> On Fri, Sep 18, 2026 at 01:15:12PM +0200, Andrey Albershteyn wrote:
> > Hi all,
> > 
> > This is next revision of fsverity for XFS.
> > 
> > The main change from v15 is addion of kmem cache
> > xfs_fsverity_ioend_cache instead of adding work_struct to iomap's ioend
> > struct.
> 
> FWIW, Sashiko found at least one or two bugs, but the reports only got
> echoed to the ext4 list.  Perhaps it's time to discuss allowing it to
> email linux-xfs for bugs that it claims to have found in xfs code?

I'm checking its report for fsverity series, but for other patches
not that often. So, maybe it would be useful for "critical" and
"high" priority issues (not sure if it's possible to configure it
that way).

*this time sashiko sent me a bunch of "no regression found" emails
which I think are not necessary

-- 
- Andrey


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 12/21] xfs: use read ioend for fsverity data verification
  2026-09-22  7:18   ` Christoph Hellwig
@ 2026-09-22  9:13     ` Andrey Albershteyn
  2026-09-22 12:31       ` Christoph Hellwig
  0 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-22  9:13 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Andrey Albershteyn, djwong, ebiggers, Carlos Maiolino, fsverity,
	linux-fsdevel, linux-xfs, linux-unionfs, linux-ext4,
	linux-f2fs-devel, linux-btrfs, david

On 2026-09-22 00:18:14, Christoph Hellwig wrote:
> On Fri, Sep 18, 2026 at 01:15:24PM +0200, Andrey Albershteyn wrote:
> > +	if (IS_ENABLED(CONFIG_FS_VERITY) && !error && ioend->io_vi &&
> > +			xfs_fsverity_is_file_data(ip, ioend->io_offset)) {
> > +		if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) {
> > +			fsv_ioend = kmem_cache_zalloc(xfs_fsverity_ioend_cache,
> > +					GFP_KERNEL);
> > +			if (!fsv_ioend) {
> 
> This needs a mempool to be deadlock safe.
> 
> > +				iomap_finish_ioends(ioend, -ENOMEM);
> > +				return;
> > +			}
> > +			fsv_ioend->ioend = ioend;
> > +			INIT_WORK(&fsv_ioend->work, xfs_end_fsverity_io_read);
> > +
> > +			fsverity_enqueue_verify_work(&fsv_ioend->work);
> > +			return;
> > +		}
> > +
> > +		fsverity_verify_bio(ioend->io_vi, &ioend->io_bio);
> > +		error = blk_status_to_errno(ioend->io_bio.bi_status);
> 
> And I think you always need to do the offload because fsverity can
> synchronously read metadata.
> 

hmmm not sure what case you see here.

Without integrity:
For data reads we get here. Here, while verifing data page, fsverity
requests reads of merkle tree pages which WILL NOT go through this
path as no read completion is done for them.

With integrity:
For data reads we get here. Here, while verifing data page, fsverity
requests reads of merkle tree pages which WILL go through this
path as they need integrity verification on the read completion.
Therefore, to use this workqueue for merkle tree pages integrity
verification we offload data block verification to fsverity
workqueue.

-- 
- Andrey


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-22  4:45   ` Darrick J. Wong
@ 2026-09-22  9:27     ` Andrey Albershteyn
  0 siblings, 0 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-22  9:27 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Andrey Albershteyn, ebiggers, hch, Carlos Maiolino, fsverity,
	linux-fsdevel, linux-xfs, linux-unionfs, linux-ext4,
	linux-f2fs-devel, linux-btrfs, david

On 2026-09-21 21:45:59, Darrick J. Wong wrote:
> On Fri, Sep 18, 2026 at 01:15:27PM +0200, Andrey Albershteyn wrote:
> > Add integration with fs-verity. XFS stores fs-verity descriptor and
> > Merkle tree in the inode data fork at first block aligned to 64k past
> > EOF.
> > 
> > The Merkle tree reading/writing is done through iomap interface. The
> > data itself is read to the inode's page cache. When XFS reads from this
> > region iomap doesn't call into fsverity to verify it against Merkle
> > tree. For data, verification is done at ioend completion in a workqueue.
> > 
> > When fs-verity is enabled on an inode, the XFS_IVERITY_CONSTRUCTION
> > flag is set meaning that the Merkle tree is being build. The
> > initialization ends with storing of verity descriptor and setting
> > inode on-disk flag (XFS_DIFLAG2_VERITY). Lastly, the
> > XFS_IVERITY_CONSTRUCTION is dropped and I_VERITY is set on inode.
> > 
> > The descriptor is stored in a new block aligned to 64k after the last
> > Merkle tree block. The size of the descriptor is stored at the end of
> > the last descriptor block (descriptor can be multiple blocks).
> > 
> > Pro-actively remove any unwritten extents as we use last extent to
> > locate descriptor.
> > 
> > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> > Reviewed-by: Christoph Hellwig <hch@lst.de>
> > ---
> >  fs/xfs/xfs_fsverity.c | 363 +++++++++++++++++++++++++++++++++++++++++-
> >  fs/xfs/xfs_fsverity.h |   2 +
> >  fs/xfs/xfs_message.c  |   4 +
> >  fs/xfs/xfs_message.h  |   1 +
> >  fs/xfs/xfs_mount.h    |   2 +
> >  fs/xfs/xfs_super.c    |   7 +
> >  6 files changed, 378 insertions(+), 1 deletion(-)
> > 
> > diff --git a/fs/xfs/xfs_fsverity.c b/fs/xfs/xfs_fsverity.c
> > index 525855fdd30d..e62c9a5d0e1d 100644
> > --- a/fs/xfs/xfs_fsverity.c
> > +++ b/fs/xfs/xfs_fsverity.c
> > @@ -6,13 +6,22 @@
> >  #include "xfs_fs.h"
> >  #include "xfs_shared.h"
> >  #include "xfs_format.h"
> > -#include "xfs_log_format.h"
> >  #include "xfs_trans_resv.h"
> >  #include "xfs_mount.h"
> >  #include "xfs_inode.h"
> > +#include "xfs_log_format.h"
> > +#include "xfs_trans.h"
> > +#include "xfs_trace.h"
> > +#include "xfs_quota.h"
> >  #include "xfs_fsverity.h"
> > +#include "xfs_iomap.h"
> > +#include "xfs_error.h"
> > +#include "xfs_health.h"
> > +#include "xfs_bmap_util.h"
> > +#include "xfs_icache.h"
> >  #include <linux/fsverity.h>
> >  #include <linux/iomap.h>
> > +#include <linux/pagemap.h>
> >  
> >  struct kmem_cache *xfs_fsverity_ioend_cache;
> >  
> > @@ -31,3 +40,355 @@ xfs_fsverity_is_file_data(
> >  	return fsverity_active(VFS_IC(ip)) &&
> >  			offset < xfs_fsverity_metadata_offset(ip);
> >  }
> > +
> > +/*
> > + * Retrieve the verity descriptor.
> > + */
> > +static int
> > +xfs_fsverity_get_descriptor(
> > +	struct inode		*inode,
> > +	void			*buf,
> > +	size_t			buf_size)
> > +{
> > +	struct xfs_inode	*ip = XFS_I(inode);
> > +	struct xfs_mount	*mp = ip->i_mount;
> > +	__be32			d_desc_size;
> > +	u32			desc_size;
> > +	u64			desc_size_pos;
> > +	int			error;
> > +	u64			desc_pos;
> > +	struct xfs_bmbt_irec	rec;
> > +	int			is_empty;
> > +	uint32_t		blocksize = i_blocksize(VFS_I(ip));
> > +	xfs_fileoff_t		last_block_offset;
> > +
> > +	ASSERT(inode->i_flags & S_VERITY);
> > +	xfs_ilock(ip, XFS_ILOCK_SHARED);
> > +	error = xfs_bmap_last_extent(NULL, ip, XFS_DATA_FORK, &rec, &is_empty);
> > +	xfs_iunlock(ip, XFS_ILOCK_SHARED);
> > +	if (error)
> > +		return error;
> > +
> > +	if (is_empty)
> > +		return -ENODATA;
> > +
> > +	last_block_offset =
> > +		XFS_FSB_TO_B(mp, rec.br_startoff + rec.br_blockcount);
> > +	if (last_block_offset <= xfs_fsverity_metadata_offset(ip))
> > +		return -ENODATA;
> > +
> > +	desc_size_pos = last_block_offset - sizeof(__be32);
> > +	error = fsverity_pagecache_read(inode, (char *)&d_desc_size,
> > +			sizeof(d_desc_size), desc_size_pos);
> > +	if (error)
> > +		return error;
> > +
> > +	desc_size = be32_to_cpu(d_desc_size);
> > +	if (XFS_IS_CORRUPT(mp, desc_size > FS_VERITY_MAX_DESCRIPTOR_SIZE))
> > +		return -ERANGE;
> > +	if (XFS_IS_CORRUPT(mp, desc_size > desc_size_pos))
> > +		return -ERANGE;
> > +
> > +	if (!buf_size)
> > +		return desc_size;
> > +
> > +	if (XFS_IS_CORRUPT(mp, desc_size > buf_size))
> > +		return -ERANGE;
> > +
> > +	desc_pos = round_down(desc_size_pos - desc_size, blocksize);
> > +	if (desc_pos < xfs_fsverity_metadata_offset(ip))
> > +		return -ERANGE;
> > +
> > +	error = fsverity_pagecache_read(inode, buf, desc_size, desc_pos);
> > +	if (error)
> > +		return error;
> > +
> > +	return desc_size;
> > +}
> > +
> > +static int
> > +xfs_fsverity_write_descriptor(
> > +	struct file		*file,
> > +	const void		*desc,
> > +	u32			desc_size,
> > +	u64			merkle_tree_size)
> > +{
> > +	int			error;
> > +	struct inode		*inode = file_inode(file);
> > +	struct xfs_inode	*ip = XFS_I(inode);
> > +	unsigned int		blksize = ip->i_mount->m_sb.sb_blocksize;
> > +	u64			tree_last_block =
> > +			xfs_fsverity_metadata_offset(ip) + merkle_tree_size;
> > +	u64			desc_pos =
> > +			round_up(tree_last_block, XFS_FSVERITY_START_ALIGN);
> > +	u64			desc_end = desc_pos + desc_size;
> > +	__be32			desc_size_disk = cpu_to_be32(desc_size);
> > +	u64			desc_size_pos =
> > +			round_up(desc_end + sizeof(desc_size_disk), blksize) -
> > +			sizeof(desc_size_disk);
> > +
> > +	error = iomap_fsverity_write(file, desc_size_pos, sizeof(__be32),
> > +			(const void *)&desc_size_disk,
> > +			&xfs_buffered_write_iomap_ops,
> > +			&xfs_iomap_write_ops);
> > +	if (error)
> > +		return error;
> > +
> > +	return iomap_fsverity_write(file, desc_pos, desc_size, desc,
> > +			&xfs_buffered_write_iomap_ops,
> > +			&xfs_iomap_write_ops);
> > +}
> > +
> > +/*
> > + * Try to remove all the fsverity metadata after a failed enablement.
> > + */
> > +static int
> > +xfs_fsverity_delete_metadata(
> > +	struct xfs_inode	*ip)
> > +{
> > +	struct xfs_trans	*tp;
> > +	struct xfs_mount	*mp = ip->i_mount;
> > +	int			error;
> > +
> > +	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
> > +	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> > +	if (error) {
> > +		xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);
> > +		return error;
> > +	}
> > +
> > +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> > +	xfs_trans_ijoin(tp, ip, 0);
> > +
> > +	truncate_inode_pages(VFS_I(ip)->i_mapping, XFS_ISIZE(ip));
> > +
> > +	/*
> > +	 * We remove post EOF data, no need to update i_size as fsverity
> > +	 * didn't move i_size in the first place
> > +	 */
> > +	error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, XFS_ISIZE(ip));
> > +	if (error)
> > +		goto err_cancel;
> > +
> > +	error = xfs_trans_commit(tp);
> > +	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_ILOCK_EXCL);
> > +	return error;
> > +err_cancel:
> > +	xfs_trans_cancel(tp);
> > +	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_ILOCK_EXCL);
> > +	return error;
> > +}
> > +
> > +
> > +/*
> > + * Prepare to enable fsverity by clearing old metadata.
> > + */
> > +static int
> > +xfs_fsverity_begin_enable(
> > +	struct file		*filp)
> > +{
> > +	struct inode		*inode = file_inode(filp);
> > +	struct xfs_inode	*ip = XFS_I(inode);
> > +	int			error;
> > +
> > +	xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
> > +
> > +	if (IS_DAX(inode) || ip->i_diflags2 & XFS_DIFLAG2_DAX)
> > +		return -EINVAL;
> > +
> > +	if (inode->i_size > XFS_FSVERITY_LARGEST_FILE)
> > +		return -EFBIG;
> > +
> > +	/*
> > +	 * Flush pagecache before building Merkle tree. Inode is locked and no
> > +	 * further writes will happen to the file except fsverity metadata
> > +	 */
> > +	error = filemap_write_and_wait(inode->i_mapping);
> > +	if (error)
> > +		return error;
> > +
> > +	if (xfs_iflags_test_and_set(ip, XFS_VERITY_CONSTRUCTION))
> > +		return -EBUSY;
> > +
> > +	error = xfs_qm_dqattach(ip);
> > +	if (error)
> > +		goto out_clear;
> > +
> > +	error = xfs_fsverity_delete_metadata(ip);
> > +	if (error)
> > +		goto out_clear;
> > +	return error;
> > +out_clear:
> > +	xfs_iflags_clear(ip, XFS_VERITY_CONSTRUCTION);
> > +	return error;
> > +}
> > +
> > +/*
> > + * Complete (or fail) the process of enabling fsverity.
> > + */
> > +static int
> > +xfs_fsverity_end_enable(
> > +	struct file		*file,
> > +	const void		*desc,
> > +	size_t			desc_size,
> > +	u64			merkle_tree_size)
> > +{
> > +	struct inode		*inode = file_inode(file);
> > +	struct xfs_inode	*ip = XFS_I(inode);
> > +	struct xfs_mount	*mp = ip->i_mount;
> > +	struct xfs_trans	*tp;
> > +	int			error = 0;
> > +	loff_t			range_start = xfs_fsverity_metadata_offset(ip);
> > +
> > +	xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
> > +
> > +	/* fs-verity failed, just cleanup */
> > +	if (desc == NULL) {
> > +		error = xfs_fsverity_delete_metadata(ip);
> > +		goto out;
> > +	}
> > +
> > +	error = xfs_fsverity_write_descriptor(file, desc, desc_size,
> > +			merkle_tree_size);
> > +	if (error)
> > +		goto out;
> > +
> > +	/*
> > +	 * Wait for Merkle tree get written to disk before setting on-disk inode
> > +	 * flag and clearing XFS_VERITY_CONSTRUCTION
> > +	 */
> > +	error = filemap_write_and_wait_range(inode->i_mapping, range_start,
> > +			LLONG_MAX);
> > +	if (error)
> > +		goto out;
> > +
> > +	/*
> > +	 * Set fsverity inode flag
> > +	 */
> > +	error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_ichange,
> > +			0, 0, false, &tp);
> > +	if (error)
> > +		goto out;
> > +
> > +	/*
> > +	 * Ensure that we've persisted the verity information before we enable
> > +	 * it on the inode and tell the caller we have sealed the inode.
> > +	 */
> > +	ip->i_diflags2 |= XFS_DIFLAG2_VERITY;
> > +
> > +	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
> > +	xfs_trans_set_sync(tp);
> > +
> > +	error = xfs_trans_commit(tp);
> > +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> > +	if (error)
> > +		goto out;
> > +
> > +	inode_set_flags(inode, S_VERITY, S_VERITY);
> > +
> > +	/*
> > +	 * Proactively remove unwritten extents left by COW preallocations and
> > +	 * write preallocation in the merkle tree holes and past descriptor,
> > +	 * and any delayed preallocations
> > +	 */
> > +	error = xfs_free_eofblocks(ip);
> > +out:
> > +	if (error) {
> > +		int	error2;
> > +
> > +		error2 = xfs_fsverity_delete_metadata(ip);
> > +		if (error2)
> > +			xfs_alert(ip->i_mount,
> > +"ino 0x%llx failed to clean up new fsverity metadata, err %d",
> > +					I_INO(ip), error2);
> 
> We just set S_VERITY on the VFS inoe, but then xfs_free_eofblocks fails.
> Should we clear S_VERITY?
> 
> Does xfs_fsverity_delete_metadata need to clear XFS_DIFLAG2_VERITY?

Yes, that's right. I will look if it's fine to move all the clean up
logic to xfs_fsverity_delete_metadata().

> 
> > +	}
> > +
> > +	xfs_iflags_clear(ip, XFS_VERITY_CONSTRUCTION);
> > +	return error;
> > +}
> > +
> > +/*
> > + * Retrieve a merkle tree block.
> > + */
> > +static struct page *
> > +xfs_fsverity_read_merkle(
> > +	struct inode		*inode,
> > +	pgoff_t			index)
> > +{
> > +	index += xfs_fsverity_metadata_offset(XFS_I(inode)) >> PAGE_SHIFT;
> > +
> > +	return generic_read_merkle_tree_page(inode, index);
> > +}
> > +
> > +/*
> > + * Retrieve a merkle tree block.
> > + */
> > +static void
> > +xfs_fsverity_readahead_merkle_tree(
> > +	struct inode		*inode,
> > +	pgoff_t			index,
> > +	unsigned long		nr_pages)
> > +{
> > +	index += xfs_fsverity_metadata_offset(XFS_I(inode)) >> PAGE_SHIFT;
> > +
> > +	generic_readahead_merkle_tree(inode, index, nr_pages);
> > +}
> > +
> > +/*
> > + * Write a merkle tree block.
> > + */
> > +static int
> > +xfs_fsverity_write_merkle(
> > +	struct file		*file,
> > +	const void		*buf,
> > +	u64			pos,
> > +	unsigned int		size,
> > +	const u8		*zero_digest,
> > +	unsigned int		digest_size)
> > +{
> > +	struct inode		*inode = file_inode(file);
> > +	struct xfs_inode	*ip = XFS_I(inode);
> > +	loff_t			position = pos +
> > +		xfs_fsverity_metadata_offset(ip);
> > +
> > +	if (position + size > inode->i_sb->s_maxbytes)
> > +		return -EFBIG;
> > +
> > +	/*
> > +	 * If this is a block full of hashes of zeroed blocks, don't bother
> > +	 * storing the block. We can synthesize them later.
> > +	 *
> > +	 * However, do this only in case Merkle tree block == fs block size.
> > +	 * Iomap synthesizes these blocks based on holes in the merkle tree. We
> > +	 * won't be able to tell if something need to be synthesizes for the
> > +	 * range in the fs block. For example, for 4k filesystem block
> > +	 *
> > +	 *	[ 1k | zero hashes | zero hashes | 1k ]
> > +	 *
> > +	 * Iomap won't know about these empty blocks.
> > +	 */
> > +	if (size == ip->i_mount->m_sb.sb_blocksize &&
> > +			/*
> > +			 * First digest is zero_digest
> > +			 */
> > +			memcmp(buf, zero_digest, digest_size) == 0 &&
> > +			/*
> > +			 * Every digest is same as previous, thus all are
> > +			 * zero_digest
> > +			 */
> > +			memcmp(buf + digest_size, buf, size - digest_size) == 0)
> 
> Weird indentation in this very long if test.  Please just put that in a
> separate helper:
> 
> static inline bool
> xfs_fsverity_is_hashes_of_zeroed_blocks(...)
> {
> 	/*
> 	 * If this is a block full of hashes of zeroed blocks, don't bother
> 	 * storing the block. We can synthesize them later.
> 	 *
> 	 * However, do this only in case Merkle tree block == fs block size.
> 	 * Iomap synthesizes these blocks based on holes in the merkle tree. We
> 	 * won't be able to tell if something need to be synthesizes for the
> 	 * range in the fs block. For example, for 4k filesystem block
> 	 *
> 	 *	[ 1k | zero hashes | zero hashes | 1k ]
> 	 *
> 	 * Iomap won't know about these empty blocks.
> 	 */
> 	if (size != ip->i_mount->m_sb.sb_blocksize)
> 		return false;
> 	/*
> 	 * First digest is zero_digest
> 	 */
> 	if (memcmp(buf, zero_digest, digest_size))
> 		return false;
> 	/*
> 	 * Every digest is same as previous, thus all are
> 	 * zero_digest
> 	 */
> 	return memcmp(buf + digest_size, buf, size - digest_size) == 0;
> }

sure

> 
> (Don't you need to compare all the hash slots in the buffer??)

Oh that's neat optimization suggested by Eric, you can check that
first hash is correct (first memcmp) and then compare second to the
first, third to the second etc. to check that they're all the same
(second memcmp).

-- 
- Andrey


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes
  2026-09-22  8:33     ` Andrey Albershteyn
@ 2026-09-22 11:29       ` Andrey Albershteyn
  0 siblings, 0 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-22 11:29 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: Darrick J. Wong, ebiggers, hch, Carlos Maiolino, fsverity,
	linux-fsdevel, linux-xfs, linux-unionfs, linux-ext4,
	linux-f2fs-devel, linux-btrfs, david

On 2026-09-22 10:33:22, Andrey Albershteyn wrote:
> On 2026-09-21 21:37:50, Darrick J. Wong wrote:
> > On Fri, Sep 18, 2026 at 01:15:26PM +0200, Andrey Albershteyn wrote:
> > > xfs_free_eofblocks() removes all extents past EOF unless the
> > > XFS_DIFLAG_PREALLOC or XFS_DIFLAG_APPEND flags are set. This is
> > > undesired for fsverity as it stores metadata beyond EOF. However, while
> > > merkle tree is being built, delayed preallocation and unwritten extents
> > > are used. After metadata construction is done, fsverity inodes becomes
> > > read-only and won't be changed anymore, none of these unwritten extents
> > > or preallocations in post EOF region will be used.
> > > 
> > > Let xfs_free_eofblocks() be called on fsverity inode as usual to remove
> > > anything which is not written extent. However, inodes which are
> > > undergoing merkle tree construction need to be skipped in case reclaim
> > > takes place.
> > > 
> > > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> > > ---
> > >  fs/xfs/xfs_bmap_util.c | 25 ++++++++++++++++++++++---
> > >  1 file changed, 22 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> > > index 268d159339d0..7fd951992557 100644
> > > --- a/fs/xfs/xfs_bmap_util.c
> > > +++ b/fs/xfs/xfs_bmap_util.c
> > > @@ -31,6 +31,7 @@
> > >  #include "xfs_rtbitmap.h"
> > >  #include "xfs_rtgroup.h"
> > >  #include "xfs_zone_alloc.h"
> > > +#include <linux/fsverity.h>
> > >  
> > >  /* Kernel only BMAP related definitions and functions */
> > >  
> > > @@ -553,6 +554,13 @@ xfs_can_free_eofblocks(
> > >  	if (last_fsb <= end_fsb)
> > >  		return false;
> > >  
> > > +	/*
> > > +	 * Don't clean fsverity inodes which have merkle tree being built, the
> > > +	 * merkle tree is written beyond EOF
> > > +	 */
> > > +	if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION))
> > > +		return false;
> > > +
> > >  	/*
> > >  	 * Check if there is an post-EOF extent to free.  If there are any
> > >  	 * delalloc blocks attached to the inode (data fork delalloc
> > > @@ -579,6 +587,9 @@ xfs_free_eofblocks(
> > >  	struct xfs_trans	*tp;
> > >  	struct xfs_mount	*mp = ip->i_mount;
> > >  	int			error;
> > > +	int			bmapi_flags = XFS_BMAPI_NODISCARD;
> > > +	bool			has_verity =
> > > +			ip->i_diflags2 & XFS_DIFLAG2_VERITY;
> > >  
> > >  	/* Attach the dquots to the inode up front. */
> > >  	error = xfs_qm_dqattach(ip);
> > > @@ -593,15 +604,20 @@ xfs_free_eofblocks(
> > >  	 *
> > >  	 * Note that this means we also leave speculative preallocations in
> > >  	 * place for preallocated files.
> > > +	 *
> > > +	 * Clean up delalloc reservations for fsverity too as those won't be
> > > +	 * used
> > >  	 */
> > > -	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) {
> > > +	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND) ||
> > > +			has_verity) {
> > >  		if (ip->i_delayed_blks) {
> > >  			xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK,
> > >  				round_up(XFS_ISIZE(ip), mp->m_sb.sb_blocksize),
> > >  				LLONG_MAX, NULL);
> > >  		}
> > >  		xfs_inode_clear_eofblocks_tag(ip);
> > > -		return 0;
> > > +		if (!has_verity)
> > > +			return 0;
> > >  	}
> > >  
> > >  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> > > @@ -613,6 +629,9 @@ xfs_free_eofblocks(
> > >  	xfs_ilock(ip, XFS_ILOCK_EXCL);
> > >  	xfs_trans_ijoin(tp, ip, 0);
> > >  
> > > +	if (has_verity)
> > > +		bmapi_flags |= XFS_BMAPI_UNWRITTEN;
> > 
> > This ought to have a comment explaining where post-eof unwritten extents
> > might come from:
> > 
> > 	/*
> > 	 * If fs-verity fails to write the full metadata, it can leave
> > 	 * unwritten preallocations after EOF.  Clear all that out.
> > 	 */
> > 	if (has_verity)
> > 		bmapi_flags |= XFS_BMAPI_UNWRITTEN;
> > 
> > 
> > With that documented,
> > Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
> 
> Sure, I will add the comment
> Thanks!

I will re-pharese the comment to:

	/*
	 * While fs-verity writes metadata after EOF, it can leave unwritten
	 * preallocations.  Clear all that out.
	 */

The preallocation are left even in good case, in the merkle tree
holes and after fsverity descriptor.

-- 
- Andrey

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 12/21] xfs: use read ioend for fsverity data verification
  2026-09-22  9:13     ` Andrey Albershteyn
@ 2026-09-22 12:31       ` Christoph Hellwig
  2026-09-22 13:17         ` Andrey Albershteyn
  0 siblings, 1 reply; 67+ messages in thread
From: Christoph Hellwig @ 2026-09-22 12:31 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: Christoph Hellwig, Andrey Albershteyn, djwong, ebiggers,
	Carlos Maiolino, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

On Tue, Sep 22, 2026 at 11:13:57AM +0200, Andrey Albershteyn wrote:
> > And I think you always need to do the offload because fsverity can
> > synchronously read metadata.
> > 
> 
> hmmm not sure what case you see here.
> 
> Without integrity:
> For data reads we get here. Here, while verifing data page, fsverity
> requests reads of merkle tree pages which WILL NOT go through this
> path as no read completion is done for them.
> 
> With integrity:
> For data reads we get here. Here, while verifing data page, fsverity
> requests reads of merkle tree pages which WILL go through this
> path as they need integrity verification on the read completion.
> Therefore, to use this workqueue for merkle tree pages integrity
> verification we offload data block verification to fsverity
> workqueue.

Yes, but we want to be able to use the bio defer path for other
things including iomap error handling (I'm about to send a series
for that).  So please always make fsverity do it's thing so that
we don't add really complicated dependency chains.


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-22  8:56     ` Andrey Albershteyn
@ 2026-09-22 12:32       ` Christoph Hellwig
  2026-09-22 13:27         ` Andrey Albershteyn
  0 siblings, 1 reply; 67+ messages in thread
From: Christoph Hellwig @ 2026-09-22 12:32 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: Christoph Hellwig, Andrey Albershteyn, djwong, ebiggers, hch,
	Carlos Maiolino, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

On Tue, Sep 22, 2026 at 10:56:09AM +0200, Andrey Albershteyn wrote:
> On 2026-09-22 00:22:52, Christoph Hellwig wrote:
> > On Fri, Sep 18, 2026 at 01:15:27PM +0200, Andrey Albershteyn wrote:
> > > +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> > > +	xfs_trans_ijoin(tp, ip, 0);
> > > +
> > > +	truncate_inode_pages(VFS_I(ip)->i_mapping, XFS_ISIZE(ip));
> > 
> > We can't call truncate_inode_pages with the ilock held.
> > 
> > I also don't see what this is trying to protect to start with.
> > 
> 
> I added this to remove stale pages created post-EOF, so, writeback
> doesn't try to flush them latter (but now I see that writeback will
> fail to map them to extents anyway). Also, if EOF gets extended
> these pages would be exposed as they are up-to-date which would be
> unexpected garbage.

Yeah, but what is the ilock trying to protect against?  ilock is
the innermost sleeping lock and protects modifications to the
inode fields and the extent mapping.   Higher level synchronization
is done using the iolock and the mmaplock (which is already taken
here).


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 12/21] xfs: use read ioend for fsverity data verification
  2026-09-22 12:31       ` Christoph Hellwig
@ 2026-09-22 13:17         ` Andrey Albershteyn
  0 siblings, 0 replies; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-22 13:17 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Andrey Albershteyn, djwong, ebiggers, Carlos Maiolino, fsverity,
	linux-fsdevel, linux-xfs, linux-unionfs, linux-ext4,
	linux-f2fs-devel, linux-btrfs, david

On 2026-09-22 05:31:36, Christoph Hellwig wrote:
> On Tue, Sep 22, 2026 at 11:13:57AM +0200, Andrey Albershteyn wrote:
> > > And I think you always need to do the offload because fsverity can
> > > synchronously read metadata.
> > > 
> > 
> > hmmm not sure what case you see here.
> > 
> > Without integrity:
> > For data reads we get here. Here, while verifing data page, fsverity
> > requests reads of merkle tree pages which WILL NOT go through this
> > path as no read completion is done for them.
> > 
> > With integrity:
> > For data reads we get here. Here, while verifing data page, fsverity
> > requests reads of merkle tree pages which WILL go through this
> > path as they need integrity verification on the read completion.
> > Therefore, to use this workqueue for merkle tree pages integrity
> > verification we offload data block verification to fsverity
> > workqueue.
> 
> Yes, but we want to be able to use the bio defer path for other
> things including iomap error handling (I'm about to send a series
> for that).  So please always make fsverity do it's thing so that
> we don't add really complicated dependency chains.

ah ok, I see, I will change that

-- 
- Andrey

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-22 12:32       ` Christoph Hellwig
@ 2026-09-22 13:27         ` Andrey Albershteyn
  2026-09-23  4:44           ` Christoph Hellwig
  0 siblings, 1 reply; 67+ messages in thread
From: Andrey Albershteyn @ 2026-09-22 13:27 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Andrey Albershteyn, djwong, ebiggers, hch, Carlos Maiolino,
	fsverity, linux-fsdevel, linux-xfs, linux-unionfs, linux-ext4,
	linux-f2fs-devel, linux-btrfs, david

On 2026-09-22 05:32:53, Christoph Hellwig wrote:
> On Tue, Sep 22, 2026 at 10:56:09AM +0200, Andrey Albershteyn wrote:
> > On 2026-09-22 00:22:52, Christoph Hellwig wrote:
> > > On Fri, Sep 18, 2026 at 01:15:27PM +0200, Andrey Albershteyn wrote:
> > > > +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> > > > +	xfs_trans_ijoin(tp, ip, 0);
> > > > +
> > > > +	truncate_inode_pages(VFS_I(ip)->i_mapping, XFS_ISIZE(ip));
> > > 
> > > We can't call truncate_inode_pages with the ilock held.
> > > 
> > > I also don't see what this is trying to protect to start with.
> > > 
> > 
> > I added this to remove stale pages created post-EOF, so, writeback
> > doesn't try to flush them latter (but now I see that writeback will
> > fail to map them to extents anyway). Also, if EOF gets extended
> > these pages would be exposed as they are up-to-date which would be
> > unexpected garbage.
> 
> Yeah, but what is the ilock trying to protect against?  ilock is
> the innermost sleeping lock and protects modifications to the
> inode fields and the extent mapping.   Higher level synchronization
> is done using the iolock and the mmaplock (which is already taken
> here).
> 

hmm maybe I don't understand what you addressing here but don't I
need ilock for xfs_itruncate_extents() used with this transaction.
So, doing extent removal.

With this function I want to remove post EOF pages from pagecache
and then remove post EOF extents on that inode.

iolock is already hold, mmaplock is for pagecache truncation and
ilock for extents removal.

-- 
- Andrey

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 15/21] xfs: add fs-verity support
  2026-09-22 13:27         ` Andrey Albershteyn
@ 2026-09-23  4:44           ` Christoph Hellwig
  0 siblings, 0 replies; 67+ messages in thread
From: Christoph Hellwig @ 2026-09-23  4:44 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: Christoph Hellwig, Andrey Albershteyn, djwong, ebiggers, hch,
	Carlos Maiolino, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

On Tue, Sep 22, 2026 at 03:27:54PM +0200, Andrey Albershteyn wrote:
> > 
> > Yeah, but what is the ilock trying to protect against?  ilock is
> > the innermost sleeping lock and protects modifications to the
> > inode fields and the extent mapping.   Higher level synchronization
> > is done using the iolock and the mmaplock (which is already taken
> > here).
> > 
> 
> hmm maybe I don't understand what you addressing here but don't I
> need ilock for xfs_itruncate_extents() used with this transaction.
> So, doing extent removal.
> 
> With this function I want to remove post EOF pages from pagecache
> and then remove post EOF extents on that inode.
> 
> iolock is already hold, mmaplock is for pagecache truncation and
> ilock for extents removal.

So there should be no reason to hold it over the call to
truncate_inode_pages.  I.e., you should be able to move the locking
and ijoin below that.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v16 05/21] fsverity: don't allow setting DAX file attribute on fsverity files
  2026-09-18 11:15 ` [PATCH v16 05/21] fsverity: don't allow setting DAX file attribute on fsverity files Andrey Albershteyn
  2026-09-18 11:28   ` sashiko-bot
@ 2026-09-25  4:35   ` Eric Biggers
  1 sibling, 0 replies; 67+ messages in thread
From: Eric Biggers @ 2026-09-25  4:35 UTC (permalink / raw)
  To: Andrey Albershteyn
  Cc: djwong, hch, Carlos Maiolino, fsverity, linux-fsdevel, linux-xfs,
	linux-unionfs, linux-ext4, linux-f2fs-devel, linux-btrfs, david

On Fri, Sep 18, 2026 at 01:15:17PM +0200, Andrey Albershteyn wrote:
> When fsverity is enabled on the file, with FS_IOC_ENABLE_VERITY ioctl(),
> it checks if file has DAX enabled and fails if that's true. However, the
> opposite case is not checked.
> 
> Note, that the only other filesystem supporting DAX and fsverity is
> ext4, and ext4 does check for this case.
> 
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
> ---
>  fs/file_attr.c | 11 ++++++++---
>  1 file changed, 8 insertions(+), 3 deletions(-)

Reviewed-by: Eric Biggers <ebiggers@kernel.org>

- Eric

^ permalink raw reply	[flat|nested] 67+ messages in thread

end of thread, other threads:[~2026-09-25  4:35 UTC | newest]

Thread overview: 67+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-18 11:15 [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Andrey Albershteyn
2026-09-18 11:15 ` [PATCH v16 01/21] fsverity: report validation errors through fserror to fsnotify Andrey Albershteyn
2026-09-18 11:27   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 02/21] fsverity: expose ensure_fsverity_info() Andrey Albershteyn
2026-09-18 11:32   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 03/21] fsverity: pass digest size and hash of the all-zeroes block to ->write Andrey Albershteyn
2026-09-18 11:24   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 04/21] fsverity: hoist pagecache_read from f2fs/ext4 to fsverity Andrey Albershteyn
2026-09-18 11:26   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 05/21] fsverity: don't allow setting DAX file attribute on fsverity files Andrey Albershteyn
2026-09-18 11:28   ` sashiko-bot
2026-09-25  4:35   ` Eric Biggers
2026-09-18 11:15 ` [PATCH v16 06/21] fsverity: hoist statx reporting of fs-verity flag Andrey Albershteyn
2026-09-18 11:28   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 07/21] xfs: introduce fsverity on-disk changes Andrey Albershteyn
2026-09-18 11:34   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 08/21] xfs: don't allow to enable DAX on fs-verity sealed inode Andrey Albershteyn
2026-09-18 11:27   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 09/21] xfs: disable direct read path for fs-verity files Andrey Albershteyn
2026-09-18 11:26   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 10/21] xfs: don't report dio_mem_align and dio_offset_align for fsverity files Andrey Albershteyn
2026-09-18 11:26   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 11/21] xfs: handle fsverity I/O in write/read path Andrey Albershteyn
2026-09-18 11:38   ` sashiko-bot
2026-09-22  7:15   ` Christoph Hellwig
2026-09-18 11:15 ` [PATCH v16 12/21] xfs: use read ioend for fsverity data verification Andrey Albershteyn
2026-09-18 11:36   ` sashiko-bot
2026-09-22  4:29   ` Darrick J. Wong
2026-09-22  7:18   ` Christoph Hellwig
2026-09-22  9:13     ` Andrey Albershteyn
2026-09-22 12:31       ` Christoph Hellwig
2026-09-22 13:17         ` Andrey Albershteyn
2026-09-18 11:15 ` [PATCH v16 13/21] xfs: add XFS_BMAPI_UNWRITTEN to unmap unwritten extents in __xfs_bunmapi() Andrey Albershteyn
2026-09-18 11:32   ` sashiko-bot
2026-09-22  4:49     ` Darrick J. Wong
2026-09-22  4:34   ` Darrick J. Wong
2026-09-22  7:20     ` Christoph Hellwig
2026-09-22  8:32       ` Andrey Albershteyn
2026-09-18 11:15 ` [PATCH v16 14/21] xfs: don't remove written extents past EOF on fsverity inodes Andrey Albershteyn
2026-09-18 11:41   ` sashiko-bot
2026-09-22  4:37   ` Darrick J. Wong
2026-09-22  8:33     ` Andrey Albershteyn
2026-09-22 11:29       ` Andrey Albershteyn
2026-09-18 11:15 ` [PATCH v16 15/21] xfs: add fs-verity support Andrey Albershteyn
2026-09-18 11:43   ` sashiko-bot
2026-09-22  4:50     ` Darrick J. Wong
2026-09-22  4:45   ` Darrick J. Wong
2026-09-22  9:27     ` Andrey Albershteyn
2026-09-22  7:22   ` Christoph Hellwig
2026-09-22  8:56     ` Andrey Albershteyn
2026-09-22 12:32       ` Christoph Hellwig
2026-09-22 13:27         ` Andrey Albershteyn
2026-09-23  4:44           ` Christoph Hellwig
2026-09-18 11:15 ` [PATCH v16 16/21] xfs: initialize fs-verity on file open Andrey Albershteyn
2026-09-18 11:33   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 17/21] xfs: add fs-verity ioctls Andrey Albershteyn
2026-09-18 11:31   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 18/21] xfs: advertise fs-verity being available on filesystem Andrey Albershteyn
2026-09-18 11:30   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 19/21] xfs: check and repair the verity inode flag state Andrey Albershteyn
2026-09-18 11:40   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 20/21] xfs: introduce health state for corrupted fsverity metadata Andrey Albershteyn
2026-09-18 11:34   ` sashiko-bot
2026-09-18 11:15 ` [PATCH v16 21/21] xfs: enable ro-compat fs-verity flag Andrey Albershteyn
2026-09-18 11:44   ` sashiko-bot
2026-09-22  4:51 ` [PATCH v16 00/21] fs-verity support for XFS with post EOF merkle tree Darrick J. Wong
2026-09-22  9:00   ` Andrey Albershteyn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox