]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_db: allow recalculating CRCs on invalid metadata
authorDave Chinner <dchinner@redhat.com>
Mon, 30 May 2016 00:32:41 +0000 (10:32 +1000)
committerDave Chinner <david@fromorbit.com>
Mon, 30 May 2016 00:32:41 +0000 (10:32 +1000)
Currently we can't write corrupt structures with valid CRCs on v5
filesystems via xfs_db. TO emulate certain types of corruption
result from software bugs in the kernel code, we need this
capability to set up the corrupted state. i.e. corrupt state with a
valid CRC needs to appear on disk.

This requires us to avoid running the verifier that would otherwise
prevent writing corrupt state to disk. To enable this, add the CRC
offset to the type table for different buffers and add a new flag to
the write command to trigger running a CRC calculation base don this
type table. We can then insert the calculated value into the correct
location in the buffer...

Because some objects are not directly buffer based, we can't easily
do this CRC trick. Those object types will be marked as
TYP_NO_CRC_OFF, and as a result will emit an error such as:

# xfs_db -x -c "inode 96" -c "write -d magic 0x4949" /dev/ram0
Cannot recalculate CRCs on this type of object
#

All v4 superblock types are configured this way, as are inode,
dquots and other v5 metadata types that either don't have CRCs or
don't have a fixed offset into a buffer to store their CRC.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
db/io.c
db/io.h
db/type.c
db/type.h
db/write.c

diff --git a/db/io.c b/db/io.c
index 9452e072e95ff5e5de34ef43e652f47e8b3731b5..91cab12f72e888a8c35f7012b6130c76e585c103 100644 (file)
--- a/db/io.c
+++ b/db/io.c
@@ -463,6 +463,13 @@ xfs_dummy_verify(
        return;
 }
 
