From patchwork Thu Feb 4 23:21:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Burkov X-Patchwork-Id: 12068923 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 61BBEC433E6 for ; Thu, 4 Feb 2021 23:22:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2194C64FB4 for ; Thu, 4 Feb 2021 23:22:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230000AbhBDXWh (ORCPT ); Thu, 4 Feb 2021 18:22:37 -0500 Received: from out2-smtp.messagingengine.com ([66.111.4.26]:48951 "EHLO out2-smtp.messagingengine.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229977AbhBDXWf (ORCPT ); Thu, 4 Feb 2021 18:22:35 -0500 Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id 340995C0093; Thu, 4 Feb 2021 18:21:48 -0500 (EST) Received: from mailfrontend1 ([10.202.2.162]) by compute2.internal (MEProxy); Thu, 04 Feb 2021 18:21:48 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bur.io; h=from :to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=fm3; bh=58Zunp3Tg7bESTzisIrnIibyce waRC6z+Qrcg47W84Y=; b=mi1Hsvo9t5DqIjLnx0Qaz65H/4+XFM+dImefY/1gWn 9HLd6d2qNZtZbF4IWJ6XRmkea4Wu0YANK0IYWNZxSpe4hctGPBwqCfJpz/o7vAN6 V1kwVDKAT3+83PgMjVY3eM7iUFuc0PhpRsg9ITNw+icR45v7e0g/741ENg7+Ier+ z3stjyu5YJXLw/1RaYM6uYgi2jx6wdoJiPlEhTdMIqnpssnNbYcz8xrVxisELW3z 08il2uB5FaNbK1zGO/MF5uB/xl2TCHrPiiAFTW6VS9OOc3cBbKren0SGyQneNCWo qhi3I2fVS/nVgvV+lyTiwWOwVCf0PMy6CpGx0odyXBDw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; bh=58Zunp3Tg7bESTzisIrnIibycewaRC6z+Qrcg47W84Y=; b=mXzuMBqh zPa8p4ujUQoJffcQrvUFZJuRt6fUAi0+aYRxUIuT8zN+5tcZ7akhc0GPWISPP3mX LC0NYDoolpBZO9gqTrad4QURw1XfEwGGGywlfH8nA+z5cL00C3usFbpK+erdL4S8 hhr88scvr3P1BN4s4yb2c4rrP7SUr7n86EICSfsCGAyyYBcOKL6TS2VPK/CYVYYC 6xhltFSM9V5PaZ/UE/bN+ztS+vwyWr9OLQJz6WxFXhaPD+UTxXK8rvbfJGqaRdsZ mKLI5Vfxu2skH76MucqEK6vP0NpaHF8JWgzlT95ZAzCeqgPUU5rnwO4CgHbbL5y1 DgwKRRXcCAhuSQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduledrgeehgddtkecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefhvffufffkofgjfhgggfestdekredtredttdenucfhrhhomhepuehorhhishcu uehurhhkohhvuceosghorhhishessghurhdrihhoqeenucggtffrrghtthgvrhhnpeeiue ffuedvieeujefhheeigfekvedujeejjeffvedvhedtudefiefhkeegueehleenucfkphep udeifedruddugedrudefvddrfeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmh epmhgrihhlfhhrohhmpegsohhrihhssegsuhhrrdhioh X-ME-Proxy: Received: from localhost (unknown [163.114.132.3]) by mail.messagingengine.com (Postfix) with ESMTPA id 7273024005B; Thu, 4 Feb 2021 18:21:47 -0500 (EST) From: Boris Burkov To: linux-btrfs@vger.kernel.org, kernel-team@fb.com, Eric Biggers Subject: [PATCH 1/5] btrfs: add compat_flags to btrfs_inode_item Date: Thu, 4 Feb 2021 15:21:37 -0800 Message-Id: <568a9a7bd49ce818fe1847640acf7fbf2d1861af.1612475783.git.boris@bur.io> X-Mailer: git-send-email 2.24.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org The tree checker currently rejects unrecognized flags when it reads btrfs_inode_item. Practically, this means that adding a new flag makes the change backwards incompatible if the flag is ever set on a file. Take up one of the 4 reserved u64 fields in the btrfs_inode_item as a new "compat_flags". These flags are zero on inode creation in btrfs and mkfs and are ignored by an older kernel, so it should be safe to use them in this way. Signed-off-by: Boris Burkov --- fs/btrfs/btrfs_inode.h | 1 + fs/btrfs/ctree.h | 2 ++ fs/btrfs/delayed-inode.c | 2 ++ fs/btrfs/inode.c | 3 +++ fs/btrfs/ioctl.c | 7 ++++--- fs/btrfs/tree-log.c | 1 + include/uapi/linux/btrfs_tree.h | 7 ++++++- 7 files changed, 19 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 28e202e89660..8b95932f25a8 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -191,6 +191,7 @@ struct btrfs_inode { /* flags field from the on disk inode */ u32 flags; + u64 compat_flags; /* * Counters to keep track of the number of extent item's we may use due diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index a9b0521d9e89..2f233591c3e3 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1752,6 +1752,7 @@ BTRFS_SETGET_FUNCS(inode_gid, struct btrfs_inode_item, gid, 32); BTRFS_SETGET_FUNCS(inode_mode, struct btrfs_inode_item, mode, 32); BTRFS_SETGET_FUNCS(inode_rdev, struct btrfs_inode_item, rdev, 64); BTRFS_SETGET_FUNCS(inode_flags, struct btrfs_inode_item, flags, 64); +BTRFS_SETGET_FUNCS(inode_compat_flags, struct btrfs_inode_item, compat_flags, 64); BTRFS_SETGET_STACK_FUNCS(stack_inode_generation, struct btrfs_inode_item, generation, 64); BTRFS_SETGET_STACK_FUNCS(stack_inode_sequence, struct btrfs_inode_item, @@ -1769,6 +1770,7 @@ BTRFS_SETGET_STACK_FUNCS(stack_inode_gid, struct btrfs_inode_item, gid, 32); BTRFS_SETGET_STACK_FUNCS(stack_inode_mode, struct btrfs_inode_item, mode, 32); BTRFS_SETGET_STACK_FUNCS(stack_inode_rdev, struct btrfs_inode_item, rdev, 64); BTRFS_SETGET_STACK_FUNCS(stack_inode_flags, struct btrfs_inode_item, flags, 64); +BTRFS_SETGET_STACK_FUNCS(stack_inode_compat_flags, struct btrfs_inode_item, compat_flags, 64); BTRFS_SETGET_FUNCS(timespec_sec, struct btrfs_timespec, sec, 64); BTRFS_SETGET_FUNCS(timespec_nsec, struct btrfs_timespec, nsec, 32); BTRFS_SETGET_STACK_FUNCS(stack_timespec_sec, struct btrfs_timespec, sec, 64); diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index ec0b50b8c5d6..6ea9d27b9c9d 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1733,6 +1733,7 @@ static void fill_stack_inode_item(struct btrfs_trans_handle *trans, btrfs_set_stack_inode_transid(inode_item, trans->transid); btrfs_set_stack_inode_rdev(inode_item, inode->i_rdev); btrfs_set_stack_inode_flags(inode_item, BTRFS_I(inode)->flags); + btrfs_set_stack_inode_compat_flags(inode_item, BTRFS_I(inode)->compat_flags); btrfs_set_stack_inode_block_group(inode_item, 0); btrfs_set_stack_timespec_sec(&inode_item->atime, @@ -1791,6 +1792,7 @@ int btrfs_fill_inode(struct inode *inode, u32 *rdev) inode->i_rdev = 0; *rdev = btrfs_stack_inode_rdev(inode_item); BTRFS_I(inode)->flags = btrfs_stack_inode_flags(inode_item); + BTRFS_I(inode)->compat_flags = btrfs_stack_inode_compat_flags(inode_item); inode->i_atime.tv_sec = btrfs_stack_timespec_sec(&inode_item->atime); inode->i_atime.tv_nsec = btrfs_stack_timespec_nsec(&inode_item->atime); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 1a160db01878..f97d4d5c03d8 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3481,6 +3481,7 @@ static int btrfs_read_locked_inode(struct inode *inode, BTRFS_I(inode)->index_cnt = (u64)-1; BTRFS_I(inode)->flags = btrfs_inode_flags(leaf, inode_item); + BTRFS_I(inode)->compat_flags = btrfs_inode_compat_flags(leaf, inode_item); cache_index: /* @@ -3647,6 +3648,7 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, btrfs_set_token_inode_transid(&token, item, trans->transid); btrfs_set_token_inode_rdev(&token, item, inode->i_rdev); btrfs_set_token_inode_flags(&token, item, BTRFS_I(inode)->flags); + btrfs_set_token_inode_compat_flags(&token, item, BTRFS_I(inode)->compat_flags); btrfs_set_token_inode_block_group(&token, item, 0); } @@ -8663,6 +8665,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) ei->defrag_bytes = 0; ei->disk_i_size = 0; ei->flags = 0; + ei->compat_flags = 0; ei->csum_bytes = 0; ei->index_cnt = (u64)-1; ei->dir_index = 0; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 7f2935ea8d3a..c5e21e564921 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -102,8 +102,9 @@ static unsigned int btrfs_mask_fsflags_for_type(struct inode *inode, * Export internal inode flags to the format expected by the FS_IOC_GETFLAGS * ioctl. */ -static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags) +static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode) { + unsigned int flags = binode->flags; unsigned int iflags = 0; if (flags & BTRFS_INODE_SYNC) @@ -156,7 +157,7 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode) static int btrfs_ioctl_getflags(struct file *file, void __user *arg) { struct btrfs_inode *binode = BTRFS_I(file_inode(file)); - unsigned int flags = btrfs_inode_flags_to_fsflags(binode->flags); + unsigned int flags = btrfs_inode_flags_to_fsflags(binode); if (copy_to_user(arg, &flags, sizeof(flags))) return -EFAULT; @@ -228,7 +229,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) inode_lock(inode); fsflags = btrfs_mask_fsflags_for_type(inode, fsflags); - old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags); + old_fsflags = btrfs_inode_flags_to_fsflags(binode); ret = vfs_ioc_setflags_prepare(inode, old_fsflags, fsflags); if (ret) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 8ee0700a980f..c3169571ee1e 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3895,6 +3895,7 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, btrfs_set_token_inode_transid(&token, item, trans->transid); btrfs_set_token_inode_rdev(&token, item, inode->i_rdev); btrfs_set_token_inode_flags(&token, item, BTRFS_I(inode)->flags); + btrfs_set_token_inode_compat_flags(&token, item, BTRFS_I(inode)->compat_flags); btrfs_set_token_inode_block_group(&token, item, 0); } diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index 58d7cff9afb1..ae25280316bd 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -574,11 +574,16 @@ struct btrfs_inode_item { /* modification sequence number for NFS */ __le64 sequence; + /* + * flags which aren't checked for corruption at mount + * and can be added in a backwards compatible way + */ + __le64 compat_flags; /* * a little future expansion, for more than this we can * just grow the inode item and version it */ - __le64 reserved[4]; + __le64 reserved[3]; struct btrfs_timespec atime; struct btrfs_timespec ctime; struct btrfs_timespec mtime; From patchwork Thu Feb 4 23:21:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Burkov X-Patchwork-Id: 12068927 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AF6FFC433DB for ; Thu, 4 Feb 2021 23:23:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6E80664FB1 for ; Thu, 4 Feb 2021 23:23:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230033AbhBDXW7 (ORCPT ); Thu, 4 Feb 2021 18:22:59 -0500 Received: from out2-smtp.messagingengine.com ([66.111.4.26]:60127 "EHLO out2-smtp.messagingengine.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229977AbhBDXW6 (ORCPT ); Thu, 4 Feb 2021 18:22:58 -0500 Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id 90B0C5C00C3; Thu, 4 Feb 2021 18:21:51 -0500 (EST) Received: from mailfrontend2 ([10.202.2.163]) by compute2.internal (MEProxy); Thu, 04 Feb 2021 18:21:51 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bur.io; h=from :to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=fm3; bh=4SLAmn5htKo0Evn7FdMJHrnsjx OF5zuYJnK2ynnYI9E=; b=hn/R5poSnUpH2d5C1dLm+O54UvJnswE7GoWkEfIAxB X1s+oFMPg3bGNsCUJ4wbhOm5d5HeAB2kdQDIoczQhGXqF9RPyXHcFZzCtquEAAEL CC/MbDojmbdHWm9AlMKkYWMsW6w2KPt0isauzkEjbLZ5bSkb4Ma0kll0RZRhIyrG Rhb3nFyjeSapk3EmR3l18lugf9ukSge5UMVOARg6YswxllCTCkeWuIIZJ+tpP7t3 zAn9guz5qd86jgs3Q+QnkMWRsfCTDJ4mT5EbnmZjP1kxGiWK0FIGO4DDRXAvjmBF 98FBLI+xJAW6/01b17PljGzOduJAbTnL9csK2gPIgc9w== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; bh=4SLAmn5htKo0Evn7FdMJHrnsjxOF5zuYJnK2ynnYI9E=; b=dDfc/+8n 5knZKvgeAh5dQ9EHqwQVPoH9lrZOhF3h/KfjO+jyw13JdnXZBI2OYFwAj7xpDJHt A2JJvViiYY4+WF65UAh7G1Ilez8ouYo0LZ6lEokdITLjBN3AKMA1zZYfOSntUHft qI53o7X3f8Fb2eIqh/S/Af5xcY/dImnO/YJ6ta1FCJ3he3bShjCUQOngMbyOTILV cGapux4sYedGQgzyfdPJ5Hw+N/JU/Ed+Tf6NNQn0R3qwPx/+grTXgJJtgBdc1KRw gXDxabZUHKDc9Mi+qcC4C4o3QgMqiuDpT6pG/iee1SNkyYyM9FSC4RIkORx0x7kx XAQl/x6kDcRi8w== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduledrgeehgddtkecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefhvffufffkofgjfhgggfestdekredtredttdenucfhrhhomhepuehorhhishcu uehurhhkohhvuceosghorhhishessghurhdrihhoqeenucggtffrrghtthgvrhhnpeeiue ffuedvieeujefhheeigfekvedujeejjeffvedvhedtudefiefhkeegueehleenucfkphep udeifedruddugedrudefvddrfeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmh epmhgrihhlfhhrohhmpegsohhrihhssegsuhhrrdhioh X-ME-Proxy: Received: from localhost (unknown [163.114.132.3]) by mail.messagingengine.com (Postfix) with ESMTPA id 998C0108005B; Thu, 4 Feb 2021 18:21:50 -0500 (EST) From: Boris Burkov To: linux-btrfs@vger.kernel.org, kernel-team@fb.com, Eric Biggers Subject: [PATCH 2/5] btrfs: initial fsverity support Date: Thu, 4 Feb 2021 15:21:38 -0800 Message-Id: <88389022bd9f264f215c9d85fe48214190402fd6.1612475783.git.boris@bur.io> X-Mailer: git-send-email 2.24.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Chris Mason Add support for fsverity in btrfs. To support the generic interface in fs/verity, we add two new item types in the fs tree for inodes with verity enabled. One stores the per-file verity descriptor and the other stores the Merkle tree data itself. Verity checking is done at the end of IOs to ensure each page is checked before it is marked uptodate. Verity relies on PageChecked for the Merkle tree data itself to avoid re-walking up shared paths in the tree. For this reason, we need to cache the Merkle tree data. Since the file is immutable after verity is turned on, we can cache it at an index past EOF. Use the new inode compat_flags to store verity on the inode item, so that we can enable verity on a file, then rollback to an older kernel and still mount the file system and read the file. Signed-off-by: Chris Mason Reported-by: kernel test robot Reported-by: kernel test robot Reported-by: kernel test robot --- fs/btrfs/Makefile | 1 + fs/btrfs/btrfs_inode.h | 1 + fs/btrfs/ctree.h | 12 +- fs/btrfs/extent_io.c | 5 +- fs/btrfs/file.c | 6 + fs/btrfs/inode.c | 28 +- fs/btrfs/ioctl.c | 14 +- fs/btrfs/super.c | 1 + fs/btrfs/verity.c | 527 ++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs_tree.h | 8 + 10 files changed, 587 insertions(+), 16 deletions(-) create mode 100644 fs/btrfs/verity.c diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index e45957319424..1d77ebe1836b 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -33,6 +33,7 @@ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o btrfs-$(CONFIG_BTRFS_FS_REF_VERIFY) += ref-verify.o btrfs-$(CONFIG_BLK_DEV_ZONED) += zoned.o +btrfs-$(CONFIG_FS_VERITY) += verity.o btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \ tests/extent-buffer-tests.o tests/btrfs-tests.o \ diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 8b95932f25a8..c0b0537bd50e 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -51,6 +51,7 @@ enum { * the file range, inode's io_tree). */ BTRFS_INODE_NO_DELALLOC_FLUSH, + BTRFS_INODE_VERITY_IN_PROGRESS, }; /* in memory btrfs inode */ diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 2f233591c3e3..7c3a4c10c426 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1471,6 +1471,11 @@ do { \ BTRFS_INODE_COMPRESS | \ BTRFS_INODE_ROOT_ITEM_INIT) +/* + * Inode compat flags + */ +#define BTRFS_INODE_VERITY (1 << 0) + struct btrfs_map_token { struct extent_buffer *eb; char *kaddr; @@ -3076,8 +3081,8 @@ u64 btrfs_file_extent_end(const struct btrfs_path *path); /* inode.c */ blk_status_t btrfs_submit_data_bio(struct inode *inode, struct bio *bio, int mirror_num, unsigned long bio_flags); -int btrfs_verify_data_csum(struct btrfs_io_bio *io_bio, u32 bio_offset, - struct page *page, u64 start, u64 end, int mirror); +int btrfs_verify_data(struct btrfs_io_bio *io_bio, u32 bio_offset, + struct page *page, u64 start, u64 end, int mirror); struct extent_map *btrfs_get_extent_fiemap(struct btrfs_inode *inode, u64 start, u64 len); noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len, @@ -3724,6 +3729,9 @@ static inline int btrfs_defrag_cancelled(struct btrfs_fs_info *fs_info) #define in_range(b, first, len) ((b) >= (first) && (b) < (first) + (len)) +/* verity.c */ +extern const struct fsverity_operations btrfs_verityops; + /* Sanity test specific functions */ #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS void btrfs_test_destroy_inode(struct inode *inode); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index edcdbd739a1e..7254387200a2 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2918,9 +2918,8 @@ static void end_bio_extent_readpage(struct bio *bio) mirror = io_bio->mirror_num; if (likely(uptodate)) { if (is_data_inode(inode)) - ret = btrfs_verify_data_csum(io_bio, - bio_offset, page, start, end, - mirror); + ret = btrfs_verify_data(io_bio, bio_offset, + page, start, end, mirror); else ret = btrfs_validate_metadata_buffer(io_bio, page, start, end, mirror); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index d81ae1f518f2..6c08bca09d62 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "ctree.h" #include "disk-io.h" #include "transaction.h" @@ -3586,7 +3587,12 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence) static int btrfs_file_open(struct inode *inode, struct file *filp) { + int ret; filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC; + + ret = fsverity_file_open(inode, filp); + if (ret) + return ret; return generic_file_open(inode, filp); } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f97d4d5c03d8..a6bfe29449cc 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "misc.h" #include "ctree.h" #include "disk-io.h" @@ -2996,31 +2997,32 @@ static int check_data_csum(struct inode *inode, struct btrfs_io_bio *io_bio, } /* - * When reads are done, we need to check csums to verify the data is correct. - * if there's a match, we allow the bio to finish. If not, the code in - * extent_io.c will try to find good copies for us. + * When reads are done, we need to check csums and verity to verify the + * data is correct. If there's a match, we allow the bio to finish. + * If not, the code in extent_io.c will try to find good copies for us. * * @bio_offset: offset to the beginning of the bio (in bytes) * @start: file offset of the range start * @end: file offset of the range end (inclusive) * @mirror: mirror number */ -int btrfs_verify_data_csum(struct btrfs_io_bio *io_bio, u32 bio_offset, - struct page *page, u64 start, u64 end, int mirror) +int btrfs_verify_data(struct btrfs_io_bio *io_bio, u32 bio_offset, + struct page *page, u64 start, u64 end, int mirror) { struct inode *inode = page->mapping->host; struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; struct btrfs_root *root = BTRFS_I(inode)->root; const u32 sectorsize = root->fs_info->sectorsize; u32 pg_off; + int ret = 0; if (PageChecked(page)) { ClearPageChecked(page); - return 0; + goto check_verity; } if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM) - return 0; + goto check_verity; if (!root->fs_info->csum_root) return 0; @@ -3036,13 +3038,15 @@ int btrfs_verify_data_csum(struct btrfs_io_bio *io_bio, u32 bio_offset, for (pg_off = offset_in_page(start); pg_off < offset_in_page(end); pg_off += sectorsize, bio_offset += sectorsize) { - int ret; ret = check_data_csum(inode, io_bio, bio_offset, page, pg_off); if (ret < 0) return -EIO; } - return 0; +check_verity: + if (!ret && fsverity_active(inode) && fsverity_verify_page(page) != true) + ret = -EIO; + return ret; } /* @@ -5242,7 +5246,9 @@ void btrfs_evict_inode(struct inode *inode) trace_btrfs_inode_evict(inode); + if (!root) { + fsverity_cleanup_inode(inode); clear_inode(inode); return; } @@ -5325,6 +5331,7 @@ void btrfs_evict_inode(struct inode *inode) * to retry these periodically in the future. */ btrfs_remove_delayed_node(BTRFS_I(inode)); + fsverity_cleanup_inode(inode); clear_inode(inode); } @@ -8845,6 +8852,7 @@ static int btrfs_getattr(const struct path *path, struct kstat *stat, struct inode *inode = d_inode(path->dentry); u32 blocksize = inode->i_sb->s_blocksize; u32 bi_flags = BTRFS_I(inode)->flags; + u32 bi_compat_flags = BTRFS_I(inode)->compat_flags; stat->result_mask |= STATX_BTIME; stat->btime.tv_sec = BTRFS_I(inode)->i_otime.tv_sec; @@ -8857,6 +8865,8 @@ static int btrfs_getattr(const struct path *path, struct kstat *stat, stat->attributes |= STATX_ATTR_IMMUTABLE; if (bi_flags & BTRFS_INODE_NODUMP) stat->attributes |= STATX_ATTR_NODUMP; + if (bi_compat_flags & BTRFS_INODE_VERITY) + stat->attributes |= STATX_ATTR_VERITY; stat->attributes_mask |= (STATX_ATTR_APPEND | STATX_ATTR_COMPRESSED | diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index c5e21e564921..80541951aa3e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "ctree.h" #include "disk-io.h" #include "export.h" @@ -105,6 +106,7 @@ static unsigned int btrfs_mask_fsflags_for_type(struct inode *inode, static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode) { unsigned int flags = binode->flags; + unsigned int compat_flags = binode->flags; unsigned int iflags = 0; if (flags & BTRFS_INODE_SYNC) @@ -121,6 +123,8 @@ static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode) iflags |= FS_DIRSYNC_FL; if (flags & BTRFS_INODE_NODATACOW) iflags |= FS_NOCOW_FL; + if (compat_flags & BTRFS_INODE_VERITY) + iflags |= FS_VERITY_FL; if (flags & BTRFS_INODE_NOCOMPRESS) iflags |= FS_NOCOMP_FL; @@ -148,10 +152,12 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode) new_fl |= S_NOATIME; if (binode->flags & BTRFS_INODE_DIRSYNC) new_fl |= S_DIRSYNC; + if (binode->compat_flags & BTRFS_INODE_VERITY) + new_fl |= S_VERITY; set_mask_bits(&inode->i_flags, - S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC, - new_fl); + S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC | + S_VERITY, new_fl); } static int btrfs_ioctl_getflags(struct file *file, void __user *arg) @@ -5021,6 +5027,10 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_get_subvol_rootref(file, argp); case BTRFS_IOC_INO_LOOKUP_USER: return btrfs_ioctl_ino_lookup_user(file, argp); + case FS_IOC_ENABLE_VERITY: + return fsverity_ioctl_enable(file, (const void __user *)argp); + case FS_IOC_MEASURE_VERITY: + return fsverity_ioctl_measure(file, argp); } return -ENOTTY; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 12d7d3be7cd4..77fefff5eff1 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1340,6 +1340,7 @@ static int btrfs_fill_super(struct super_block *sb, sb->s_op = &btrfs_super_ops; sb->s_d_op = &btrfs_dentry_operations; sb->s_export_op = &btrfs_export_ops; + sb->s_vop = &btrfs_verityops; sb->s_xattr = btrfs_xattr_handlers; sb->s_time_gran = 1; #ifdef CONFIG_BTRFS_FS_POSIX_ACL diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c new file mode 100644 index 000000000000..6f3dbaee81b7 --- /dev/null +++ b/fs/btrfs/verity.c @@ -0,0 +1,527 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Facebook. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ctree.h" +#include "btrfs_inode.h" +#include "transaction.h" +#include "disk-io.h" +#include "locking.h" + +/* + * Just like ext4, we cache the merkle tree in pages after EOF in the page + * cache. Unlike ext4, we're storing these in dedicated btree items and + * not just shoving them after EOF in the file. This means we'll need to + * do extra work to encrypt them once encryption is supported in btrfs, + * but btrfs has a lot of careful code around i_size and it seems better + * to make a new key type than try and adjust all of our expectations + * for i_size. + * + * fs verity items are stored under two different key types on disk. + * + * The descriptor items: + * [ inode objectid, BTRFS_VERITY_DESC_ITEM_KEY, offset ] + * + * These start at offset 0 and hold the fs verity descriptor. They are opaque + * to btrfs, we just read and write them as a blob for the higher level + * verity code. The most common size for this is 256 bytes. + * + * The merkle tree items: + * [ inode objectid, BTRFS_VERITY_MERKLE_ITEM_KEY, offset ] + * + * These also start at offset 0, and correspond to the merkle tree bytes. + * So when fsverity asks for page 0 of the merkle tree, we pull up one page + * starting at offset 0 for this key type. These are also opaque to btrfs, + * we're blindly storing whatever fsverity sends down. + * + * This file is just reading and writing the various items whenever + * fsverity needs us to. + */ + +/* + * drop all the items for this inode with this key_type. Before + * doing a verity enable we cleanup any existing verity items. + * + * This is also used to clean up if a verity enable failed half way + * through + */ +static int drop_verity_items(struct btrfs_inode *inode, u8 key_type) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *root = inode->root; + struct btrfs_path *path; + struct btrfs_key key; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + while (1) { + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } + + /* + * walk backwards through all the items until we find one + * that isn't from our key type or objectid + */ + key.objectid = btrfs_ino(inode); + key.offset = (u64)-1; + key.type = key_type; + + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret > 0) { + ret = 0; + /* no more keys of this type, we're done */ + if (path->slots[0] == 0) + break; + path->slots[0]--; + } else if (ret < 0) { + break; + } + + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + + /* no more keys of this type, we're done */ + if (key.objectid != btrfs_ino(inode) || key.type != key_type) + break; + + /* + * this shouldn't be a performance sensitive function because + * it's not used as part of truncate. If it ever becomes + * perf sensitive, change this to walk forward and bulk delete + * items + */ + ret = btrfs_del_items(trans, root, path, + path->slots[0], 1); + btrfs_release_path(path); + btrfs_end_transaction(trans); + + if (ret) + goto out; + } + + btrfs_end_transaction(trans); +out: + btrfs_free_path(path); + return ret; + +} + +/* + * helper function to insert a single item. Returns zero if all went + * well + */ +static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, + const char *src, u64 len) +{ + struct btrfs_trans_handle *trans; + struct btrfs_path *path; + struct btrfs_root *root = inode->root; + struct extent_buffer *leaf; + struct btrfs_key key; + u64 orig_len = len; + u64 copied = 0; + unsigned long copy_bytes; + unsigned long src_offset = 0; + void *data; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + while (len > 0) { + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + break; + } + + key.objectid = btrfs_ino(inode); + key.offset = offset; + key.type = key_type; + + /* + * insert 1K at a time mostly to be friendly for smaller + * leaf size filesystems + */ + copy_bytes = min_t(u64, len, 1024); + + ret = btrfs_insert_empty_item(trans, root, path, &key, copy_bytes); + if (ret) { + btrfs_end_transaction(trans); + break; + } + + leaf = path->nodes[0]; + + data = btrfs_item_ptr(leaf, path->slots[0], void); + write_extent_buffer(leaf, src + src_offset, + (unsigned long)data, copy_bytes); + offset += copy_bytes; + src_offset += copy_bytes; + len -= copy_bytes; + copied += copy_bytes; + + btrfs_release_path(path); + btrfs_end_transaction(trans); + } + + btrfs_free_path(path); + + if (!ret && copied != orig_len) + ret = -EIO; + return ret; +} + +/* + * helper function to read items from the btree. This returns the number + * of bytes read or < 0 for errors. We can return short reads if the + * items don't exist on disk or aren't big enough to fill the desired length + * + * Since we're potentially copying into page cache, passing dest_page + * will make us kmap_atomic that page and then use the kmap address instead + * of dest. + * + * pass dest == NULL to find out the size of all the items up to len bytes + * we'll just do the tree walk without copying anything + */ +static ssize_t read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, + char *dest, u64 len, struct page *dest_page) +{ + struct btrfs_path *path; + struct btrfs_root *root = inode->root; + struct extent_buffer *leaf; + struct btrfs_key key; + u64 item_end; + u64 copy_end; + u64 copied = 0; + u32 copy_offset; + unsigned long copy_bytes; + unsigned long dest_offset = 0; + void *data; + char *kaddr = dest; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + if (dest_page) + path->reada = READA_FORWARD; + + key.objectid = btrfs_ino(inode); + key.offset = offset; + key.type = key_type; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) { + goto out; + } else if (ret > 0) { + ret = 0; + if (path->slots[0] == 0) + goto out; + path->slots[0]--; + } + + while (len > 0) { + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + + if (key.objectid != btrfs_ino(inode) || + key.type != key_type) + break; + + item_end = btrfs_item_size_nr(leaf, path->slots[0]) + key.offset; + + if (copied > 0) { + /* + * once we've copied something, we want all of the items + * to be sequential + */ + if (key.offset != offset) + break; + } else { + /* + * our initial offset might be in the middle of an + * item. Make sure it all makes sense + */ + if (key.offset > offset) + break; + if (item_end <= offset) + break; + } + + /* desc = NULL to just sum all the item lengths */ + if (!dest) + copy_end = item_end; + else + copy_end = min(offset + len, item_end); + + /* number of bytes in this item we want to copy */ + copy_bytes = copy_end - offset; + + /* offset from the start of item for copying */ + copy_offset = offset - key.offset; + + if (dest) { + if (dest_page) + kaddr = kmap_atomic(dest_page); + + data = btrfs_item_ptr(leaf, path->slots[0], void); + read_extent_buffer(leaf, kaddr + dest_offset, + (unsigned long)data + copy_offset, + copy_bytes); + + if (dest_page) + kunmap_atomic(kaddr); + } + + offset += copy_bytes; + dest_offset += copy_bytes; + len -= copy_bytes; + copied += copy_bytes; + + path->slots[0]++; + if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { + /* + * we've reached the last slot in this leaf and we need + * to go to the next leaf. + */ + ret = btrfs_next_leaf(root, path); + if (ret < 0) { + break; + } else if (ret > 0) { + ret = 0; + break; + } + } + } +out: + btrfs_free_path(path); + if (!ret) + ret = copied; + return ret; +} + +/* + * fsverity calls this to ask us to setup the inode for enabling. We + * drop any existing verity items and set the in progress bit + */ +static int btrfs_begin_enable_verity(struct file *filp) +{ + struct inode *inode = file_inode(filp); + int ret; + + if (test_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &BTRFS_I(inode)->runtime_flags)) + return -EBUSY; + + /* + * ext4 adds the inode to the orphan list here, presumably because the + * truncate done at orphan processing time will delete partial + * measurements. TODO: setup orphans + */ + set_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &BTRFS_I(inode)->runtime_flags); + ret = drop_verity_items(BTRFS_I(inode), BTRFS_VERITY_DESC_ITEM_KEY); + if (ret) + goto err; + + ret = drop_verity_items(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY); + if (ret) + goto err; + + return 0; + +err: + clear_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &BTRFS_I(inode)->runtime_flags); + return ret; + +} + +/* + * fsverity calls this when it's done with all of the pages in the file + * and all of the merkle items have been inserted. We write the + * descriptor and update the inode in the btree to reflect it's new life + * as a verity file + */ +static int btrfs_end_enable_verity(struct file *filp, const void *desc, + size_t desc_size, u64 merkle_tree_size) +{ + struct btrfs_trans_handle *trans; + struct inode *inode = file_inode(filp); + struct btrfs_root *root = BTRFS_I(inode)->root; + int ret; + + if (desc != NULL) { + /* write out the descriptor */ + ret = write_key_bytes(BTRFS_I(inode), + BTRFS_VERITY_DESC_ITEM_KEY, 0, + desc, desc_size); + if (ret) + goto out; + + /* update our inode flags to include fs verity */ + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } + BTRFS_I(inode)->compat_flags |= BTRFS_INODE_VERITY; + btrfs_sync_inode_flags_to_i_flags(inode); + ret = btrfs_update_inode(trans, root, BTRFS_I(inode)); + btrfs_end_transaction(trans); + } + +out: + if (desc == NULL || ret) { + /* If we failed, drop all the verity items */ + drop_verity_items(BTRFS_I(inode), BTRFS_VERITY_DESC_ITEM_KEY); + drop_verity_items(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY); + } + clear_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &BTRFS_I(inode)->runtime_flags); + return ret; +} + +/* + * fsverity does a two pass setup for reading the descriptor, in the first pass + * it calls with buf_size = 0 to query the size of the descriptor, + * and then in the second pass it actually reads the descriptor off + * disk. + */ +static int btrfs_get_verity_descriptor(struct inode *inode, void *buf, + size_t buf_size) +{ + ssize_t ret = 0; + + if (!buf_size) { + return read_key_bytes(BTRFS_I(inode), + BTRFS_VERITY_DESC_ITEM_KEY, + 0, NULL, (u64)-1, NULL); + } + + ret = read_key_bytes(BTRFS_I(inode), + BTRFS_VERITY_DESC_ITEM_KEY, 0, + buf, buf_size, NULL); + if (ret < 0) + return ret; + + if (ret != buf_size) + return -EIO; + + return buf_size; +} + +/* + * reads and caches a merkle tree page. These are stored in the btree, + * but we cache them in the inode's address space after EOF. + */ +static struct page *btrfs_read_merkle_tree_page(struct inode *inode, + pgoff_t index, + unsigned long num_ra_pages) +{ + struct page *p; + u64 start = index << PAGE_SHIFT; + unsigned long mapping_index; + ssize_t ret; + int err; + + /* + * the file is readonly, so i_size can't change here. We jump + * some pages past the last page to cache our merkles. The goal + * is just to jump past any hugepages that might be mapped in. + */ + mapping_index = (i_size_read(inode) >> PAGE_SHIFT) + 2048 + index; +again: + p = find_get_page_flags(inode->i_mapping, mapping_index, FGP_ACCESSED); + if (p) { + if (PageUptodate(p)) + return p; + + lock_page(p); + /* + * we only insert uptodate pages, so !Uptodate has to be + * an error + */ + if (!PageUptodate(p)) { + unlock_page(p); + put_page(p); + return ERR_PTR(-EIO); + } + unlock_page(p); + return p; + } + + p = page_cache_alloc(inode->i_mapping); + if (!p) + return ERR_PTR(-ENOMEM); + + /* + * merkle item keys are indexed from byte 0 in the merkle tree. + * they have the form: + * + * [ inode objectid, BTRFS_MERKLE_ITEM_KEY, offset in bytes ] + */ + ret = read_key_bytes(BTRFS_I(inode), + BTRFS_VERITY_MERKLE_ITEM_KEY, start, + page_address(p), PAGE_SIZE, p); + if (ret < 0) { + put_page(p); + return ERR_PTR(ret); + } + + /* zero fill any bytes we didn't write into the page */ + if (ret < PAGE_SIZE) { + char *kaddr = kmap_atomic(p); + + memset(kaddr + ret, 0, PAGE_SIZE - ret); + kunmap_atomic(kaddr); + } + SetPageUptodate(p); + err = add_to_page_cache_lru(p, inode->i_mapping, mapping_index, + mapping_gfp_mask(inode->i_mapping)); + + if (!err) { + /* inserted and ready for fsverity */ + unlock_page(p); + } else { + put_page(p); + /* did someone race us into inserting this page? */ + if (err == -EEXIST) + goto again; + p = ERR_PTR(err); + } + return p; +} + +static int btrfs_write_merkle_tree_block(struct inode *inode, const void *buf, + u64 index, int log_blocksize) +{ + u64 start = index << log_blocksize; + u64 len = 1 << log_blocksize; + + return write_key_bytes(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY, + start, buf, len); +} + +const struct fsverity_operations btrfs_verityops = { + .begin_enable_verity = btrfs_begin_enable_verity, + .end_enable_verity = btrfs_end_enable_verity, + .get_verity_descriptor = btrfs_get_verity_descriptor, + .read_merkle_tree_page = btrfs_read_merkle_tree_page, + .write_merkle_tree_block = btrfs_write_merkle_tree_block, +}; diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index ae25280316bd..e0071327a7d0 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -118,6 +118,14 @@ #define BTRFS_INODE_REF_KEY 12 #define BTRFS_INODE_EXTREF_KEY 13 #define BTRFS_XATTR_ITEM_KEY 24 + +/* + * fsverity has a descriptor per file, and then + * a number of sha or csum items indexed by offset in to the file. + */ +#define BTRFS_VERITY_DESC_ITEM_KEY 36 +#define BTRFS_VERITY_MERKLE_ITEM_KEY 37 + #define BTRFS_ORPHAN_ITEM_KEY 48 /* reserve 2-15 close to the inode for later flexibility */ From patchwork Thu Feb 4 23:21:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Burkov X-Patchwork-Id: 12068925 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 85EFEC433E9 for ; Thu, 4 Feb 2021 23:22:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4155864FAD for ; Thu, 4 Feb 2021 23:22:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230013AbhBDXWl (ORCPT ); Thu, 4 Feb 2021 18:22:41 -0500 Received: from out2-smtp.messagingengine.com ([66.111.4.26]:45139 "EHLO out2-smtp.messagingengine.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229977AbhBDXWk (ORCPT ); Thu, 4 Feb 2021 18:22:40 -0500 Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id 3A6505C00BE; Thu, 4 Feb 2021 18:21:54 -0500 (EST) Received: from mailfrontend2 ([10.202.2.163]) by compute2.internal (MEProxy); Thu, 04 Feb 2021 18:21:54 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bur.io; h=from :to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=fm3; bh=hj9m/BHKdmCwInKVRTCFfYK+j5 NCJUzSvxYY7oF58tM=; b=qBexaR8fDKKjAis3jus31PjFhP4ADR1ygNnSN6wKUe D5YnhgSF4Rlrt9cDbNCWmTt5Tm5yAOiJjB9q2I6aYoUgYEv1p7f5Uyeslfa5d+Xn VcmkhpTB/hPnD2raCB/PANeMINfjocS+2ZkMcvqdQzvlYv0o12VNFtmNJszWpKjF Pvq8CXqDLx3I+UnyuIWCnS6PxL3N//U9tz3SSQZHtX+BFksCaIU+wVcn5Cvf7CxT YXj8jAKwlXdMW0n5+/ExX5vSuobQhX60XiQ5ao2TcRxMKrL/FENr8dIXIpxQ4qBm +MF/kj0ApD1rh6sGfWfO8NojxgQ8OXSECV8cng7CiB6Q== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; bh=hj9m/BHKdmCwInKVRTCFfYK+j5NCJUzSvxYY7oF58tM=; b=ldvKdf2l GG7c3MSYDq5WqFDw3ArI3kdBlNbUYQFBgwiFO5cU8BekRSBFuHpl566jqalSFIUS CSXyfcaoAn2WIeoR6a5N2sZfsHpa867eHi19OzTSgIkKlmWt0YG4AMqpS9TwOCW0 ZDpQ4z6MauJ5e8LQS2ND8BbtBRJ5gV2ABnCaUnVOkxOod2EE/O7nhAGBpaWH6Rbp PIDUKPOGme+9VBwLiVkrWyEent5uZOo8rKVrl1KpU1tmjen9RIuerQkZwKFh9PUK i86onSyyBT3E0ApEbspEE6x3zB+WvMFmlxh4giQHGlrlYIliSqJcrR6KlgODfZk2 Nb5g1rvWttdGZQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduledrgeehgddtkecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefhvffufffkofgjfhgggfestdekredtredttdenucfhrhhomhepuehorhhishcu uehurhhkohhvuceosghorhhishessghurhdrihhoqeenucggtffrrghtthgvrhhnpeeiue ffuedvieeujefhheeigfekvedujeejjeffvedvhedtudefiefhkeegueehleenucfkphep udeifedruddugedrudefvddrfeenucevlhhushhtvghrufhiiigvpedvnecurfgrrhgrmh epmhgrihhlfhhrohhmpegsohhrihhssegsuhhrrdhioh X-ME-Proxy: Received: from localhost (unknown [163.114.132.3]) by mail.messagingengine.com (Postfix) with ESMTPA id BB1A0108005B; Thu, 4 Feb 2021 18:21:53 -0500 (EST) From: Boris Burkov To: linux-btrfs@vger.kernel.org, kernel-team@fb.com, Eric Biggers Subject: [PATCH 3/5] btrfs: check verity for reads of inline extents and holes Date: Thu, 4 Feb 2021 15:21:39 -0800 Message-Id: <489ca41d09a928df5f9b54c3340cf3866daacdfd.1612475783.git.boris@bur.io> X-Mailer: git-send-email 2.24.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org The majority of reads receive a verity check after the bio is complete as the page is marked uptodate. However, there is a class of reads which are handled with btrfs logic in readpage, rather than by submitting a bio. Specifically, these are inline extents, preallocated extents, and holes. Tweak readpage so that if it is going to mark such a page uptodate, it first checks verity on it. Now if a veritied file has corruption to this class of EXTENT_DATA items, it will be detected at read time. There is one annoying edge case that requires checking for start < last_byte: if userspace reads to the end of a file with page aligned size and then tries to keep reading (as cat does), the buffered read code will try to read the page past the end of the file, and expects it to be filled with 0s and marked uptodate. That bogus page is not part of the data hashed by verity, so we have to ignore it. Signed-off-by: Boris Burkov --- fs/btrfs/extent_io.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 7254387200a2..16e3f4304d2e 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "extent_io.h" #include "extent-io-tree.h" #include "extent_map.h" @@ -2197,18 +2198,6 @@ int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, return bitset; } -/* - * helper function to set a given page up to date if all the - * extents in the tree for that page are up to date - */ -static void check_page_uptodate(struct extent_io_tree *tree, struct page *page) -{ - u64 start = page_offset(page); - u64 end = start + PAGE_SIZE - 1; - if (test_range_bit(tree, start, end, EXTENT_UPTODATE, 1, NULL)) - SetPageUptodate(page); -} - int free_io_failure(struct extent_io_tree *failure_tree, struct extent_io_tree *io_tree, struct io_failure_record *rec) @@ -3344,6 +3333,7 @@ int btrfs_do_readpage(struct page *page, struct extent_map **em_cached, set_extent_uptodate(tree, cur, cur + iosize - 1, &cached, GFP_NOFS); + unlock_extent_cached(tree, cur, cur + iosize - 1, &cached); cur = cur + iosize; @@ -3353,7 +3343,6 @@ int btrfs_do_readpage(struct page *page, struct extent_map **em_cached, /* the get_extent function already copied into the page */ if (test_range_bit(tree, cur, cur_end, EXTENT_UPTODATE, 1, NULL)) { - check_page_uptodate(tree, page); unlock_extent(tree, cur, cur + iosize - 1); cur = cur + iosize; pg_offset += iosize; @@ -3390,8 +3379,13 @@ int btrfs_do_readpage(struct page *page, struct extent_map **em_cached, } out: if (!nr) { - if (!PageError(page)) - SetPageUptodate(page); + if (!PageError(page) && !PageUptodate(page)) { + if (start < last_byte && fsverity_active(inode) && + fsverity_verify_page(page) != true) + ret = -EIO; + else + SetPageUptodate(page); + } unlock_page(page); } return ret; From patchwork Thu Feb 4 23:21:40 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Burkov X-Patchwork-Id: 12068929 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 624B0C433E0 for ; Thu, 4 Feb 2021 23:23:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0A23B64FB1 for ; Thu, 4 Feb 2021 23:23:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230058AbhBDXXE (ORCPT ); Thu, 4 Feb 2021 18:23:04 -0500 Received: from out2-smtp.messagingengine.com ([66.111.4.26]:56841 "EHLO out2-smtp.messagingengine.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229977AbhBDXXC (ORCPT ); Thu, 4 Feb 2021 18:23:02 -0500 Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id 0051A5C00D2; Thu, 4 Feb 2021 18:21:57 -0500 (EST) Received: from mailfrontend2 ([10.202.2.163]) by compute2.internal (MEProxy); Thu, 04 Feb 2021 18:21:56 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bur.io; h=from :to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=fm3; bh=SdbaCXnJC+aWW0sM5n35VGQ32G labIuSas0db7LSwc4=; b=qQGf2euu3ouRATkorGNKvSRpEU9QgzkT42fycAZNKF h4q0z5yeLBMwwgV3E08dOtsnUAVRbxkL8YAcEyURmFQEaPEiigN/sUN4grJYo+ck ezM3FrDud1tu4sOCQFLzYl3EEAKYKANx1PtSBplKUfpm0TBnmKCwZ90SC3o9IPst IjOxZLLh33MTnvc7ug8M1fscxnMhb2aDEhGRF27UheR8k+1HndCB6pP4rCHA8a8/ hlQBJuxb/Y+e8LKMRnyBEhQLaPaqEpm4Yc5BDz11Wma9EQyh3AErKSMKL2yeVT5X vk23O9DWLPxWhDntr67ruNskoTpssw1WUMTsp4d0Ce/A== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; bh=SdbaCXnJC+aWW0sM5n35VGQ32GlabIuSas0db7LSwc4=; b=FW+SpB7N aC+TFSExn4jCa7/O41A7t13VtjS3M/fh3k7PB/bJb5i/e0phfPbvmUxznVsKZkLS uxlKGVIe5alrCqiOG4aqTX/lB0o9bcWInJCUfcdoi88GoD9scvL5cs2+59iVGINy 3T3ZbkfodnKLc4UNoT76FELNaAtagNOHI4ijF198itnaO+tXmDZUg3g9OizDOjUc qS4nHgm6blfBnvm5ZjiWL/ZYhMmpoXXL1esAk2x/d9J7jCOO1W+9Eux865h58xmM nVK3M/aVHq2MynXwf9jdhf8yk8ok5mheimuEXZR9OD8qZFxyJlSkF46a7DJGDjf4 XGnjtxCLDJwYWA== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduledrgeehgddtkecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefhvffufffkofgjfhgggfestdekredtredttdenucfhrhhomhepuehorhhishcu uehurhhkohhvuceosghorhhishessghurhdrihhoqeenucggtffrrghtthgvrhhnpeeiue ffuedvieeujefhheeigfekvedujeejjeffvedvhedtudefiefhkeegueehleenucfkphep udeifedruddugedrudefvddrfeenucevlhhushhtvghrufhiiigvpedvnecurfgrrhgrmh epmhgrihhlfhhrohhmpegsohhrihhssegsuhhrrdhioh X-ME-Proxy: Received: from localhost (unknown [163.114.132.3]) by mail.messagingengine.com (Postfix) with ESMTPA id B5A6D108005B; Thu, 4 Feb 2021 18:21:56 -0500 (EST) From: Boris Burkov To: linux-btrfs@vger.kernel.org, kernel-team@fb.com, Eric Biggers Subject: [PATCH 4/5] btrfs: fallback to buffered io for verity files Date: Thu, 4 Feb 2021 15:21:40 -0800 Message-Id: X-Mailer: git-send-email 2.24.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Reading the contents with direct IO would circumvent verity checks, so fallback to buffered reads. For what it's worth, this is how ext4 handles it as well. Signed-off-by: Boris Burkov --- fs/btrfs/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 6c08bca09d62..a4a2c9c9fcf0 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -3621,6 +3621,9 @@ static ssize_t btrfs_direct_read(struct kiocb *iocb, struct iov_iter *to) struct inode *inode = file_inode(iocb->ki_filp); ssize_t ret; + if (fsverity_active(inode)) + return 0; + if (check_direct_read(btrfs_sb(inode->i_sb), to, iocb->ki_pos)) return 0; From patchwork Thu Feb 4 23:21:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Burkov X-Patchwork-Id: 12068931 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 175C4C433E0 for ; Thu, 4 Feb 2021 23:23:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D703964FB2 for ; Thu, 4 Feb 2021 23:23:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229963AbhBDXXk (ORCPT ); Thu, 4 Feb 2021 18:23:40 -0500 Received: from out2-smtp.messagingengine.com ([66.111.4.26]:54631 "EHLO out2-smtp.messagingengine.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229977AbhBDXXk (ORCPT ); Thu, 4 Feb 2021 18:23:40 -0500 Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id A56B95C00D7; Thu, 4 Feb 2021 18:22:00 -0500 (EST) Received: from mailfrontend2 ([10.202.2.163]) by compute2.internal (MEProxy); Thu, 04 Feb 2021 18:22:00 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bur.io; h=from :to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=fm3; bh=J3dDpVNU4Gbdy9Rp7q5jjblp6N IL0xqOCbkQDvXjZIs=; b=oBVowBtAU7Ols+CEB6yA/HG6fipCxfMDi8F3f0vC0F mfPHfeUMJcqq9A0JDj+HwhjCB8IoixbhbaVZt2XftSRbZqTpKwO+TCnfX+wExiDi QBKTrvORc4m6YegYAbRITJrKwOruvHZ5KBdjkow+Yj0JBE58in6ByRMWTvQpclU+ bIWvLvzDK8ueFUVSZRa/6TaYdxsSp7nz2byghibBDBLSEDLMY2GI27lGCgYsfjfu nvArUkrfcBk8dEdNFV4f8ZYcVm+fxE2voaiLJECKL1DGThIJkF99wTvuDSwsCgPP l1vfD5ciWnwc8zxTxxMFXCWQzggkupRt9Tv3tyCxJbkA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; bh=J3dDpVNU4Gbdy9Rp7q5jjblp6NIL0xqOCbkQDvXjZIs=; b=Yd1vlMtr FDXjXI15RdFzRYyO3+ebJ/OJHC01kWX4Qx0ShkpHqYfr9Vd7dTVeLDzQo/2iOq6I Kl2/Jx8xqEDgw7astJDMbjAUvu0atGXIYzp1uhrGMJeviYS27ZGSWE94UQ3GCncg WCLrgSOW1Hy+mY1oO52oum06V4iVqT9ADUh2MDuIOIiSDPP9nyrUuOVOtgMBBoFg 7sW97EiaYnptu6mYcm5JCwT8FPnRbTEINjoTzrWSz0Ny01Rks9eTP1GGr5nMqWmZ Z+o5M2wzUiFdOiaNyLp4R0kAUt38jZ74SQN2LFwbBAHB7NkfDqedZk9USd+LxniP +ed4hQq2KmYhGA== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduledrgeehgddtkecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefhvffufffkofgjfhgggfestdekredtredttdenucfhrhhomhepuehorhhishcu uehurhhkohhvuceosghorhhishessghurhdrihhoqeenucggtffrrghtthgvrhhnpeeiue ffuedvieeujefhheeigfekvedujeejjeffvedvhedtudefiefhkeegueehleenucfkphep udeifedruddugedrudefvddrfeenucevlhhushhtvghrufhiiigvpeegnecurfgrrhgrmh epmhgrihhlfhhrohhmpegsohhrihhssegsuhhrrdhioh X-ME-Proxy: Received: from localhost (unknown [163.114.132.3]) by mail.messagingengine.com (Postfix) with ESMTPA id 1F8281080057; Thu, 4 Feb 2021 18:22:00 -0500 (EST) From: Boris Burkov To: linux-btrfs@vger.kernel.org, kernel-team@fb.com, Eric Biggers Subject: [PATCH 5/5] btrfs: add sysfs feature for fsverity Date: Thu, 4 Feb 2021 15:21:41 -0800 Message-Id: <603ea427c87a482de165dae7b77425853b333f24.1612475783.git.boris@bur.io> X-Mailer: git-send-email 2.24.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Now that we support fsverity, enable a feature flag for it in sysfs. Signed-off-by: Boris Burkov --- fs/btrfs/sysfs.c | 6 ++++++ include/uapi/linux/btrfs.h | 1 + 2 files changed, 7 insertions(+) diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 19b9fffa2c9c..40e780724c03 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -267,6 +267,9 @@ BTRFS_FEAT_ATTR_INCOMPAT(raid1c34, RAID1C34); #ifdef CONFIG_BTRFS_DEBUG BTRFS_FEAT_ATTR_INCOMPAT(zoned, ZONED); #endif +#ifdef CONFIG_FS_VERITY +BTRFS_FEAT_ATTR_COMPAT(verity, VERITY); +#endif static struct attribute *btrfs_supported_feature_attrs[] = { BTRFS_FEAT_ATTR_PTR(mixed_backref), @@ -284,6 +287,9 @@ static struct attribute *btrfs_supported_feature_attrs[] = { BTRFS_FEAT_ATTR_PTR(raid1c34), #ifdef CONFIG_BTRFS_DEBUG BTRFS_FEAT_ATTR_PTR(zoned), +#endif +#ifdef CONFIG_FS_VERITY + BTRFS_FEAT_ATTR_PTR(verity), #endif NULL }; diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 5df73001aad4..5e0eaf37b60c 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -309,6 +309,7 @@ struct btrfs_ioctl_fs_info_args { #define BTRFS_FEATURE_INCOMPAT_RAID1C34 (1ULL << 11) #define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12) +#define BTRFS_FEATURE_COMPAT_VERITY (1ULL << 13) struct btrfs_ioctl_feature_flags { __u64 compat_flags; __u64 compat_ro_flags;