From: "Darrick J. Wong" <[email protected]>

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 <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
Signed-off-by: Andrey Albershteyn <[email protected]>
---
 fs/xfs/scrub/attr.c         |  7 +++++
 fs/xfs/scrub/common.c       | 53 +++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h       |  2 ++
 fs/xfs/scrub/inode.c        |  7 +++++
 fs/xfs/scrub/inode_repair.c | 36 +++++++++++++++++++++++++
 5 files changed, 105 insertions(+)

diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c
index 390ac2e11ee0..daf7962c2374 100644
--- a/fs/xfs/scrub/attr.c
+++ b/fs/xfs/scrub/attr.c
@@ -649,6 +649,13 @@ xchk_xattr(
        if (!xfs_inode_hasattr(sc->ip))
                return -ENOENT;
 
+       /*
+        * If this is a verity file that won't activate, we cannot check the
+        * merkle tree geometry.
+        */
+       if (xchk_inode_verity_broken(sc->ip))
+               xchk_set_incomplete(sc);
+
        /* Allocate memory for xattr checking. */
        error = xchk_setup_xattr_buf(sc, 0);
        if (error == -ENOMEM)
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 3d40cb0b2496..706a2777d7e6 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,54 @@ 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:
+               /*
+                * 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 b494d747c008..ff51bbe62e9f 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -266,6 +266,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 948d04dcba2a..8ce6917e22b4 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, sc->ip->i_ino);
 
+       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, sc->ip->i_ino, &di);
diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c
index 9738b9ce3f2d..3761e3922466 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;
@@ -1613,6 +1615,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;
@@ -2032,6 +2038,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->i_flags &= ~S_VERITY;
+
+               xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
+       }
+
+       return 0;
+}
+
 /* Repair an inode's fields. */
 int
 xrep_inode(
@@ -2081,6 +2108,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.51.2



_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to