+void
+xfs_verify_recalc_crc(
+       struct xfs_buf *bp)
+{
+       xfs_buf_update_cksum(bp, iocur_top->typ->crc_off);
+}
+
 void
 write_cur(void)
 {
diff --git a/db/io.h b/db/io.h
index 6201d7b7e1163421c53f4743670daa187f8467f7..c69e9ce9e910fb8c9fddfb03baa08ae519251dad 100644 (file)
--- a/db/io.h
+++ b/db/io.h
@@ -64,6 +64,7 @@ extern void   set_cur(const struct typ *t, __int64_t d, int c, int ring_add,
 extern void     ring_add(void);
 extern void    set_iocur_type(const struct typ *t);
 extern void    xfs_dummy_verify(struct xfs_buf *bp);
+extern void    xfs_verify_recalc_crc(struct xfs_buf *bp);
 
 /*
  * returns -1 for unchecked, 0 for bad and 1 for good
index 1da7ee10766f09a5f85a20949182fcee727610eb..4bca87e3e884de4f7d43b8d3adc0f3a4daaa7d93 100644 (file)
--- a/db/type.c
+++ b/db/type.c
@@ -50,99 +50,110 @@ static const cmdinfo_t     type_cmd =
          N_("set/show current data type"), NULL };
 
 static const typ_t     __typtab[] = {
-       { TYP_AGF, "agf", handle_struct, agf_hfld, NULL },
-       { TYP_AGFL, "agfl", handle_struct, agfl_hfld, NULL },
-       { TYP_AGI, "agi", handle_struct, agi_hfld, NULL },
-       { TYP_ATTR, "attr", handle_struct, attr_hfld, NULL },
-       { TYP_BMAPBTA, "bmapbta", handle_struct, bmapbta_hfld, NULL },
-       { TYP_BMAPBTD, "bmapbtd", handle_struct, bmapbtd_hfld, NULL },
-       { TYP_BNOBT, "bnobt", handle_struct, bnobt_hfld, NULL },
-       { TYP_CNTBT, "cntbt", handle_struct, cntbt_hfld, NULL },
-       { TYP_DATA, "data", handle_block, NULL, NULL },
-       { TYP_DIR2, "dir2", handle_struct, dir2_hfld, NULL },
-       { TYP_DQBLK, "dqblk", handle_struct, dqblk_hfld, NULL },
-       { TYP_INOBT, "inobt", handle_struct, inobt_hfld, NULL },
-       { TYP_INODATA, "inodata", NULL, NULL, NULL },
-       { TYP_INODE, "inode", handle_struct, inode_hfld, NULL },
-       { TYP_LOG, "log", NULL, NULL, NULL },
-       { TYP_RTBITMAP, "rtbitmap", NULL, NULL, NULL },
-       { TYP_RTSUMMARY, "rtsummary", NULL, NULL, NULL },
-       { TYP_SB, "sb", handle_struct, sb_hfld, NULL },
-       { TYP_SYMLINK, "symlink", handle_string, NULL, NULL },
-       { TYP_TEXT, "text", handle_text, NULL, NULL },
-       { TYP_FINOBT, "finobt", handle_struct, inobt_hfld, NULL },
+       { TYP_AGF, "agf", handle_struct, agf_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_AGFL, "agfl", handle_struct, agfl_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_AGI, "agi", handle_struct, agi_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_ATTR, "attr", handle_struct, attr_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_BMAPBTA, "bmapbta", handle_struct, bmapbta_hfld, NULL,
+               TYP_F_NO_CRC_OFF },
+       { TYP_BMAPBTD, "bmapbtd", handle_struct, bmapbtd_hfld, NULL,
+               TYP_F_NO_CRC_OFF },
+       { TYP_BNOBT, "bnobt", handle_struct, bnobt_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_CNTBT, "cntbt", handle_struct, cntbt_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_DATA, "data", handle_block, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_DIR2, "dir2", handle_struct, dir2_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_DQBLK, "dqblk", handle_struct, dqblk_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_INOBT, "inobt", handle_struct, inobt_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_INODATA, "inodata", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_INODE, "inode", handle_struct, inode_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_LOG, "log", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_RTBITMAP, "rtbitmap", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_RTSUMMARY, "rtsummary", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_SB, "sb", handle_struct, sb_hfld, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_SYMLINK, "symlink", handle_string, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_TEXT, "text", handle_text, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_FINOBT, "finobt", handle_struct, inobt_hfld, NULL,
+               TYP_F_NO_CRC_OFF },
        { TYP_NONE, NULL }
 };
 
 static const typ_t     __typtab_crc[] = {
-       { TYP_AGF, "agf", handle_struct, agf_hfld, &xfs_agf_buf_ops },
-       { TYP_AGFL, "agfl", handle_struct, agfl_crc_hfld, &xfs_agfl_buf_ops },
-       { TYP_AGI, "agi", handle_struct, agi_hfld, &xfs_agi_buf_ops },
+       { TYP_AGF, "agf", handle_struct, agf_hfld, &xfs_agf_buf_ops,
+               XFS_AGF_CRC_OFF },
+       { TYP_AGFL, "agfl", handle_struct, agfl_crc_hfld, &xfs_agfl_buf_ops,
+               XFS_AGFL_CRC_OFF },
+       { TYP_AGI, "agi", handle_struct, agi_hfld, &xfs_agi_buf_ops,
+               XFS_AGI_CRC_OFF },
        { TYP_ATTR, "attr3", handle_struct, attr3_hfld,
-               &xfs_attr3_db_buf_ops },
+               &xfs_attr3_db_buf_ops, TYP_F_NO_CRC_OFF },
        { TYP_BMAPBTA, "bmapbta", handle_struct, bmapbta_crc_hfld,
-               &xfs_bmbt_buf_ops },
+               &xfs_bmbt_buf_ops, XFS_BTREE_LBLOCK_CRC_OFF },
        { TYP_BMAPBTD, "bmapbtd", handle_struct, bmapbtd_crc_hfld,
-               &xfs_bmbt_buf_ops },
+               &xfs_bmbt_buf_ops, XFS_BTREE_LBLOCK_CRC_OFF },
        { TYP_BNOBT, "bnobt", handle_struct, bnobt_crc_hfld,
-               &xfs_allocbt_buf_ops },
+               &xfs_allocbt_buf_ops, XFS_BTREE_SBLOCK_CRC_OFF },
        { TYP_CNTBT, "cntbt", handle_struct, cntbt_crc_hfld,
-               &xfs_allocbt_buf_ops },
-       { TYP_DATA, "data", handle_block, NULL, NULL },
+               &xfs_allocbt_buf_ops, XFS_BTREE_SBLOCK_CRC_OFF },
+       { TYP_DATA, "data", handle_block, NULL, NULL, TYP_F_NO_CRC_OFF },
        { TYP_DIR2, "dir3", handle_struct, dir3_hfld,
-               &xfs_dir3_db_buf_ops },
+               &xfs_dir3_db_buf_ops, TYP_F_NO_CRC_OFF },
        { TYP_DQBLK, "dqblk", handle_struct, dqblk_hfld,
-               &xfs_dquot_buf_ops },
+               &xfs_dquot_buf_ops, TYP_F_NO_CRC_OFF },
        { TYP_INOBT, "inobt", handle_struct, inobt_crc_hfld,
-               &xfs_inobt_buf_ops },
-       { TYP_INODATA, "inodata", NULL, NULL, NULL },
+               &xfs_inobt_buf_ops, XFS_BTREE_SBLOCK_CRC_OFF },
+       { TYP_INODATA, "inodata", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
        { TYP_INODE, "inode", handle_struct, inode_crc_hfld,
-               &xfs_inode_buf_ops },
-       { TYP_LOG, "log", NULL, NULL, NULL },
-       { TYP_RTBITMAP, "rtbitmap", NULL, NULL, NULL },
-       { TYP_RTSUMMARY, "rtsummary", NULL, NULL, NULL },
-       { TYP_SB, "sb", handle_struct, sb_hfld, &xfs_sb_buf_ops },
+               &xfs_inode_buf_ops, TYP_F_NO_CRC_OFF },
+       { TYP_LOG, "log", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_RTBITMAP, "rtbitmap", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_RTSUMMARY, "rtsummary", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_SB, "sb", handle_struct, sb_hfld, &xfs_sb_buf_ops,
+               XFS_SB_CRC_OFF },
        { TYP_SYMLINK, "symlink", handle_struct, symlink_crc_hfld,
-               &xfs_symlink_buf_ops },
-       { TYP_TEXT, "text", handle_text, NULL, NULL },
+               &xfs_symlink_buf_ops, XFS_SYMLINK_CRC_OFF },
+       { TYP_TEXT, "text", handle_text, NULL, NULL, TYP_F_NO_CRC_OFF },
        { TYP_FINOBT, "finobt", handle_struct, inobt_crc_hfld,
-               &xfs_inobt_buf_ops },
+               &xfs_inobt_buf_ops, XFS_BTREE_SBLOCK_CRC_OFF },
        { TYP_NONE, NULL }
 };
 
 static const typ_t     __typtab_spcrc[] = {
-       { TYP_AGF, "agf", handle_struct, agf_hfld, &xfs_agf_buf_ops },
-       { TYP_AGFL, "agfl", handle_struct, agfl_crc_hfld, &xfs_agfl_buf_ops },
-       { TYP_AGI, "agi", handle_struct, agi_hfld, &xfs_agi_buf_ops },
+       { TYP_AGF, "agf", handle_struct, agf_hfld, &xfs_agf_buf_ops,
+               XFS_AGF_CRC_OFF },
+       { TYP_AGFL, "agfl", handle_struct, agfl_crc_hfld, &xfs_agfl_buf_ops ,
+               XFS_AGFL_CRC_OFF },
+       { TYP_AGI, "agi", handle_struct, agi_hfld, &xfs_agi_buf_ops ,
+               XFS_AGI_CRC_OFF },
        { TYP_ATTR, "attr3", handle_struct, attr3_hfld,
-               &xfs_attr3_db_buf_ops },
+               &xfs_attr3_db_buf_ops, TYP_F_NO_CRC_OFF },
        { TYP_BMAPBTA, "bmapbta", handle_struct, bmapbta_crc_hfld,
-               &xfs_bmbt_buf_ops },
+               &xfs_bmbt_buf_ops, XFS_BTREE_LBLOCK_CRC_OFF },
        { TYP_BMAPBTD, "bmapbtd", handle_struct, bmapbtd_crc_hfld,
-               &xfs_bmbt_buf_ops },
+               &xfs_bmbt_buf_ops, XFS_BTREE_LBLOCK_CRC_OFF },
        { TYP_BNOBT, "bnobt", handle_struct, bnobt_crc_hfld,
-               &xfs_allocbt_buf_ops },
+               &xfs_allocbt_buf_ops, XFS_BTREE_SBLOCK_CRC_OFF },
        { TYP_CNTBT, "cntbt", handle_struct, cntbt_crc_hfld,
-               &xfs_allocbt_buf_ops },
-       { TYP_DATA, "data", handle_block, NULL, NULL },
+               &xfs_allocbt_buf_ops, XFS_BTREE_SBLOCK_CRC_OFF },
+       { TYP_DATA, "data", handle_block, NULL, NULL, TYP_F_NO_CRC_OFF },
        { TYP_DIR2, "dir3", handle_struct, dir3_hfld,
-               &xfs_dir3_db_buf_ops },
+               &xfs_dir3_db_buf_ops, TYP_F_NO_CRC_OFF },
        { TYP_DQBLK, "dqblk", handle_struct, dqblk_hfld,
-               &xfs_dquot_buf_ops },
+               &xfs_dquot_buf_ops, TYP_F_NO_CRC_OFF },
        { TYP_INOBT, "inobt", handle_struct, inobt_spcrc_hfld,
-               &xfs_inobt_buf_ops },
-       { TYP_INODATA, "inodata", NULL, NULL, NULL },
+               &xfs_inobt_buf_ops, XFS_BTREE_SBLOCK_CRC_OFF },
+       { TYP_INODATA, "inodata", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
        { TYP_INODE, "inode", handle_struct, inode_crc_hfld,
-               &xfs_inode_buf_ops },
-       { TYP_LOG, "log", NULL, NULL, NULL },
-       { TYP_RTBITMAP, "rtbitmap", NULL, NULL, NULL },
-       { TYP_RTSUMMARY, "rtsummary", NULL, NULL, NULL },
-       { TYP_SB, "sb", handle_struct, sb_hfld, &xfs_sb_buf_ops },
+               &xfs_inode_buf_ops, TYP_F_NO_CRC_OFF },
+       { TYP_LOG, "log", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_RTBITMAP, "rtbitmap", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_RTSUMMARY, "rtsummary", NULL, NULL, NULL, TYP_F_NO_CRC_OFF },
+       { TYP_SB, "sb", handle_struct, sb_hfld, &xfs_sb_buf_ops,
+               XFS_SB_CRC_OFF },
        { TYP_SYMLINK, "symlink", handle_struct, symlink_crc_hfld,
-               &xfs_symlink_buf_ops },
-       { TYP_TEXT, "text", handle_text, NULL, NULL },
+               &xfs_symlink_buf_ops, XFS_SYMLINK_CRC_OFF },
+       { TYP_TEXT, "text", handle_text, NULL, NULL, TYP_F_NO_CRC_OFF },
        { TYP_FINOBT, "finobt", handle_struct, inobt_crc_hfld,
-               &xfs_inobt_buf_ops },
+               &xfs_inobt_buf_ops, XFS_BTREE_SBLOCK_CRC_OFF },
        { TYP_NONE, NULL }
 };
 
index d9583e55328b0fa8cdadbd9d2e577a5780431928..e5d6d10edb329ee421326f406c8516b787c53212 100644 (file)
--- a/db/type.h
+++ b/db/type.h
@@ -43,6 +43,8 @@ typedef struct typ
        pfunc_t                 pfunc;
        const struct field      *fields;
        const struct xfs_buf_ops *bops;
+       unsigned long           crc_off;
+#define TYP_F_NO_CRC_OFF       (-1UL)
 } typ_t;
 extern const typ_t     *typtab, *cur_typ;
 
index 9f5b4234d4a4f2c135863a836480873ed080495e..82a0c746cd61cb6cdea35daa230eecdc164c061b 100644 (file)
@@ -79,7 +79,10 @@ write_help(void)
 "  String mode: 'write \"This_is_a_filename\" - write null terminated string.\n"
 "\n"
 " In data mode type 'write' by itself for a list of specific commands.\n\n"
-" Specifying the -c option will allow writes of invalid (corrupt) data.\n\n"
+" Specifying the -c option will allow writes of invalid (corrupt) data with\n"
+" an invalid CRC. Specifying the -d option will allow writes of invalid data,\n"
+" but still recalculate the CRC so we are forced to check and detect the\n"
+" invalid data appropriately.\n\n"
 ));
 
 }
@@ -92,8 +95,9 @@ write_f(
        pfunc_t pf;
        extern char *progname;
        int c;
-       int corrupt = 0;                /* Allow write of corrupt data; skip verification */
-       struct xfs_buf_ops nowrite_ops;
+       bool corrupt = false;   /* Allow write of bad data w/ invalid CRC */
+       bool invalid_data = false; /* Allow write of bad data w/ valid CRC */
+       struct xfs_buf_ops local_ops;
        const struct xfs_buf_ops *stashed_ops = NULL;
 
        if (x.isreadonly & LIBXFS_ISREADONLY) {
@@ -114,10 +118,13 @@ write_f(
                return 0;
        }
 
-       while ((c = getopt(argc, argv, "c")) != EOF) {
+       while ((c = getopt(argc, argv, "cd")) != EOF) {
                switch (c) {
                case 'c':
-                       corrupt = 1;
+                       corrupt = true;
+                       break;
+               case 'd':
+                       invalid_data = true;
                        break;
                default:
                        dbprintf(_("bad option for write command\n"));
@@ -125,22 +132,46 @@ write_f(
                }
        }
 
+       if (corrupt && invalid_data) {
+               dbprintf(_("Cannot specify both -c and -d options\n"));
+               return 0;
+       }
+
+       if (invalid_data && iocur_top->typ->crc_off == TYP_F_NO_CRC_OFF) {
+               dbprintf(_("Cannot recalculate CRCs on this type of object\n"));
+               return 0;
+       }
+
        argc -= optind;
        argv += optind;
 
-       if (iocur_top->bp->b_ops && corrupt) {
-               /* Temporarily remove write verifier to write bad data */
-               stashed_ops = iocur_top->bp->b_ops;
-               nowrite_ops.verify_read = stashed_ops->verify_read;
-               nowrite_ops.verify_write = xfs_dummy_verify;
-               iocur_top->bp->b_ops = &nowrite_ops;
-               dbprintf(_("Allowing write of corrupted data\n"));
+       /*
+        * If the buffer has no verifier or we are using standard verifier
+        * paths, then just write it out and return
+        */
+       if (!iocur_top->bp->b_ops ||
+           !(corrupt || invalid_data)) {
+               (*pf)(DB_WRITE, cur_typ->fields, argc, argv);
+               return 0;
+       }
+
+
+       /* Temporarily remove write verifier to write bad data */
+       stashed_ops = iocur_top->bp->b_ops;
+       local_ops.verify_read = stashed_ops->verify_read;
+       iocur_top->bp->b_ops = &local_ops;
+
+       if (corrupt) {
+               local_ops.verify_write = xfs_dummy_verify;
+               dbprintf(_("Allowing write of corrupted data and bad CRC\n"));
+       } else { /* invalid data */
+               local_ops.verify_write = xfs_verify_recalc_crc;
+               dbprintf(_("Allowing write of corrupted data with good CRC\n"));
        }
 
        (*pf)(DB_WRITE, cur_typ->fields, argc, argv);
 
-       if (stashed_ops)
-               iocur_top->bp->b_ops = stashed_ops;
+       iocur_top->bp->b_ops = stashed_ops;
 
        return 0;
 }