From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B40C13DEAF2; Fri, 10 Jul 2026 08:54:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783673642; cv=none; b=PghiTIwfaQXvxzO6+Qxl9BJ919uwv8FKIz6/9+hW/WQ+RzCl1Z0Mpm4ZtSMkPTLsy+ovPTL+zPcKTPT0W9yXGckj120mNSyx1MOedCIz3o0ZGXuz37rzLz1ktLfEXuu0vqiwB2P+IplzgU8safwl6TOUEplwJerXcLzzJjw3TTU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783673642; c=relaxed/simple; bh=fYtjvaiAm+7Jt8csqTjzwiiTzVeFQyBoyAnACh4qy/8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZPM6bSS03Rmk/aZMKGz/TEutI/mMMCQuz7UHOtOUSU99FYx77sSjaEO/jLfTOVjGx0syB4CgpMhgcmGB5ur492GNDdzIByffFup+9GyQYIYGK/YBGpq8o1MDxP4oKBAIDONY3GPcmf70yYbdenfTode5yOvowaFlNiaL/6GeMGU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OOOfULIR; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="OOOfULIR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D3F421F00A3A; Fri, 10 Jul 2026 08:53:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1783673640; bh=sFABs5UbVMeuJixZj1QrkQOQ0CQ/wKZVKUS8iPm4h6M=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=OOOfULIRvJgs5IcJBLv4ujRF4O0j2sJIM83gvlX9GPNAxysIgKKQe7Y9lKvvEd5dz KzF2BHDNTAROoT/rNiH6qMzpIUOJ/p9M5ykuBdM5chzaZ5VoVGDuuwq+gylu/sVoaK t/nk5B8KLx774b9PlSzv7VIvBee4n+X9WL+omPCRlLVZ+Rfsf6JYtK+1b+F6efEyma w6Sv4kgbD8XaLO0GkDifueFhptytbx37dfFo1lh7a3pVVrsrcYwIhOdkjzhrwItvb7 2kss1WBZe4sbrg4PWQKRfEgF/wip/S6qbUPZaWQj55tNrEPBxAaS917xCuA6lOzk1+ YX9+Dr4Sk+yyg== From: Andrey Albershteyn To: linux-xfs@vger.kernel.org, fsverity@lists.linux.dev, linux-fsdevel@vger.kernel.org, ebiggers@kernel.org Cc: Andrey Albershteyn , hch@lst.de, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-btrfs@vger.kernel.org, djwong@kernel.org Subject: [PATCH v11 12/20] xfs: handle fsverity I/O in write/read path Date: Fri, 10 Jul 2026 10:52:40 +0200 Message-ID: <20260710085256.3464201-13-aalbersh@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260710085256.3464201-1-aalbersh@kernel.org> References: <20260710085256.3464201-1-aalbersh@kernel.org> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 --- fs/xfs/Makefile | 1 + fs/xfs/libxfs/xfs_bmap.c | 11 +++++++++++ fs/xfs/xfs_aops.c | 36 +++++++++++++++++++++++++++++++----- fs/xfs/xfs_fsverity.c | 20 ++++++++++++++++++++ fs/xfs/xfs_fsverity.h | 20 ++++++++++++++++++++ fs/xfs/xfs_inode.h | 6 ++++++ fs/xfs/xfs_iomap.c | 23 ++++++++++++++++++----- 7 files changed, 107 insertions(+), 10 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 9f7133e02576..38b7f51e5d84 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -149,6 +149,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 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 51293b6f331f..27ff2b240457 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -22,6 +22,7 @@ #include "xfs_icache.h" #include "xfs_zone_alloc.h" #include "xfs_rtgroup.h" +#include "xfs_fsverity.h" #include struct xfs_writepage_ctx { @@ -350,12 +351,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 @@ -443,7 +448,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: @@ -590,12 +596,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. @@ -619,7 +629,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); @@ -632,7 +642,7 @@ xfs_zoned_map_blocks( xfs_iunlock(ip, XFS_ILOCK_EXCL); wpc->iomap.type = IOMAP_MAPPED; - wpc->iomap.flags = IOMAP_F_DIRTY; + wpc->iomap.flags = iomap_flags | IOMAP_F_DIRTY; wpc->iomap.bdev = mp->m_rtdev_targp->bt_bdev; wpc->iomap.offset = offset; wpc->iomap.length = XFS_FSB_TO_B(mp, count_fsb); @@ -684,6 +694,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, @@ -703,7 +729,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; @@ -716,7 +742,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..0971d2f983db --- /dev/null +++ b/fs/xfs/xfs_fsverity.c @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026 Red Hat, Inc. + */ +#include "xfs_platform.h" +#include "xfs_format.h" +#include "xfs_inode.h" +#include "xfs_shared.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" +#include "xfs_fsverity.h" +#include +#include + +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..5771db2cd797 --- /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 34c1038ebfcd..17ce083591f4 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 225c3de88d03..fba51902416b 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 #define XFS_ALLOC_ALIGN(mp, off) \ (((off) >> mp->m_allocsize_log) << mp->m_allocsize_log) @@ -1589,7 +1591,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); @@ -1599,7 +1602,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; @@ -1607,6 +1609,8 @@ xfs_zoned_buffered_write_iomap_begin( struct xfs_iext_cursor icur; int error = 0; + iomap_flags = iomap_flags | IOMAP_F_SHARED; + ASSERT(!xfs_get_extsz_hint(ip)); ASSERT(!(flags & IOMAP_UNSHARE)); ASSERT(ac); @@ -1803,9 +1807,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)) @@ -2191,12 +2198,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; @@ -2210,8 +2222,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); } const struct iomap_ops xfs_read_iomap_ops = { -- 2.54.0