2012-03-06 23:57:33

by djwong

[permalink] [raw]
Subject: [PATCH v3 00/54] e2fsprogs: Add metadata checksumming

Hi all,

This patchset adds support in e2fsprogs for attaching crc32c checksums to most
of the ext4 metadata objects. A full design document is on the ext4 wiki[1].
Please see the cover letter for the kernel patches for a more thorough summary
of this work.

Please have a look at the design document and patches, and please feel free to
suggest any changes.

v2: Checksum the MMP block, store the checksum type in the superblock, include
the inode generation in file checksums, and finally solve the problem of limited
space in block groups by splitting the checksum into two halves.

v2.1: Checksum the reserved parts of the htree tail structure.

v2.2: Reincorporate the FS UUID in the bitmap checksum calcuations. Move all
disk layout changes to the front and the feature flag enablement to the end of
the patch set. Fail journal recovery if revoke block fails checksum.

v2.3: Change fsck handling of checksum errors: First ask to clear the item; if
the user declines, run the regular checks; if no corrective actions result, ask
to reset the checksum. Also, precompute the UUID checksum seed, refactor the
inode generation patch into respective patches, and clean up structure member
declarations.

v3: Eliminate the bg_use_meta_csum feature flag and make metadata_csum
supersede uninit_bg. Enable metadata_csum by default on ext4dev filesystems.

This patchset has been tested on 3.3.0-rc6 on x64 and i386. The patches seems
to work fine on both platforms. The patchset is based atop the 1.42.1 release.

--D

[1] https://ext4.wiki.kernel.org/articles/e/x/t/Ext4_Disk_Layout_aecb.html



2012-03-06 23:57:55

by djwong

[permalink] [raw]
Subject: [PATCH 04/54] libext2fs: Precompute FS UUID checksum seed

Precompute the FS UUID checksum seed that is used for all metadata checksumming
operations and store it in ext2_filsys.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/super.c | 1 +
lib/ext2fs/ext2fs.h | 14 ++++++++++++++
lib/ext2fs/openfs.c | 2 ++
misc/mke2fs.c | 1 +
4 files changed, 18 insertions(+), 0 deletions(-)


diff --git a/e2fsck/super.c b/e2fsck/super.c
index 3397d77..94648c3 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -710,6 +710,7 @@ void check_super_block(e2fsck_t ctx)
if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
uuid_generate(sb->s_uuid);
+ ext2fs_init_csum_seed(fs);
fs->flags |= EXT2_FLAG_DIRTY;
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
}
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index bde22b6..f7a73e5 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -281,6 +281,9 @@ struct struct_ext2_filsys {
* Time at which e2fsck last updated the MMP block.
*/
long mmp_last_written;
+
+ /* Precomputed FS UUID checksum for seeding other checksums */
+ __u32 csum_seed;
};

#if EXT2_FLAT_INCLUDES
@@ -1438,6 +1441,7 @@ extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,


/* inline functions */
+extern void ext2fs_init_csum_seed(ext2_filsys fs);
extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
extern errcode_t ext2fs_get_memalign(unsigned long size,
unsigned long align, void *ptr);
@@ -1490,6 +1494,16 @@ extern int ext2fs_fstat(int fd, ext2fs_struct_stat *buf);

#ifndef EXT2_CUSTOM_MEMORY_ROUTINES
#include <string.h>
+_INLINE_ void ext2fs_init_csum_seed(ext2_filsys fs)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return;
+
+ fs->csum_seed = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+}
+
/*
* Allocate memory
*/
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 32e068c..10beb06 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -298,6 +298,8 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
retval = EXT2_ET_CORRUPT_SUPERBLOCK;
goto cleanup;
}
+ /* Precompute the FS UUID to seed other checksums */
+ ext2fs_init_csum_seed(fs);

/*
* Read group descriptors
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index c70c1b4..d80c0b2 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -2318,6 +2318,7 @@ int main (int argc, char *argv[])
}
} else
uuid_generate(fs->super->s_uuid);
+ ext2fs_init_csum_seed(fs);

/*
* Initialize the directory index variables


2012-03-06 23:57:51

by djwong

[permalink] [raw]
Subject: [PATCH 03/54] debugfs: Optionally ignore bad checksums

If someone is debugging a badly damaged filesystem, it might be useful to
disable the checksum verifications that will otherwise prevent the filesystem
from loading.

Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/debugfs.8.in | 7 ++++++-
debugfs/debugfs.c | 7 +++++--
2 files changed, 11 insertions(+), 3 deletions(-)


diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in
index 08d9068..157c667 100644
--- a/debugfs/debugfs.8.in
+++ b/debugfs/debugfs.8.in
@@ -8,7 +8,7 @@ debugfs \- ext2/ext3/ext4 file system debugger
.SH SYNOPSIS
.B debugfs
[
-.B \-DVwci
+.B \-DVwcin
]
[
.B \-b
@@ -48,6 +48,11 @@ file system (e.g /dev/hdXX).
Specifies that the file system should be opened in read-write mode.
Without this option, the file system is opened in read-only mode.
.TP
+.I \-n
+Disables metadata checksum verification. This should only be used if
+you believe the metadata to be correct despite the complaints of
+e2fsprogs.
+.TP
.I \-c
Specifies that the file system should be opened in catastrophic mode, in
which the inode and group bitmaps are not read initially. This can be
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 04d5139..4ff23ef 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -2271,9 +2271,9 @@ int main(int argc, char **argv)
int catastrophic = 0;
char *data_filename = 0;
#ifdef READ_ONLY
- const char *opt_string = "icR:f:b:s:Vd:D";
+ const char *opt_string = "nicR:f:b:s:Vd:D";
#else
- const char *opt_string = "iwcR:f:b:s:Vd:D";
+ const char *opt_string = "niwcR:f:b:s:Vd:D";
#endif

if (debug_prog_name == 0)
@@ -2300,6 +2300,9 @@ int main(int argc, char **argv)
case 'i':
open_flags |= EXT2_FLAG_IMAGE_FILE;
break;
+ case 'n':
+ open_flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ break;
#ifndef READ_ONLY
case 'w':
open_flags |= EXT2_FLAG_RW;


2012-03-06 23:57:50

by djwong

[permalink] [raw]
Subject: [PATCH 02/54] libext2fs: Change ext4 on-disk layout to support metadata checksumming

Define flags and extend ext4 structure definitions to support metadata
checksumming. Ted T'so covered many of these fields in an earlier patch, but
there are more required changes to the disk layout.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/blkid/probe.h | 1 +
lib/ext2fs/ext2_ext_attr.h | 4 +++-
lib/ext2fs/ext2_fs.h | 40 ++++++++++++++++++++++++++++++++++++++--
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/ext3_extents.h | 11 +++++++++++
5 files changed, 54 insertions(+), 3 deletions(-)


diff --git a/lib/blkid/probe.h b/lib/blkid/probe.h
index 37e80ef..d6809e1 100644
--- a/lib/blkid/probe.h
+++ b/lib/blkid/probe.h
@@ -110,6 +110,7 @@ struct ext2_super_block {
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100
+#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400

/* for s_feature_incompat */
#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
diff --git a/lib/ext2fs/ext2_ext_attr.h b/lib/ext2fs/ext2_ext_attr.h
index ed548d1..bbb0aaa 100644
--- a/lib/ext2fs/ext2_ext_attr.h
+++ b/lib/ext2fs/ext2_ext_attr.h
@@ -20,7 +20,9 @@ struct ext2_ext_attr_header {
__u32 h_refcount; /* reference count */
__u32 h_blocks; /* number of disk blocks used */
__u32 h_hash; /* hash value of all attributes */
- __u32 h_reserved[4]; /* zero right now */
+ __u32 h_checksum; /* crc32c(uuid+id+xattrs) */
+ /* id = inum if refcount = 1, else blknum */
+ __u32 h_reserved[3]; /* zero right now */
};

struct ext2_ext_attr_entry {
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 3b01285..b0b798b 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -235,6 +235,13 @@ struct ext2_dx_countlimit {
__u16 count;
};

+/*
+ * This goes at the end of each htree block.
+ */
+struct ext2_dx_tail {
+ __u32 dt_reserved;
+ __u32 dt_checksum; /* crc32c(uuid+inum+dxblock) */
+};

/*
* Macro-instructions used to manage group descriptors
@@ -463,6 +470,7 @@ struct ext2_inode_large {
#define i_gid_low i_gid
#define i_uid_high osd2.linux2.l_i_uid_high
#define i_gid_high osd2.linux2.l_i_gid_high
+#define i_checksum_lo osd2.linux2.l_i_checksum_lo
#else
#if defined(__GNU__)

@@ -534,6 +542,9 @@ struct ext2_inode_large {
#define ext4_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

+/* Metadata checksum algorithms */
+#define EXT2_CRC32C_CHKSUM 1
+
/*
* Structure of the super block
*/
@@ -619,7 +630,7 @@ struct ext2_super_block {
__u64 s_mmp_block; /* Block for multi-mount protection */
__u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
__u8 s_log_groups_per_flex; /* FLEX_BG group size */
- __u8 s_reserved_char_pad;
+ __u8 s_checksum_type; /* metadata checksum algorithm */
__u16 s_reserved_pad; /* Padding to next 32bits */
__u64 s_kbytes_written; /* nr of lifetime kilobytes written */
__u32 s_snapshot_inum; /* Inode number of active snapshot */
@@ -707,6 +718,11 @@ struct ext2_super_block {
#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080
#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100
#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200
+/*
+ * METADATA_CSUM implies GDT_CSUM. When METADATA_CSUM is set, group
+ * descriptor checksums use the same algorithm as all other data
+ * structures' checksums.
+ */
#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
#define EXT4_FEATURE_RO_COMPAT_REPLICA 0x0800

@@ -780,6 +796,17 @@ struct ext2_dir_entry_2 {
};

/*
+ * This is a bogus directory entry at the end of each leaf block that
+ * records checksums.
+ */
+struct ext2_dir_entry_tail {
+ __u32 det_reserved_zero1; /* Pretend to be unused */
+ __u16 det_rec_len; /* 12 */
+ __u16 det_reserved_name_len; /* 0xDE00, fake namelen/filetype */
+ __u32 det_checksum; /* crc32c(uuid+inode+dirent) */
+};
+
+/*
* Ext2 directory file types. Only the low 3 bits are used. The
* other bits are reserved for now.
*/
@@ -795,6 +822,14 @@ struct ext2_dir_entry_2 {
#define EXT2_FT_MAX 8

/*
+ * Annoyingly, e2fsprogs always swab16s ext2_dir_entry.name_len, so we
+ * have to build ext2_dir_entry_tail with that assumption too. This
+ * constant helps to build the dir_entry_tail to look like it has an
+ * "invalid" file type.
+ */
+#define EXT2_DIR_NAME_LEN_CSUM 0xDE00
+
+/*
* EXT2_DIR_PAD defines the directory entries boundaries
*
* NOTE: It must be a multiple of 4
@@ -835,7 +870,8 @@ struct mmp_struct {
char mmp_bdevname[32]; /* Bdev which last updated MMP block */
__u16 mmp_check_interval; /* Changed mmp_check_interval */
__u16 mmp_pad1;
- __u32 mmp_pad2[227];
+ __u32 mmp_pad2[226];
+ __u32 mmp_checksum; /* crc32c(uuid+mmp_block) */
};

/*
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 682f0cd..bde22b6 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -199,6 +199,7 @@ typedef struct ext2_file *ext2_file_t;
#define EXT2_FLAG_PRINT_PROGRESS 0x40000
#define EXT2_FLAG_DIRECT_IO 0x80000
#define EXT2_FLAG_SKIP_MMP 0x100000
+#define EXT2_FLAG_IGNORE_CSUM_ERRORS 0x200000

/*
* Special flag in the ext2 inode i_flag field that means that this is
diff --git a/lib/ext2fs/ext3_extents.h b/lib/ext2fs/ext3_extents.h
index 88fabc9..4163436 100644
--- a/lib/ext2fs/ext3_extents.h
+++ b/lib/ext2fs/ext3_extents.h
@@ -19,6 +19,17 @@
*/

/*
+ * This is extent tail on-disk structure.
+ * All other extent structures are 12 bytes long. It turns out that
+ * block_size % 12 >= 4 for at least all powers of 2 greater than 512, which
+ * covers all valid ext4 block sizes. Therefore, this tail structure can be
+ * crammed into the end of the block without having to rebalance the tree.
+ */
+struct ext3_extent_tail {
+ __u32 et_checksum; /* crc32c(uuid+inum+extent_block) */
+};
+
+/*
* this is extent on-disk structure
* it's used at the bottom of the tree
*/


2012-03-06 23:58:14

by djwong

[permalink] [raw]
Subject: [PATCH 07/54] tune2fs: Add inode checksum support

This patch adds to tune2fs the ability to toggle the metadata checksum rocompat
feature flag, which will rewrite the inode table with checksums. Disallow
changing the UUID while the fs is mounted, because rewriting the metadata
objects is racy.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 106 insertions(+), 2 deletions(-)


diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 74a0489..cb1aff3 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -91,6 +91,7 @@ static char *extended_cmd;
static unsigned long new_inode_size;
static char *ext_mount_opts;
static int usrquota, grpquota;
+static int rewrite_checksums;

int journal_size, journal_flags;
char *journal_device;
@@ -142,7 +143,8 @@ static __u32 ok_features[3] = {
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
- EXT4_FEATURE_RO_COMPAT_QUOTA
+ EXT4_FEATURE_RO_COMPAT_QUOTA |
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
};

static __u32 clear_ok_features[3] = {
@@ -160,7 +162,8 @@ static __u32 clear_ok_features[3] = {
EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
- EXT4_FEATURE_RO_COMPAT_QUOTA
+ EXT4_FEATURE_RO_COMPAT_QUOTA |
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
};

/*
@@ -351,6 +354,16 @@ static int update_mntopts(ext2_filsys fs, char *mntopts)
return 0;
}

+static int check_fsck_needed(ext2_filsys fs)
+{
+ if (fs->super->s_state & EXT2_VALID_FS)
+ return 0;
+ printf("\n%s\n", _(please_fsck));
+ if (mount_flags & EXT2_MF_READONLY)
+ printf(_("(and reboot afterwards!)\n"));
+ return 1;
+}
+
static void request_fsck_afterwards(ext2_filsys fs)
{
static int requested = 0;
@@ -364,6 +377,60 @@ static void request_fsck_afterwards(ext2_filsys fs)
}

/*
+ * Forcibly set checksums in all inodes.
+ */
+static void rewrite_inodes(ext2_filsys fs)
+{
+ int length = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode *inode;
+ ext2_inode_scan scan;
+ errcode_t retval;
+ ext2_ino_t ino;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX)
+ return;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval) {
+ com_err("set_csum", retval, "while opening inode scan");
+ exit(1);
+ }
+
+ retval = ext2fs_get_mem(length, &inode);
+ if (retval) {
+ com_err("set_csum", retval, "while allocating memory");
+ exit(1);
+ }
+
+ do {
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
+ if (retval) {
+ com_err("set_csum", retval, "while getting next inode");
+ exit(1);
+ }
+ if (!ino)
+ break;
+
+ retval = ext2fs_write_inode_full(fs, ino, inode, length);
+ if (retval) {
+ com_err("set_csum", retval, "while writing inode");
+ exit(1);
+ }
+ } while (ino);
+
+ ext2fs_free_mem(&inode);
+ ext2fs_close_inode_scan(scan);
+}
+
+static void rewrite_metadata_checksums(ext2_filsys fs)
+{
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ ext2fs_init_csum_seed(fs);
+ rewrite_inodes(fs);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+}
+
+/*
* Update the feature set as provided by the user.
*/
static int update_feature_set(ext2_filsys fs, char *features)
@@ -536,6 +603,20 @@ mmp_error:
}

if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (check_fsck_needed(fs))
+ exit(1);
+ rewrite_checksums = 1;
+ }
+
+ if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (check_fsck_needed(fs))
+ exit(1);
+ rewrite_checksums = 1;
+ }
+
+ if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
for (i = 0; i < fs->group_desc_count; i++) {
gd = ext2fs_group_desc(fs, fs->group_desc, i);
@@ -2128,6 +2209,23 @@ retry_open:
int set_csum = 0;
dgrp_t i;

+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ /*
+ * Changing the UUID requires rewriting all metadata,
+ * which can race with a mounted fs. Don't allow that.
+ */
+ if (mount_flags & EXT2_MF_MOUNTED) {
+ fputs(_("The UUID may only be "
+ "changed when the filesystem is "
+ "unmounted.\n"), stderr);
+ exit(1);
+ }
+
+ if (check_fsck_needed(fs))
+ exit(1);
+ }
+
if (sb->s_feature_ro_compat &
EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
/*
@@ -2153,13 +2251,19 @@ retry_open:
rc = 1;
goto closefs;
}
+ ext2fs_init_csum_seed(fs);
if (set_csum) {
for (i = 0; i < fs->group_desc_count; i++)
ext2fs_group_desc_csum_set(fs, i);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
}
ext2fs_mark_super_dirty(fs);
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ rewrite_checksums = 1;
}
+ if (rewrite_checksums)
+ rewrite_metadata_checksums(fs);
if (I_flag) {
if (mount_flags & EXT2_MF_MOUNTED) {
fputs(_("The inode size may only be "


2012-03-06 23:58:08

by djwong

[permalink] [raw]
Subject: [PATCH 06/54] debugfs: Dump inode checksum when appropriate

Dump inode checksum when displaying inode info

Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/debugfs.c | 13 +++++++++++++
1 files changed, 13 insertions(+), 0 deletions(-)


diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 4ff23ef..36dd730 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -803,6 +803,19 @@ void internal_dump_inode(FILE *out, const char *prefix,
if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
internal_dump_inode_extra(out, prefix, inode_num,
(struct ext2_inode_large *) inode);
+ if (current_fs->super->s_creator_os == EXT2_OS_LINUX &&
+ current_fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+ __u32 crc = inode->i_checksum_lo;
+ if (is_large_inode &&
+ large_inode->i_extra_isize >=
+ (offsetof(struct ext2_inode_large,
+ i_checksum_hi) -
+ EXT2_GOOD_OLD_INODE_SIZE))
+ crc |= ((__u32)large_inode->i_checksum_hi) << 16;
+ fprintf(out, "Inode checksum: 0x%08x\n", crc);
+ }
+
if (LINUX_S_ISLNK(inode->i_mode) && ext2fs_inode_data_blocks(current_fs,inode) == 0)
fprintf(out, "%sFast_link_dest: %.*s\n", prefix,
(int) inode->i_size, (char *)inode->i_block);


2012-03-06 23:58:24

by djwong

[permalink] [raw]
Subject: [PATCH 01/54] libext2fs: Read and write full size inodes

Change libext2fs to read and write full-size inodes in preparation for the
metadata checksumming patchset, which will require this. Due to ABI
compatibility requirements, this change must be hidden from client programs.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 3 +
lib/ext2fs/ext2fsP.h | 2 -
lib/ext2fs/inode.c | 139 +++++++++++++++++++++++++++-----------------------
3 files changed, 79 insertions(+), 65 deletions(-)


diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 5faa093..c31e073 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -691,7 +691,8 @@ void e2fsck_pass1(e2fsck_t ctx)
}
block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
"block interate buffer");
- e2fsck_use_inode_shortcuts(ctx, 1);
+ if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
+ e2fsck_use_inode_shortcuts(ctx, 1);
old_op = ehandler_operation(_("opening inode scan"));
pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
&scan);
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index 729d5c5..4db0ae9 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -75,7 +75,7 @@ struct ext2_inode_cache {

struct ext2_inode_cache_ent {
ext2_ino_t ino;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
};

/* Function prototypes */
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 6c524ff..a4f6670 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -74,7 +74,9 @@ errcode_t ext2fs_flush_icache(ext2_filsys fs)

static errcode_t create_icache(ext2_filsys fs)
{
+ int i;
errcode_t retval;
+ void *p;

if (fs->icache)
return 0;
@@ -93,13 +95,20 @@ static errcode_t create_icache(ext2_filsys fs)
fs->icache->cache_size = 4;
fs->icache->refcount = 1;
retval = ext2fs_get_array(fs->icache->cache_size,
- sizeof(struct ext2_inode_cache_ent),
+ sizeof(struct ext2_inode_cache_ent) +
+ EXT2_INODE_SIZE(fs->super),
&fs->icache->cache);
if (retval) {
ext2fs_free_mem(&fs->icache->buffer);
ext2fs_free_mem(&fs->icache);
return retval;
}
+
+ for (i = 0, p = (void *)(fs->icache->cache + fs->icache->cache_size);
+ i < fs->icache->cache_size;
+ i++, p += EXT2_INODE_SIZE(fs->super))
+ fs->icache->cache[i].inode = p;
+
ext2fs_flush_icache(fs);
return 0;
}
@@ -407,6 +416,8 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
{
errcode_t retval;
int extra_bytes = 0;
+ const int length = EXT2_INODE_SIZE(scan->fs->super);
+ struct ext2_inode *iptr = inode;

EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);

@@ -469,6 +480,12 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
#endif
}

+ if (bufsize < length) {
+ retval = ext2fs_get_mem(length, &iptr);
+ if (retval)
+ return retval;
+ }
+
retval = 0;
if (extra_bytes) {
memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
@@ -477,26 +494,26 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->bytes_left -= scan->inode_size - extra_bytes;

#ifdef WORDS_BIGENDIAN
- memset(inode, 0, bufsize);
+ memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
- (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) iptr,
(struct ext2_inode_large *) scan->temp_buffer,
- 0, bufsize);
+ 0, length);
#else
- *inode = *((struct ext2_inode *) scan->temp_buffer);
+ memcpy(iptr, scan->temp_buffer, length);
#endif
if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
} else {
#ifdef WORDS_BIGENDIAN
- memset(inode, 0, bufsize);
+ memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
- (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) iptr,
(struct ext2_inode_large *) scan->ptr,
- 0, bufsize);
+ 0, length);
#else
- memcpy(inode, scan->ptr, bufsize);
+ memcpy(iptr, scan->ptr, length);
#endif
scan->ptr += scan->inode_size;
scan->bytes_left -= scan->inode_size;
@@ -507,6 +524,10 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->inodes_left--;
scan->current_inode++;
*ino = scan->current_inode;
+ if (inode != iptr) {
+ memcpy(inode, iptr, bufsize);
+ ext2fs_free_mem(&iptr);
+ }
return retval;
}

@@ -527,8 +548,11 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
unsigned long group, block, offset;
char *ptr;
errcode_t retval;
- int clen, i, inodes_per_block, length;
+ int clen, i, inodes_per_block;
io_channel io;
+ int length = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode *iptr;
+ int cache_slot;

EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

@@ -547,13 +571,10 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
return retval;
}
/* Check to see if it's in the inode cache */
- if (bufsize == sizeof(struct ext2_inode)) {
- /* only old good inode can be retrieved from the cache */
- for (i=0; i < fs->icache->cache_size; i++) {
- if (fs->icache->cache[i].ino == ino) {
- *inode = fs->icache->cache[i].inode;
- return 0;
- }
+ for (i = 0; i < fs->icache->cache_size; i++) {
+ if (fs->icache->cache[i].ino == ino) {
+ memcpy(inode, fs->icache->cache[i].inode, bufsize);
+ return 0;
}
}
if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
@@ -578,11 +599,10 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
}
offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

- length = EXT2_INODE_SIZE(fs->super);
- if (bufsize < length)
- length = bufsize;
+ cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size;
+ iptr = fs->icache->cache[cache_slot].inode;

- ptr = (char *) inode;
+ ptr = (char *) iptr;
while (length) {
clen = length;
if ((offset + length) > fs->blocksize)
@@ -604,18 +624,18 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
ptr += clen;
block_nr++;
}
+ length = EXT2_INODE_SIZE(fs->super);

#ifdef WORDS_BIGENDIAN
- ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode,
- (struct ext2_inode_large *) inode,
- 0, bufsize);
+ ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
+ (struct ext2_inode_large *) iptr,
+ 0, length);
#endif

- /* Update the inode cache */
- fs->icache->cache_last = (fs->icache->cache_last + 1) %
- fs->icache->cache_size;
- fs->icache->cache[fs->icache->cache_last].ino = ino;
- fs->icache->cache[fs->icache->cache_last].inode = *inode;
+ /* Update the inode cache bookkeeping */
+ fs->icache->cache_last = cache_slot;
+ fs->icache->cache[cache_slot].ino = ino;
+ memcpy(inode, iptr, bufsize);

return 0;
}
@@ -633,9 +653,10 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
blk64_t block_nr;
unsigned long group, block, offset;
errcode_t retval = 0;
- struct ext2_inode_large temp_inode, *w_inode;
+ struct ext2_inode_large *w_inode;
char *ptr;
- int clen, i, length;
+ int clen, i;
+ int length = EXT2_INODE_SIZE(fs->super);

EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

@@ -646,46 +667,43 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
return retval;
}

+ if ((ino == 0) || (ino > fs->super->s_inodes_count))
+ return EXT2_ET_BAD_INODE_NUM;
+
+ /* Prepare our shadow buffer for read/modify/byteswap/write */
+ retval = ext2fs_get_mem(length, &w_inode);
+ if (retval)
+ return retval;
+ if (bufsize < length)
+ retval = ext2fs_read_inode_full(fs, ino,
+ (struct ext2_inode *)w_inode,
+ length);
+ if (retval)
+ goto errout;
+
/* Check to see if the inode cache needs to be updated */
if (fs->icache) {
for (i=0; i < fs->icache->cache_size; i++) {
if (fs->icache->cache[i].ino == ino) {
- fs->icache->cache[i].inode = *inode;
+ memcpy(fs->icache->cache[i].inode, inode,
+ bufsize);
break;
}
}
} else {
retval = create_icache(fs);
if (retval)
- return retval;
+ goto errout;
}
+ memcpy(w_inode, inode, bufsize);

- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
-
- if ((ino == 0) || (ino > fs->super->s_inodes_count))
- return EXT2_ET_BAD_INODE_NUM;
-
- length = bufsize;
- if (length < EXT2_INODE_SIZE(fs->super))
- length = EXT2_INODE_SIZE(fs->super);
-
- if (length > (int) sizeof(struct ext2_inode_large)) {
- w_inode = malloc(length);
- if (!w_inode) {
- retval = ENOMEM;
- goto errout;
- }
- } else
- w_inode = &temp_inode;
- memset(w_inode, 0, length);
+ if (!(fs->flags & EXT2_FLAG_RW)) {
+ retval = EXT2_ET_RO_FILSYS;
+ goto errout;
+ }

#ifdef WORDS_BIGENDIAN
- ext2fs_swap_inode_full(fs, w_inode,
- (struct ext2_inode_large *) inode,
- 1, bufsize);
-#else
- memcpy(w_inode, inode, bufsize);
+ ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
#endif

group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
@@ -700,10 +718,6 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,

offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

- length = EXT2_INODE_SIZE(fs->super);
- if (length > bufsize)
- length = bufsize;

2012-03-06 23:58:24

by djwong

[permalink] [raw]
Subject: [PATCH 05/54] libext2fs: Add inode checksum support

This patch adds the ability for the libext2fs functions to read and write the
inode checksum.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2_fs.h | 4 ++
lib/ext2fs/ext2fs.h | 4 ++
lib/ext2fs/inode.c | 40 ++++++++++++++++++----
lib/ext2fs/mkdir.c | 8 +++-
6 files changed, 131 insertions(+), 10 deletions(-)


diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 596923e..5091a00 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,88 @@
#define STATIC static
#endif

+static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode,
+ __u32 *crc, int has_hi)
+{
+ __u32 gen;
+ struct ext2_inode_large *desc = inode;
+ size_t size = fs->super->s_inode_size;
+ __u16 old_lo;
+ __u16 old_hi = 0;
+
+ old_lo = inode->i_checksum_lo;
+ inode->i_checksum_lo = 0;
+ if (has_hi) {
+ old_hi = inode->i_checksum_hi;
+ inode->i_checksum_hi = 0;
+ }
+
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = inode->i_generation;
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)desc, size);
+
+ inode->i_checksum_lo = old_lo;
+ if (has_hi)
+ inode->i_checksum_hi = old_hi;
+ return 0;
+}
+
+int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode)
+{
+ errcode_t retval;
+ __u32 provided, calculated;
+ int has_hi;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX ||
+ !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
+
+ provided = ext2fs_le16_to_cpu(inode->i_checksum_lo);
+ retval = ext2fs_inode_csum(fs, inum, inode, &calculated, has_hi);
+ if (retval)
+ return 0;
+ if (has_hi) {
+ __u32 hi = ext2fs_le16_to_cpu(inode->i_checksum_hi);
+ provided |= hi << 16;
+ } else
+ calculated &= 0xFFFF;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode)
+{
+ errcode_t retval;
+ __u32 crc;
+ int has_hi;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX ||
+ !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
+
+ retval = ext2fs_inode_csum(fs, inum, inode, &crc, has_hi);
+ if (retval)
+ return retval;
+ inode->i_checksum_lo = ext2fs_cpu_to_le16(crc & 0xFFFF);
+ if (has_hi)
+ inode->i_checksum_hi = ext2fs_cpu_to_le16(crc >> 16);
+ return 0;
+}
+
STATIC __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group)
{
__u16 crc = 0;
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index ccf1894..d4a5b10 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -443,4 +443,7 @@ ec EXT2_ET_MMP_CHANGE_ABORT,
ec EXT2_ET_MMP_OPEN_DIRECT,
"MMP: open with O_DIRECT failed"

+ec EXT2_ET_INODE_CSUM_INVALID,
+ "Inode checksum does not match inode"
+
end
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index b0b798b..3ec48a2 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -460,6 +460,10 @@ struct ext2_inode_large {
__u32 i_version_hi; /* high 32 bits for 64-bit version */
};

+#define EXT4_INODE_CSUM_HI_EXTRA_END \
+ (offsetof(struct ext2_inode_large, i_checksum_hi) + sizeof(__u16) - \
+ EXT2_GOOD_OLD_INODE_SIZE)
+
#define i_dir_acl i_size_high

#if defined(__KERNEL__) || defined(__linux__)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index f7a73e5..1728599 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -948,6 +948,10 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+extern errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode);
+extern int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode);
extern void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group);
extern int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group);
extern errcode_t ext2fs_set_gdt_csum(ext2_filsys fs);
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index a4f6670..74703c5 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -417,7 +417,7 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
errcode_t retval;
int extra_bytes = 0;
const int length = EXT2_INODE_SIZE(scan->fs->super);
- struct ext2_inode *iptr = inode;
+ struct ext2_inode_large *iptr = (struct ext2_inode_large *)inode;

EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);

@@ -493,6 +493,12 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->ptr += scan->inode_size - extra_bytes;
scan->bytes_left -= scan->inode_size - extra_bytes;

+ /* Verify the inode checksum. */
+ if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
+ (struct ext2_inode_large *)scan->temp_buffer))
+ retval = EXT2_ET_INODE_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
@@ -506,6 +512,12 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
} else {
+ /* Verify the inode checksum. */
+ if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
+ (struct ext2_inode_large *)scan->ptr))
+ retval = EXT2_ET_INODE_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
@@ -524,7 +536,7 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->inodes_left--;
scan->current_inode++;
*ino = scan->current_inode;
- if (inode != iptr) {
+ if (iptr != (struct ext2_inode_large *)inode) {
memcpy(inode, iptr, bufsize);
ext2fs_free_mem(&iptr);
}
@@ -551,7 +563,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
int clen, i, inodes_per_block;
io_channel io;
int length = EXT2_INODE_SIZE(fs->super);
- struct ext2_inode *iptr;
+ struct ext2_inode_large *iptr;
int cache_slot;

EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -600,7 +612,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size;
- iptr = fs->icache->cache[cache_slot].inode;
+ iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode;

ptr = (char *) iptr;
while (length) {
@@ -626,6 +638,11 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
}
length = EXT2_INODE_SIZE(fs->super);

+ /* Verify the inode checksum. */
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(fs, ino, iptr))
+ return EXT2_ET_INODE_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
(struct ext2_inode_large *) iptr,
@@ -674,12 +691,17 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
retval = ext2fs_get_mem(length, &w_inode);
if (retval)
return retval;
- if (bufsize < length)
+
+ if (bufsize < length) {
+ int old_flags = fs->flags;
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_read_inode_full(fs, ino,
(struct ext2_inode *)w_inode,
length);
- if (retval)
- goto errout;
+ fs->flags = old_flags;
+ if (retval)
+ goto errout;
+ }

/* Check to see if the inode cache needs to be updated */
if (fs->icache) {
@@ -706,6 +728,10 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
#endif

+ retval = ext2fs_inode_csum_set(fs, ino, w_inode);
+ if (retval)
+ goto errout;
+
group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
EXT2_INODE_SIZE(fs->super);
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index b12bf2d..861ddab 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -93,12 +93,14 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
inode.i_size = fs->blocksize;

/*
- * Write out the inode and inode data block
+ * Write out the inode and inode data block. The inode generation
+ * number is assigned by write_new_inode, which means that the call
+ * to write_dir_block must come after that.
*/
- retval = ext2fs_write_dir_block(fs, blk, block);
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
if (retval)
goto cleanup;
- retval = ext2fs_write_new_inode(fs, ino, &inode);
+ retval = ext2fs_write_dir_block(fs, blk, block);
if (retval)
goto cleanup;



2012-03-06 23:58:28

by djwong

[permalink] [raw]
Subject: [PATCH 09/54] mke2fs: Allow metadata checksums to be turned on at mkfs time

Write out reserved inodes with full checksums when writing out a zeroed table.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/mke2fs.c | 29 ++++++++++++++++++++++++++++-
1 files changed, 28 insertions(+), 1 deletions(-)


diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index d80c0b2..28485e3 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -306,6 +306,27 @@ _("Warning: the backup superblock/group descriptors at block %u contain\n"
ext2fs_badblocks_list_iterate_end(bb_iter);
}

+static void write_reserved_inodes(ext2_filsys fs)
+{
+ errcode_t retval;
+ ext2_ino_t ino;
+ struct ext2_inode *inode;
+
+ retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode);
+ if (retval) {
+ com_err("inode_init", retval,
+ "while allocating memory");
+ exit(1);
+ }
+ bzero(inode, EXT2_INODE_SIZE(fs->super));
+
+ for (ino = 1; ino < EXT2_FIRST_INO(fs->super); ino++)
+ ext2fs_write_inode_full(fs, ino, inode,
+ EXT2_INODE_SIZE(fs->super));
+
+ ext2fs_free_mem(&inode);
+}
+
static void write_inode_tables(ext2_filsys fs, int lazy_flag, int itable_zeroed)
{
errcode_t retval;
@@ -351,6 +372,12 @@ static void write_inode_tables(ext2_filsys fs, int lazy_flag, int itable_zeroed)
ext2fs_zero_blocks2(0, 0, 0, 0, 0);
ext2fs_numeric_progress_close(fs, &progress,
_("done \n"));
+
+ /* Reserved inodes must always have correct checksums */
+ if (fs->super->s_creator_os == EXT2_OS_LINUX &&
+ fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ write_reserved_inodes(fs);
}

static void create_root_dir(ext2_filsys fs)
@@ -870,7 +897,7 @@ static __u32 ok_features[3] = {
#ifdef CONFIG_QUOTA
EXT4_FEATURE_RO_COMPAT_QUOTA|
#endif
- 0
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
};




2012-03-06 23:58:30

by djwong

[permalink] [raw]
Subject: [PATCH 08/54] e2fsck: Verify and correct inode checksums

Detect mismatches of the inode and checksum, and prompt the user to fix the
situation.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
e2fsck/problem.c | 10 +++++++++
e2fsck/problem.h | 6 +++++
3 files changed, 75 insertions(+), 1 deletions(-)


diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index c31e073..471e3e0 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -540,6 +540,40 @@ extern void e2fsck_setup_tdb_icount(e2fsck_t ctx, int flags,
*ret = 0;
}

+static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino,
+ e2fsck_t ctx,
+ struct problem_context *pctx)
+{
+ errcode_t retval;
+ struct ext2_inode_large inode;
+
+ /*
+ * Reread inode. If we don't see checksum error, then this inode
+ * has been fixed elsewhere.
+ */
+ retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
+ sizeof(inode));
+ if (retval && retval != EXT2_ET_INODE_CSUM_INVALID)
+ return retval;
+ if (!retval)
+ return 0;
+
+ /*
+ * Checksum still doesn't match. That implies that the inode passes
+ * all the sanity checks, so maybe the checksum is simply corrupt.
+ * See if the user will go for fixing that.
+ */
+ if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx))
+ return 0;
+
+ retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
+ sizeof(inode));
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
void e2fsck_pass1(e2fsck_t ctx)
{
int i;
@@ -561,6 +595,7 @@ void e2fsck_pass1(e2fsck_t ctx)
int imagic_fs, extent_fs;
int busted_fs_time = 0;
int inode_size;
+ int failed_csum = 0;

init_resource_track(&rtrack, ctx->fs->io);
clear_problem_context(&pctx);
@@ -740,7 +775,8 @@ void e2fsck_pass1(e2fsck_t ctx)
ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
continue;
}
- if (pctx.errcode) {
+ if (pctx.errcode &&
+ pctx.errcode != EXT2_ET_INODE_CSUM_INVALID) {
fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
ctx->flags |= E2F_FLAG_ABORT;
return;
@@ -750,6 +786,14 @@ void e2fsck_pass1(e2fsck_t ctx)
pctx.ino = ino;
pctx.inode = inode;
ctx->stashed_ino = ino;
+
+ /* Clear corrupt inode? */
+ if (pctx.errcode == EXT2_ET_INODE_CSUM_INVALID) {
+ if (fix_problem(ctx, PR_1_INODE_CSUM_INVALID, &pctx))
+ goto clear_inode;
+ failed_csum = 1;
+ }
+
if (inode->i_links_count) {
pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
ino, inode->i_links_count);
@@ -1146,6 +1190,20 @@ void e2fsck_pass1(e2fsck_t ctx)
} else
check_blocks(ctx, &pctx, block_buf);

+ /*
+ * If the inode failed the checksum and the user didn't
+ * clear the inode, test the checksum again -- if it still
+ * fails, ask the user if the checksum should be corrected.
+ */
+ if (failed_csum) {
+ pctx.errcode = recheck_bad_inode_checksum(fs, ino, ctx,
+ &pctx);
+ if (pctx.errcode) {
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
+
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;

diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 579c838..a6dd88a 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -929,8 +929,18 @@ static struct e2fsck_problem problem_table[] = {
/* Quota inode is user visible */
{ PR_1_QUOTA_INODE_NOT_HIDDEN,
N_("@q @i is visible to the user. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* inode checksum does not match inode */
+ { PR_1_INODE_CSUM_INVALID,
+ N_("@i %i checksum does not match @i. "),
PROMPT_CLEAR, PR_PREEN_OK },

+ /* inode passes checks, but checksum does not match inode */
+ { PR_1_INODE_ONLY_CSUM_INVALID,
+ N_("@i %i passes checks, but checksum does not match @i. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
/* Invalid bad inode */
{ PR_1_INVALID_BAD_INODE,
N_("The bad @b @i looks @n. "),
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 3fabc90..5797fe2 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -550,6 +550,12 @@ struct problem_context {
/* Invalid bad inode */
#define PR_1_INVALID_BAD_INODE 0x010065

+/* inode checksum does not match inode */
+#define PR_1_INODE_CSUM_INVALID 0x010066
+
+/* inode passes checks, but checksum does not match inode */
+#define PR_1_INODE_ONLY_CSUM_INVALID 0x010067
+
/*
* Pass 1b errors
*/


2012-03-06 23:58:52

by djwong

[permalink] [raw]
Subject: [PATCH 12/54] dumpe2fs: Display inode bitmap checksum

Display the inode bitmap checksum for each block group.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/dumpe2fs.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)


diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 5b114e9..ec4f0e9 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -226,6 +226,10 @@ static void list_desc (ext2_filsys fs)
print_number(ext2fs_inode_bitmap_loc(fs, i));
print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0,
first_block, last_block);
+ if (fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ printf(_(", csum 0x%08x"),
+ ext2fs_inode_bitmap_checksum(fs, i));
fputs(_("\n Inode table at "), stdout);
print_range(ext2fs_inode_table_loc(fs, i),
ext2fs_inode_table_loc(fs, i) +


2012-03-06 23:58:55

by djwong

[permalink] [raw]
Subject: [PATCH 13/54] e2fsck: Verify inode bitmap checksum

Rewrite the block bitmap when the checksum doesn't match. This is ok since
e2fsck will have already computed the correct inode bitmap.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass5.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
e2fsck/problem.c | 5 ++++
e2fsck/problem.h | 3 +++
3 files changed, 72 insertions(+), 0 deletions(-)


diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index 1e836e3..72c4936 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -27,6 +27,7 @@ static void check_block_bitmaps(e2fsck_t ctx);
static void check_inode_bitmaps(e2fsck_t ctx);
static void check_inode_end(e2fsck_t ctx);
static void check_block_end(e2fsck_t ctx);
+static void check_inode_bitmap_checksum(e2fsck_t ctx);

void e2fsck_pass5(e2fsck_t ctx)
{
@@ -64,6 +65,8 @@ void e2fsck_pass5(e2fsck_t ctx)
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;

+ check_inode_bitmap_checksum(ctx);
+
ext2fs_free_inode_bitmap(ctx->inode_used_map);
ctx->inode_used_map = 0;
ext2fs_free_inode_bitmap(ctx->inode_dir_map);
@@ -74,6 +77,67 @@ void e2fsck_pass5(e2fsck_t ctx)
print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
}

+static void check_inode_bitmap_checksum(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ struct ext4_group_desc *gdp;
+ char *buf;
+ dgrp_t i;
+ int nbytes;
+ ext2_ino_t ino_itr;
+ errcode_t retval;
+ int csum_flag = 0;
+
+ /* If bitmap is dirty from being fixed, checksum will be corrected */
+ if (ext2fs_test_ib_dirty(ctx->fs))
+ return;
+
+ nbytes = (size_t)(EXT2_INODES_PER_GROUP(ctx->fs->super) / 8);
+ retval = ext2fs_get_memalign(ctx->fs->blocksize, ctx->fs->blocksize,
+ &buf);
+ if (retval) {
+ com_err(ctx->program_name, 0,
+ _("check_inode_bitmap_checksum: Memory allocation error"));
+ fatal_error(ctx, 0);
+ }
+
+ if (EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ csum_flag = 1;
+
+ clear_problem_context(&pctx);
+ for (i = 0; i < ctx->fs->group_desc_count; i++) {
+ if (csum_flag && ext2fs_bg_flags_test(ctx->fs, i,
+ EXT2_BG_INODE_UNINIT))
+ continue;
+
+ ino_itr = 1 + (i * (nbytes << 3));
+ gdp = (struct ext4_group_desc *)ext2fs_group_desc(ctx->fs,
+ ctx->fs->group_desc, i);
+ retval = ext2fs_get_inode_bitmap_range2(ctx->fs->inode_map,
+ ino_itr, nbytes << 3,
+ buf);
+ if (retval)
+ break;
+
+ if (ext2fs_inode_bitmap_csum_verify(ctx->fs, i, buf, nbytes))
+ continue;
+ pctx.group = i;
+ if (!fix_problem(ctx, PR_5_INODE_BITMAP_CSUM_INVALID, &pctx))
+ continue;
+
+ /*
+ * Fixing one checksum will rewrite all of them. The bitmap
+ * will be checked against the one we made during pass1 for
+ * discrepancies, and fixed if need be.
+ */
+ ext2fs_mark_ib_dirty(ctx->fs);
+ break;
+ }
+
+ ext2fs_free_mem(&buf);
+}
+
static void e2fsck_discard_blocks(e2fsck_t ctx, io_manager manager,
blk64_t start, blk64_t count)
{
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index a6dd88a..2954d73 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1684,6 +1684,11 @@ static struct e2fsck_problem problem_table[] = {
N_("@g %g @i(s) in use but @g is marked INODE_UNINIT\n"),
PROMPT_FIX, PR_PREEN_OK },

+ /* Group N inode bitmap does not match checksum */
+ { PR_5_INODE_BITMAP_CSUM_INVALID,
+ N_("@g %g @i bitmap does not match checksum\n"),
+ PROMPT_FIX, PR_LATCH_IBITMAP | PR_PREEN_OK },
+
/* Post-Pass 5 errors */

/* Recreate journal if E2F_FLAG_JOURNAL_INODE flag is set */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 5797fe2..0adc7ea 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1019,6 +1019,9 @@ struct problem_context {
/* Inode in use but group is marked INODE_UNINIT */
#define PR_5_INODE_UNINIT 0x050019

+/* Inode bitmap checksum does not match */
+#define PR_5_INODE_BITMAP_CSUM_INVALID 0x05001A
+
/*
* Post-Pass 5 errors
*/


2012-03-06 23:58:58

by djwong

[permalink] [raw]
Subject: [PATCH 10/54] libext2fs: Create the inode bitmap checksum

Provide a field in the block group descriptor to store inode bitmap checksum,
and some helper functions to calculate and verify it.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/blknum.c | 15 +++++++++++++++
lib/ext2fs/closefs.c | 29 +++++++++++++++++------------
lib/ext2fs/csum.c | 40 ++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2_fs.h | 4 ++++
lib/ext2fs/ext2fs.h | 6 ++++++
lib/ext2fs/rw_bitmaps.c | 16 ++++++++++++++++
7 files changed, 101 insertions(+), 12 deletions(-)


diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index 33da7d6..212ad48 100644
--- a/lib/ext2fs/blknum.c
+++ b/lib/ext2fs/blknum.c
@@ -230,6 +230,21 @@ void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk)
}

/*
+ * Return the inode bitmap checksum of a group
+ */
+__u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group)
+{
+ struct ext4_group_desc *gdp;
+ __u32 csum;
+
+ gdp = ext4fs_group_desc(fs, fs->group_desc, group);
+ csum = gdp->bg_inode_bitmap_csum_lo;
+ if (fs->super->s_desc_size < EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ csum |= ((__u32)gdp->bg_inode_bitmap_csum_hi << 16);
+ return csum;
+}
+
+/*
* Return the inode bitmap block of a group
*/
blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group)
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 973c2a2..1867be3 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -288,6 +288,23 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)

fs->super->s_wtime = fs->now ? fs->now : time(NULL);
fs->super->s_block_group_nr = 0;
+
+ /*
+ * If the write_bitmaps() function is present, call it to
+ * flush the bitmaps. This is done this way so that a simple
+ * program that doesn't mess with the bitmaps doesn't need to
+ * drag in the bitmaps.c code.
+ *
+ * Bitmap checksums live in the group descriptor, so the
+ * bitmaps need to be written before the descriptors.
+ */
+ if (fs->write_bitmaps) {
+ retval = fs->write_bitmaps(fs);
+ if (retval)
+ goto errout;
+ }
+
+ /* Prepare the group descriptors for writing */
#ifdef WORDS_BIGENDIAN
retval = EXT2_ET_NO_MEMORY;
retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
@@ -378,18 +395,6 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)

ext2fs_numeric_progress_close(fs, &progress, NULL);

- /*
- * If the write_bitmaps() function is present, call it to
- * flush the bitmaps. This is done this way so that a simple
- * program that doesn't mess with the bitmaps doesn't need to
- * drag in the bitmaps.c code.
- */
- if (fs->write_bitmaps) {
- retval = fs->write_bitmaps(fs);
- if (retval)
- goto errout;
- }
-
write_primary_superblock_only:
/*
* Write out master superblock. This has to be done
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 5091a00..90ccd18 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,46 @@
#define STATIC static
#endif

+int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+ __u32 provided, calculated;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+ provided = gdp->bg_inode_bitmap_csum_lo;
+ calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
+ size);
+ if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ provided |= (__u32)gdp->bg_inode_bitmap_csum_hi << 16;
+ else
+ calculated &= 0xFFFF;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ __u32 crc;
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
+ gdp->bg_inode_bitmap_csum_lo = crc & 0xFFFF;
+ if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ gdp->bg_inode_bitmap_csum_hi = crc >> 16;
+
+ return 0;
+}
+
static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_inode_large *inode,
__u32 *crc, int has_hi)
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index d4a5b10..3f5d08f 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -446,4 +446,7 @@ ec EXT2_ET_MMP_OPEN_DIRECT,
ec EXT2_ET_INODE_CSUM_INVALID,
"Inode checksum does not match inode"

+ec EXT2_ET_INODE_BITMAP_CSUM_INVALID,
+ "Inode bitmap checksum does not match bitmap"
+
end
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 3ec48a2..7853966 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -192,6 +192,10 @@ struct ext4_group_desc
__u32 bg_reserved;
};

+#define EXT4_BG_INODE_BITMAP_CSUM_HI_END \
+ (offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
+ sizeof(__u16))
+
#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 1728599..58538fe 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -809,6 +809,7 @@ extern errcode_t ext2fs_get_block_bitmap_range2(ext2fs_block_bitmap bmap,
void *out);

/* blknum.c */
+extern __u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group);
extern dgrp_t ext2fs_group_of_blk2(ext2_filsys fs, blk64_t);
extern blk64_t ext2fs_group_first_block2(ext2_filsys fs, dgrp_t group);
extern blk64_t ext2fs_group_last_block2(ext2_filsys fs, dgrp_t group);
@@ -839,6 +840,7 @@ extern struct ext2_group_desc *ext2fs_group_desc(ext2_filsys fs,
extern blk64_t ext2fs_block_bitmap_loc(ext2_filsys fs, dgrp_t group);
extern void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group,
blk64_t blk);
+extern __u32 ext2fs_inode_bitmap_csum(ext2_filsys fs, dgrp_t group);
extern blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group);
extern void ext2fs_inode_bitmap_loc_set(ext2_filsys fs, dgrp_t group,
blk64_t blk);
@@ -948,6 +950,10 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+extern errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size);
+extern int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size);
extern errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
struct ext2_inode_large *inode);
extern int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
index 1d5f7b2..0ad5eb1 100644
--- a/lib/ext2fs/rw_bitmaps.c
+++ b/lib/ext2fs/rw_bitmaps.c
@@ -117,6 +117,12 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
if (retval)
goto errout;

+ retval = ext2fs_inode_bitmap_csum_set(fs, i, inode_buf,
+ inode_nbytes);
+ if (retval)
+ goto errout;
+ ext2fs_group_desc_csum_set(fs, i);
+
blk = ext2fs_inode_bitmap_loc(fs, i);
if (blk) {
retval = io_channel_write_blk64(fs->io, blk, 1,
@@ -288,6 +294,16 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
retval = EXT2_ET_INODE_BITMAP_READ;
goto cleanup;
}
+
+ /* verify inode bitmap checksum */
+ if (!(fs->flags &
+ EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_bitmap_csum_verify(fs, i,
+ inode_bitmap, inode_nbytes)) {
+ retval =
+ EXT2_ET_INODE_BITMAP_CSUM_INVALID;
+ goto cleanup;
+ }
} else
memset(inode_bitmap, 0, inode_nbytes);
cnt = inode_nbytes << 3;


2012-03-06 23:58:59

by djwong

[permalink] [raw]
Subject: [PATCH 11/54] tune2fs: Rewrite inode bitmap checksums

When toggling metadata_csum, mark the inode bitmap dirty so that they are
written out with new checksums.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)


diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index cb1aff3..478b651 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -427,6 +427,9 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
ext2fs_init_csum_seed(fs);
rewrite_inodes(fs);
+ ext2fs_read_bitmaps(fs);
+ ext2fs_mark_ib_dirty(fs);
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
}



2012-03-06 23:59:04

by djwong

[permalink] [raw]
Subject: [PATCH 14/54] libext2fs: Create the block bitmap checksum

Calculate and verify the block bitmap checksum.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/blknum.c | 17 ++++++++++++++++-
lib/ext2fs/csum.c | 40 ++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_fs.h | 3 +++
lib/ext2fs/ext2fs.h | 6 ++++++
lib/ext2fs/rw_bitmaps.c | 16 ++++++++++++++++
5 files changed, 81 insertions(+), 1 deletions(-)


diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index 212ad48..b3eef6c 100644
--- a/lib/ext2fs/blknum.c
+++ b/lib/ext2fs/blknum.c
@@ -203,6 +203,21 @@ static struct ext4_group_desc *ext4fs_group_desc(ext2_filsys fs,
}

/*
+ * Return the block bitmap checksum of a group
+ */
+__u32 ext2fs_block_bitmap_checksum(ext2_filsys fs, dgrp_t group)
+{
+ struct ext4_group_desc *gdp;
+ __u32 csum;
+
+ gdp = ext4fs_group_desc(fs, fs->group_desc, group);
+ csum = gdp->bg_block_bitmap_csum_lo;
+ if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+ csum |= ((__u32)gdp->bg_block_bitmap_csum_hi << 16);
+ return csum;
+}
+
+/*
* Return the block bitmap block of a group
*/
blk64_t ext2fs_block_bitmap_loc(ext2_filsys fs, dgrp_t group)
@@ -239,7 +254,7 @@ __u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group)

gdp = ext4fs_group_desc(fs, fs->group_desc, group);
csum = gdp->bg_inode_bitmap_csum_lo;
- if (fs->super->s_desc_size < EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
csum |= ((__u32)gdp->bg_inode_bitmap_csum_hi << 16);
return csum;
}
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 90ccd18..5c5b7fb 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -70,6 +70,46 @@ errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
return 0;
}

+int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+ __u32 provided, calculated;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+ provided = gdp->bg_block_bitmap_csum_lo;
+ calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
+ size);
+ if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+ provided |= (__u32)gdp->bg_block_bitmap_csum_hi << 16;
+ else
+ calculated &= 0xFFFF;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ __u32 crc;
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
+ gdp->bg_block_bitmap_csum_lo = crc & 0xFFFF;
+ if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+ gdp->bg_block_bitmap_csum_hi = crc >> 16;
+
+ return 0;
+}
+
static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_inode_large *inode,
__u32 *crc, int has_hi)
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 7853966..89df977 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -195,6 +195,9 @@ struct ext4_group_desc
#define EXT4_BG_INODE_BITMAP_CSUM_HI_END \
(offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
sizeof(__u16))
+#define EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION \
+ (offsetof(struct ext4_group_desc, bg_block_bitmap_csum_hi) + \
+ sizeof(__u16))

#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 58538fe..9d3e8c9 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -810,6 +810,7 @@ extern errcode_t ext2fs_get_block_bitmap_range2(ext2fs_block_bitmap bmap,

/* blknum.c */
extern __u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group);
+extern __u32 ext2fs_block_bitmap_checksum(ext2_filsys fs, dgrp_t group);
extern dgrp_t ext2fs_group_of_blk2(ext2_filsys fs, blk64_t);
extern blk64_t ext2fs_group_first_block2(ext2_filsys fs, dgrp_t group);
extern blk64_t ext2fs_group_last_block2(ext2_filsys fs, dgrp_t group);
@@ -837,6 +838,7 @@ extern void ext2fs_free_blocks_count_add(struct ext2_super_block *super,
extern struct ext2_group_desc *ext2fs_group_desc(ext2_filsys fs,
struct opaque_ext2_group_desc *gdp,
dgrp_t group);
+extern blk64_t ext2fs_block_bitmap_csum(ext2_filsys fs, dgrp_t group);
extern blk64_t ext2fs_block_bitmap_loc(ext2_filsys fs, dgrp_t group);
extern void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group,
blk64_t blk);
@@ -950,6 +952,10 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+extern errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size);
+extern int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size);
extern errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
char *bitmap, int size);
extern int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
index 0ad5eb1..a5097c1 100644
--- a/lib/ext2fs/rw_bitmaps.c
+++ b/lib/ext2fs/rw_bitmaps.c
@@ -92,6 +92,13 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
for (j = nbits; j < fs->blocksize * 8; j++)
ext2fs_set_bit(j, block_buf);
}
+
+ retval = ext2fs_block_bitmap_csum_set(fs, i, block_buf,
+ block_nbytes);
+ if (retval)
+ return retval;
+ ext2fs_group_desc_csum_set(fs, i);
+
blk = ext2fs_block_bitmap_loc(fs, i);
if (blk) {
retval = io_channel_write_blk64(fs->io, blk, 1,
@@ -272,6 +279,15 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
retval = EXT2_ET_BLOCK_BITMAP_READ;
goto cleanup;
}
+ /* verify block bitmap checksum */
+ if (!(fs->flags &
+ EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_block_bitmap_csum_verify(fs, i,
+ block_bitmap, block_nbytes)) {
+ retval =
+ EXT2_ET_BLOCK_BITMAP_READ;
+ goto cleanup;
+ }
} else
memset(block_bitmap, 0, block_nbytes);
cnt = block_nbytes << 3;


2012-03-06 23:59:07

by djwong

[permalink] [raw]
Subject: [PATCH 15/54] dumpe2fs: Display block bitmap checksum

Display the block bitmap checksum when displaying block groups.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/dumpe2fs.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)


diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index ec4f0e9..23dec30 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -222,6 +222,10 @@ static void list_desc (ext2_filsys fs)
print_number(ext2fs_block_bitmap_loc(fs, i));
print_bg_rel_offset(fs, ext2fs_block_bitmap_loc(fs, i), 0,
first_block, last_block);
+ if (fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ printf(_(", csum 0x%08x"),
+ ext2fs_block_bitmap_checksum(fs, i));
fputs(_(", Inode bitmap at "), stdout);
print_number(ext2fs_inode_bitmap_loc(fs, i));
print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0,


2012-03-06 23:59:13

by djwong

[permalink] [raw]
Subject: [PATCH 16/54] e2fsck: Verify block bitmap checksum

Check block bitmap checksum and write a new checksum if the verification fails.
This is ok because e2fsck has already computed the correct block bitmap.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass5.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
e2fsck/problem.c | 5 ++++
e2fsck/problem.h | 3 ++
3 files changed, 73 insertions(+), 0 deletions(-)


diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index 72c4936..f1ce6d7 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -28,6 +28,7 @@ static void check_inode_bitmaps(e2fsck_t ctx);
static void check_inode_end(e2fsck_t ctx);
static void check_block_end(e2fsck_t ctx);
static void check_inode_bitmap_checksum(e2fsck_t ctx);
+static void check_block_bitmap_checksum(e2fsck_t ctx);

void e2fsck_pass5(e2fsck_t ctx)
{
@@ -66,6 +67,7 @@ void e2fsck_pass5(e2fsck_t ctx)
return;

check_inode_bitmap_checksum(ctx);
+ check_block_bitmap_checksum(ctx);

ext2fs_free_inode_bitmap(ctx->inode_used_map);
ctx->inode_used_map = 0;
@@ -138,6 +140,69 @@ static void check_inode_bitmap_checksum(e2fsck_t ctx)
ext2fs_free_mem(&buf);
}

+static void check_block_bitmap_checksum(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ struct ext4_group_desc *gdp;
+ char *buf;
+ dgrp_t i;
+ int nbytes;
+ blk64_t blk_itr;
+ errcode_t retval;
+ int csum_flag = 0;
+
+ /* If bitmap is dirty from being fixed, checksum will be corrected */
+ if (ext2fs_test_bb_dirty(ctx->fs))
+ return;
+
+ nbytes = (size_t)(EXT2_BLOCKS_PER_GROUP(ctx->fs->super) / 8);
+ retval = ext2fs_get_memalign(ctx->fs->blocksize, ctx->fs->blocksize,
+ &buf);
+ if (retval) {
+ com_err(ctx->program_name, 0,
+ _("check_block_bitmap_checksum: Memory allocation error"));
+ fatal_error(ctx, 0);
+ }
+
+ if (EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ csum_flag = 1;
+
+ clear_problem_context(&pctx);
+ for (i = 0; i < ctx->fs->group_desc_count; i++) {
+ if (csum_flag && ext2fs_bg_flags_test(ctx->fs, i,
+ EXT2_BG_BLOCK_UNINIT))
+ continue;
+
+ blk_itr = EXT2FS_B2C(ctx->fs,
+ ctx->fs->super->s_first_data_block) +
+ (i * (nbytes << 3));
+ gdp = (struct ext4_group_desc *)ext2fs_group_desc(ctx->fs,
+ ctx->fs->group_desc, i);
+ retval = ext2fs_get_block_bitmap_range2(ctx->fs->block_map,
+ blk_itr, nbytes << 3,
+ buf);
+ if (retval)
+ break;
+
+ if (ext2fs_block_bitmap_csum_verify(ctx->fs, i, buf, nbytes))
+ continue;
+ pctx.group = i;
+ if (!fix_problem(ctx, PR_5_BLOCK_BITMAP_CSUM_INVALID, &pctx))
+ continue;
+
+ /*
+ * Fixing one checksum will rewrite all of them. The bitmap
+ * will be checked against the one we made during pass1 for
+ * discrepancies, and fixed if need be.
+ */
+ ext2fs_mark_bb_dirty(ctx->fs);
+ break;
+ }
+
+ ext2fs_free_mem(&buf);
+}
+
static void e2fsck_discard_blocks(e2fsck_t ctx, io_manager manager,
blk64_t start, blk64_t count)
{
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 2954d73..7dc4c39 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1689,6 +1689,11 @@ static struct e2fsck_problem problem_table[] = {
N_("@g %g @i bitmap does not match checksum\n"),
PROMPT_FIX, PR_LATCH_IBITMAP | PR_PREEN_OK },

+ /* Group N block bitmap does not match checksum */
+ { PR_5_BLOCK_BITMAP_CSUM_INVALID,
+ N_("@g %g @b bitmap does not match checksum\n"),
+ PROMPT_FIX, PR_LATCH_BBITMAP | PR_PREEN_OK },
+
/* Post-Pass 5 errors */

/* Recreate journal if E2F_FLAG_JOURNAL_INODE flag is set */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 0adc7ea..39e6dba 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1022,6 +1022,9 @@ struct problem_context {
/* Inode bitmap checksum does not match */
#define PR_5_INODE_BITMAP_CSUM_INVALID 0x05001A

+/* Block bitmap checksum does not match */
+#define PR_5_BLOCK_BITMAP_CSUM_INVALID 0x05001B
+
/*
* Post-Pass 5 errors
*/


2012-03-06 23:59:33

by djwong

[permalink] [raw]
Subject: [PATCH 19/54] libext2fs: Verify and calculate extent tree block checksums

Verify and calculate extent tree block checksums when processing filesystems.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 79 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2fs.h | 6 +++
lib/ext2fs/extent.c | 21 ++++++++++++
4 files changed, 109 insertions(+), 0 deletions(-)


diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 5c5b7fb..8a0543c 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,85 @@
#define STATIC static
#endif

+#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
+ (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
+
+static struct ext3_extent_tail *get_extent_tail(struct ext3_extent_header *h)
+{
+ return (struct ext3_extent_tail *)(((void *)h) +
+ EXT3_EXTENT_TAIL_OFFSET(h));
+}
+
+static errcode_t ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh,
+ __u32 *crc)
+{
+ int size;
+ __u32 gen;
+ errcode_t retval;
+ struct ext2_inode inode;
+
+ size = EXT3_EXTENT_TAIL_OFFSET(eh) + offsetof(struct ext3_extent_tail,
+ et_checksum);
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)eh, size);
+
+ return 0;
+}
+
+int ext2fs_extent_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh)
+{
+ errcode_t retval;
+ __u32 provided, calculated;
+ struct ext3_extent_tail *t = get_extent_tail(eh);
+
+ /*
+ * The extent tree structures are accessed in LE order, so we must
+ * swap the checksum bytes here.
+ */
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ provided = ext2fs_le32_to_cpu(t->et_checksum);
+ retval = ext2fs_extent_block_csum(fs, inum, eh, &calculated);
+ if (retval)
+ return 0;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh)
+{
+ errcode_t retval;
+ __u32 crc;
+ struct ext3_extent_tail *t = get_extent_tail(eh);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ /*
+ * The extent tree structures are accessed in LE order, so we must
+ * swap the checksum bytes here.
+ */
+ retval = ext2fs_extent_block_csum(fs, inum, eh, &crc);
+ if (retval)
+ return retval;
+ t->et_checksum = ext2fs_cpu_to_le32(crc);
+ return retval;
+}
+
int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
char *bitmap, int size)
{
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 3f5d08f..9019026 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -449,4 +449,7 @@ ec EXT2_ET_INODE_CSUM_INVALID,
ec EXT2_ET_INODE_BITMAP_CSUM_INVALID,
"Inode bitmap checksum does not match bitmap"

+ec EXT2_ET_EXTENT_CSUM_INVALID,
+ "Extent block checksum does not match extent block"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 9d3e8c9..8dd038a 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -952,6 +952,12 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+extern errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs,
+ ext2_ino_t inum,
+ struct ext3_extent_header *eh);
+extern int ext2fs_extent_block_csum_verify(ext2_filsys fs,
+ ext2_ino_t inum,
+ struct ext3_extent_header *eh);
extern errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
char *bitmap, int size);
extern int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
diff --git a/lib/ext2fs/extent.c b/lib/ext2fs/extent.c
index eb096d6..7d456ef 100644
--- a/lib/ext2fs/extent.c
+++ b/lib/ext2fs/extent.c
@@ -455,6 +455,13 @@ retry:
return retval;
}

+ if (!(handle->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_extent_block_csum_verify(handle->fs, handle->ino,
+ eh)) {
+ handle->level--;
+ return EXT2_ET_EXTENT_CSUM_INVALID;
+ }
+
newpath->left = newpath->entries =
ext2fs_le16_to_cpu(eh->eh_entries);
newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max);
@@ -541,6 +548,7 @@ static errcode_t update_path(ext2_extent_handle_t handle)
blk64_t blk;
errcode_t retval;
struct ext3_extent_idx *ix;
+ struct ext3_extent_header *eh;

if (handle->level == 0) {
retval = ext2fs_write_inode(handle->fs, handle->ino,
@@ -550,6 +558,14 @@ static errcode_t update_path(ext2_extent_handle_t handle)
blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);

+ /* then update the checksum */
+ eh = (struct ext3_extent_header *)
+ handle->path[handle->level].buf;
+ retval = ext2fs_extent_block_csum_set(handle->fs, handle->ino,
+ eh);
+ if (retval)
+ return retval;
+
retval = io_channel_write_blk64(handle->fs->io,
blk, 1, handle->path[handle->level].buf);
}
@@ -958,6 +974,11 @@ static errcode_t extent_node_split(ext2_extent_handle_t handle)

new_node_start = ext2fs_le32_to_cpu(EXT_FIRST_INDEX(neweh)->ei_block);

+ /* then update the checksum */
+ retval = ext2fs_extent_block_csum_set(handle->fs, handle->ino, neweh);
+ if (retval)
+ goto done;
+
/* ...and write the new node block out to disk. */
retval = io_channel_write_blk64(handle->fs->io, new_node_pblk, 1,
block_buf);


2012-03-06 23:59:39

by djwong

[permalink] [raw]
Subject: [PATCH 20/54] tune2fs: Enable extent tree checksums

Add to tune2fs the ability to recalculate extent tree checksums when altering
the metadata checksum feature flag.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 49 insertions(+), 0 deletions(-)


diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index a5ff5ce..784df9a 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -376,6 +376,48 @@ static void request_fsck_afterwards(ext2_filsys fs)
printf(_("(and reboot afterwards!)\n"));
}

+/* Rewrite extents */
+static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ ext2_extent_handle_t handle;
+ struct ext2fs_extent extent;
+ int op = EXT2_EXTENT_ROOT;
+ errcode_t errcode;
+
+ if (!(inode->i_flags & EXT4_EXTENTS_FL))
+ return 0;
+
+ errcode = ext2fs_extent_open(fs, ino, &handle);
+ if (errcode)
+ return errcode;
+
+ while (1) {
+ errcode = ext2fs_extent_get(handle, op, &extent);
+ if (errcode)
+ break;
+
+ /* Root node is in the separately checksummed inode */
+ if (op == EXT2_EXTENT_ROOT) {
+ op = EXT2_EXTENT_NEXT;
+ continue;
+ }
+ op = EXT2_EXTENT_NEXT;
+
+ /* Only visit the first extent in each extent block */
+ if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
+ continue;
+ errcode = ext2fs_extent_replace(handle, 0, &extent);
+ if (errcode)
+ break;
+ }
+
+ /* Ok if we run off the end */
+ if (errcode == EXT2_ET_EXTENT_NO_NEXT)
+ errcode = 0;
+ return errcode;
+}
+
/*
* Forcibly set checksums in all inodes.
*/
@@ -416,6 +458,13 @@ static void rewrite_inodes(ext2_filsys fs)
com_err("set_csum", retval, "while writing inode");
exit(1);
}
+
+ retval = rewrite_extents(fs, ino, inode);
+ if (retval) {
+ com_err("rewrite_extents", retval,
+ "while rewriting extents");
+ exit(1);
+ }
} while (ino);

ext2fs_free_mem(&inode);


2012-03-06 23:59:47

by djwong

[permalink] [raw]
Subject: [PATCH 21/54] e2fsck: Verify extent tree blocks and clear the bad ones

When we encounter an extent tree block that passes the header check but fails
the checksum, offer to clear just that extent block instead of failing the
whole tree, which results in the entire inode being wiped out.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 34 ++++++++++++++++++++++++++++++++--
e2fsck/problem.c | 15 +++++++++++++++
e2fsck/problem.h | 6 ++++++
3 files changed, 53 insertions(+), 2 deletions(-)


diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 471e3e0..332cc22 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1819,6 +1819,7 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
int is_dir, is_leaf;
errcode_t problem;
struct ext2_extent_info info;
+ int failed_csum;

pctx->errcode = ext2fs_extent_get_info(ehandle, &info);
if (pctx->errcode)
@@ -1826,11 +1827,25 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,

pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_FIRST_SIB,
&extent);
- while (!pctx->errcode && info.num_entries-- > 0) {
+ while ((pctx->errcode == 0 ||
+ pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) &&
+ info.num_entries-- > 0) {
+ failed_csum = 0;
is_leaf = extent.e_flags & EXT2_EXTENT_FLAGS_LEAF;
is_dir = LINUX_S_ISDIR(pctx->inode->i_mode);

problem = 0;
+ /* Ask to clear a corrupt extent block */
+ if (pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) {
+ pctx->blk = extent.e_pblk;
+ pctx->blk2 = extent.e_lblk;
+ pctx->num = extent.e_len;
+ problem = PR_1_EXTENT_CSUM_INVALID;
+ if (fix_problem(ctx, problem, pctx))
+ goto fix_problem_now;
+ failed_csum = 1;
+ }
+
if (extent.e_pblk == 0 ||
extent.e_pblk < ctx->fs->super->s_first_data_block ||
extent.e_pblk >= ext2fs_blocks_count(ctx->fs->super))
@@ -1842,12 +1857,24 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
ext2fs_blocks_count(ctx->fs->super))
problem = PR_1_EXTENT_ENDS_BEYOND;

+ /* Corrupt but passes checks? Ask to fix checksum. */
+ if (failed_csum) {
+ pctx->blk = extent.e_pblk;
+ pctx->blk2 = extent.e_lblk;
+ pctx->num = extent.e_len;
+ problem = 0;
+ if (fix_problem(ctx, PR_1_EXTENT_ONLY_CSUM_INVALID,
+ pctx))
+ ext2fs_extent_replace(ehandle, 0, &extent);
+ }
+
if (problem) {
report_problem:
pctx->blk = extent.e_pblk;
pctx->blk2 = extent.e_lblk;
pctx->num = extent.e_len;
if (fix_problem(ctx, problem, pctx)) {
+fix_problem_now:
e2fsck_read_bitmaps(ctx);
pctx->errcode =
ext2fs_extent_delete(ehandle, 0);
@@ -1874,7 +1901,10 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
if (pctx->errcode) {
pctx->str = "EXT2_EXTENT_DOWN";
problem = PR_1_EXTENT_HEADER_INVALID;
- if (pctx->errcode == EXT2_ET_EXTENT_HEADER_BAD)
+ if (pctx->errcode ==
+ EXT2_ET_EXTENT_HEADER_BAD ||
+ pctx->errcode ==
+ EXT2_ET_EXTENT_CSUM_INVALID)
goto report_problem;
return;
}
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 7dc4c39..6ceee11 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -946,6 +946,21 @@ static struct e2fsck_problem problem_table[] = {
N_("The bad @b @i looks @n. "),
PROMPT_CLEAR, 0 },

+ /* Inode extent block checksum does not match extent */
+ { PR_1_EXTENT_CSUM_INVALID,
+ N_("@i %i extent block checksum does not match extent\n\t(logical @b "
+ "%c, @n physical @b %b, len %N)\n"),
+ PROMPT_CLEAR, 0 },
+
+ /*
+ * Inode extent block passes checks, but checksum does not match
+ * extent
+ */
+ { PR_1_EXTENT_ONLY_CSUM_INVALID,
+ N_("@i %i extent block passes checks, but checksum does not match "
+ "extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"),
+ PROMPT_FIX, 0 },
+
/* Pass 1b errors */

/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 39e6dba..2adb4b9 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -556,6 +556,12 @@ struct problem_context {
/* inode passes checks, but checksum does not match inode */
#define PR_1_INODE_ONLY_CSUM_INVALID 0x010067

+/* extent block checksum does not match extent block */
+#define PR_1_EXTENT_CSUM_INVALID 0x010068
+
+/* extent block passes checks, but checksum does not match extent block */
+#define PR_1_EXTENT_ONLY_CSUM_INVALID 0x010069
+
/*
* Pass 1b errors
*/


2012-03-06 23:59:55

by djwong

[permalink] [raw]
Subject: [PATCH 17/54] e2fsck: Don't verify bitmap checksums

Since the correct inode and block bitmaps are calculated in pass 5, don't fail
the bitmap read operation in prior passes since (a) incorrect results won't
kill us and (b) if we fail early, we'll never _get_ to pass 5.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/util.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)


diff --git a/e2fsck/util.c b/e2fsck/util.c
index 6e3a1dc..af483fa 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -249,7 +249,9 @@ void e2fsck_read_bitmaps(e2fsck_t ctx)
old_op = ehandler_operation(_("reading inode and block bitmaps"));
e2fsck_set_bitmap_type(fs, EXT2FS_BMAP64_RBTREE, "fs_bitmaps",
&save_type);
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_read_bitmaps(fs);
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
fs->default_bitmap_type = save_type;
ehandler_operation(old_op);
if (retval) {


2012-03-06 23:59:56

by djwong

[permalink] [raw]
Subject: [PATCH 18/54] tune2fs: Rewrite block bitmap checksums

When toggling metadata_csum, mark the block bitmap dirty so that it gets
written with new checksums.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)


diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 478b651..a5ff5ce 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -429,6 +429,7 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
rewrite_inodes(fs);
ext2fs_read_bitmaps(fs);
ext2fs_mark_ib_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
}


2012-03-07 00:00:31

by djwong

[permalink] [raw]
Subject: [PATCH 27/54] tune2fs: Rebuild and checksum directories when toggling metadata_csum or changing UUID

Since all the metadata checksums depend on the fs UUID, tune2fs must be able to
rewrite the checksums of _all_ metadata. It's not that hard to add in the bits
to resize the directory block structures at the same time.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 194 insertions(+), 0 deletions(-)


diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 784df9a..801e6ea 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -106,6 +106,8 @@ struct blk_move {


static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
+static const char *please_dir_fsck =
+ N_("Please run e2fsck -D on the filesystem.\n");

#ifdef CONFIG_BUILD_FINDFS
void do_findfs(int argc, char **argv);
@@ -364,6 +366,18 @@ static int check_fsck_needed(ext2_filsys fs)
return 1;
}

+static void request_dir_fsck_afterwards(ext2_filsys fs)
+{
+ static int requested;
+
+ if (requested++)
+ return;
+ fs->super->s_state &= ~EXT2_VALID_FS;
+ printf("\n%s\n", _(please_dir_fsck));
+ if (mount_flags & EXT2_MF_READONLY)
+ printf(_("(and reboot afterwards!)\n"));
+}
+
static void request_fsck_afterwards(ext2_filsys fs)
{
static int requested = 0;
@@ -419,6 +433,177 @@ static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino,
}

/*
+ * Rewrite directory blocks with checksums
+ */
+struct rewrite_dir_context {
+ char *buf;
+ errcode_t errcode;
+ ext2_ino_t dir;
+ int is_htree;
+};
+
+static int rewrite_dir_block(ext2_filsys fs,
+ blk64_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk64_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct ext2_dx_countlimit *dcl = NULL;
+ struct rewrite_dir_context *ctx = priv_data;
+ int dcl_offset, changed = 0;
+
+ ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
+ ctx->dir);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+
+ /* if htree node... */
+ if (ctx->is_htree)
+ ext2fs_get_dx_countlimit(fs, (struct ext2_dir_entry *)ctx->buf,
+ &dcl, &dcl_offset);
+ if (dcl) {
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ /* Ensure limit is the max size */
+ int max_entries = (fs->blocksize - dcl_offset) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(dcl->limit) != max_entries) {
+ changed = 1;
+ dcl->limit = ext2fs_cpu_to_le16(max_entries);
+ }
+ } else {
+ /* If htree block is full then rebuild the dir */
+ if (ext2fs_le16_to_cpu(dcl->count) ==
+ ext2fs_le16_to_cpu(dcl->limit)) {
+ request_dir_fsck_afterwards(fs);
+ return 0;
+ }
+ /*
+ * Ensure dcl->limit is small enough to leave room for
+ * the checksum tail.
+ */
+ int max_entries = (fs->blocksize - (dcl_offset +
+ sizeof(struct ext2_dx_tail))) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(dcl->limit) != max_entries)
+ dcl->limit = ext2fs_cpu_to_le16(max_entries);
+ /* Always rewrite checksum */
+ changed = 1;
+ }
+ } else {
+ unsigned int rec_len, name_size;
+ char *top = ctx->buf + fs->blocksize;
+ struct ext2_dir_entry *de = (struct ext2_dir_entry *)ctx->buf;
+ struct ext2_dir_entry *last_de = NULL, *penultimate_de = NULL;
+
+ /* Find last and penultimate dirent */
+ while ((char *)de < top) {
+ penultimate_de = last_de;
+ last_de = de;
+ ctx->errcode = ext2fs_get_rec_len(fs, de, &rec_len);
+ if (!ctx->errcode && !rec_len)
+ ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ de = (struct ext2_dir_entry *)(((void *)de) + rec_len);
+ }
+ ctx->errcode = ext2fs_get_rec_len(fs, last_de, &rec_len);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ name_size = last_de->name_len & 0xFF;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (!penultimate_de)
+ return 0;
+ if (last_de->inode ||
+ name_size ||
+ rec_len != sizeof(struct ext2_dir_entry_tail))
+ return 0;
+ /*
+ * The last dirent is unused and the right length to
+ * have stored a checksum. Erase it.
+ */
+ ctx->errcode = ext2fs_get_rec_len(fs, penultimate_de,
+ &rec_len);
+ if (!rec_len)
+ ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ ext2fs_set_rec_len(fs, rec_len +
+ sizeof(struct ext2_dir_entry_tail),
+ penultimate_de);
+ changed = 1;
+ } else {
+ int csum_size = sizeof(struct ext2_dir_entry_tail);
+ struct ext2_dir_entry_tail *t;
+
+ /*
+ * If the last dirent looks like the tail, just update
+ * the checksum.
+ */
+ if (!last_de->inode &&
+ rec_len == csum_size) {
+ t = (struct ext2_dir_entry_tail *)last_de;
+ t->det_reserved_name_len =
+ EXT2_DIR_NAME_LEN_CSUM;
+ changed = 1;
+ goto out;
+ }
+ if (name_size & 3)
+ name_size = (name_size & ~3) + 4;
+ /* If there's not enough space for the tail, e2fsck */
+ if (rec_len <= (8 + name_size + csum_size)) {
+ request_dir_fsck_afterwards(fs);
+ return 0;
+ }
+ /* Shorten that last de and insert the tail */
+ ext2fs_set_rec_len(fs, rec_len - csum_size, last_de);
+ t = EXT2_DIRENT_TAIL(ctx->buf, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+
+ /* Always update checksum */
+ changed = 1;
+ }
+ }
+
+out:
+ if (!changed)
+ return 0;
+
+ ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
+ 0, ctx->dir);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+
+ return 0;
+}
+
+errcode_t rewrite_directory(ext2_filsys fs, ext2_ino_t dir,
+ struct ext2_inode *inode)
+{
+ errcode_t retval;
+ struct rewrite_dir_context ctx;
+
+ retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
+ if (retval)
+ return retval;
+
+ ctx.is_htree = (inode->i_flags & EXT2_INDEX_FL);
+ ctx.dir = dir;
+ retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY |
+ BLOCK_FLAG_DATA_ONLY,
+ 0, rewrite_dir_block, &ctx);
+
+ ext2fs_free_mem(&ctx.buf);
+ if (retval)
+ return retval;
+
+ return ctx.errcode;
+}
+
+/*
* Forcibly set checksums in all inodes.
*/
static void rewrite_inodes(ext2_filsys fs)
@@ -465,6 +650,15 @@ static void rewrite_inodes(ext2_filsys fs)
"while rewriting extents");
exit(1);
}
+
+ if (LINUX_S_ISDIR(inode->i_mode)) {
+ retval = rewrite_directory(fs, ino, inode);
+ if (retval) {
+ com_err("rewrite_directory", retval,
+ "while rewriting directories");
+ exit(1);
+ }
+ }
} while (ino);

ext2fs_free_mem(&inode);


2012-03-07 00:00:35

by djwong

[permalink] [raw]
Subject: [PATCH 22/54] debugfs: Print htree internal node checksums

Print htree node checksums when dumping a directory index.

Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/htree.c | 17 ++++++++++++++++-
1 files changed, 16 insertions(+), 1 deletions(-)


diff --git a/debugfs/htree.c b/debugfs/htree.c
index 05745eb..2fb804e 100644
--- a/debugfs/htree.c
+++ b/debugfs/htree.c
@@ -120,8 +120,9 @@ static void htree_dump_int_node(ext2_filsys fs, ext2_ino_t ino,
{
struct ext2_dx_countlimit limit;
struct ext2_dx_entry e;
+ struct ext2_dx_tail *tail;
int hash, i;
-
+ int remainder;

limit = *((struct ext2_dx_countlimit *) ent);
limit.count = ext2fs_le16_to_cpu(limit.count);
@@ -130,6 +131,20 @@ static void htree_dump_int_node(ext2_filsys fs, ext2_ino_t ino,
fprintf(pager, "Number of entries (count): %d\n", limit.count);
fprintf(pager, "Number of entries (limit): %d\n", limit.limit);

+ remainder = fs->blocksize - (limit.limit *
+ sizeof(struct ext2_dx_entry));
+ if (ent == (struct ext2_dx_entry *)(rootnode + 1))
+ remainder -= sizeof(struct ext2_dx_root_info) + 24;
+ else
+ remainder -= 8;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ remainder == sizeof(struct ext2_dx_tail)) {
+ tail = (struct ext2_dx_tail *)(ent + limit.limit);
+ fprintf(pager, "Checksum: 0x%08x\n",
+ ext2fs_le32_to_cpu(tail->dt_checksum));
+ }
+
for (i=0; i < limit.count; i++) {
hash = i ? ext2fs_le32_to_cpu(ent[i].hash) : 0;
fprintf(pager, "Entry #%d: Hash 0x%08x%s, block %u\n", i,


2012-03-07 00:00:57

by djwong

[permalink] [raw]
Subject: [PATCH 23/54] libext2fs: Add dx_root/dx_node checksum calculation and verification helpers

Verify and calculate checksums of htree internal node blocks.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 149 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +
lib/ext2fs/ext2fs.h | 4 +
3 files changed, 156 insertions(+), 0 deletions(-)


diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 8a0543c..8907459 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,155 @@
#define STATIC static
#endif

+static __u16 do_nothing16(__u16 x)
+{
+ return x;
+}
+
+static __u16 disk_to_host16(__u16 x)
+{
+ return ext2fs_le16_to_cpu(x);
+}
+
+static errcode_t __get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dx_countlimit **cc,
+ int *offset,
+ int need_swab)
+{
+ struct ext2_dir_entry *dp;
+ struct ext2_dx_root_info *root;
+ struct ext2_dx_countlimit *c;
+ int count_offset, max_sane_entries;
+ unsigned int rec_len;
+ __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+ rec_len = translate(dirent->rec_len);
+
+ if (rec_len == fs->blocksize && translate(dirent->name_len) == 0)
+ count_offset = 8;
+ else if (rec_len == 12) {
+ dp = (struct ext2_dir_entry *)(((void *)dirent) + rec_len);
+ rec_len = translate(dp->rec_len);
+ if (rec_len != fs->blocksize - 12)
+ return EXT2_ET_DB_NOT_FOUND;
+ root = (struct ext2_dx_root_info *)(((void *)dp + 12));
+ if (root->reserved_zero ||
+ root->info_length != sizeof(struct ext2_dx_root_info))
+ return EXT2_ET_DB_NOT_FOUND;
+ count_offset = 32;
+ } else
+ return EXT2_ET_DB_NOT_FOUND;
+
+ c = (struct ext2_dx_countlimit *)(((void *)dirent) + count_offset);
+ max_sane_entries = (fs->blocksize - count_offset) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(c->limit) > max_sane_entries ||
+ ext2fs_le16_to_cpu(c->count) > max_sane_entries)
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+ if (offset)
+ *offset = count_offset;
+ if (cc)
+ *cc = c;
+
+ return 0;
+}
+
+errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dx_countlimit **cc,
+ int *offset)
+{
+ return __get_dx_countlimit(fs, dirent, cc, offset, 0);
+}
+
+static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent,
+ __u32 *crc, int count_offset, int count,
+ struct ext2_dx_tail *t)
+{
+ errcode_t retval;
+ char *buf = (char *)dirent;
+ int size;
+ __u32 old_csum, gen;
+ struct ext2_inode inode;
+
+ size = count_offset + (count * sizeof(struct ext2_dx_entry));
+ old_csum = t->dt_checksum;
+ t->dt_checksum = 0;
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size);
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)t,
+ sizeof(struct ext2_dx_tail));
+ t->dt_checksum = old_csum;
+
+ return 0;
+}
+
+static int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ __u32 calculated;
+ errcode_t retval;
+ struct ext2_dx_countlimit *c;
+ struct ext2_dx_tail *t;
+ int count_offset, limit, count;
+
+ retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1);
+ if (retval)
+ return 1;
+ limit = ext2fs_le16_to_cpu(c->limit);
+ count = ext2fs_le16_to_cpu(c->count);
+ if (count_offset + (limit * sizeof(struct ext2_dx_entry)) >
+ fs->blocksize - sizeof(struct ext2_dx_tail))
+ return 0;
+ /* htree structs are accessed in LE order */
+ t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit);
+ retval = ext2fs_dx_csum(fs, inum, dirent, &calculated, count_offset,
+ count, t);
+ if (retval)
+ return 0;
+
+ return ext2fs_le32_to_cpu(t->dt_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ __u32 crc;
+ errcode_t retval = 0;
+ struct ext2_dx_countlimit *c;
+ struct ext2_dx_tail *t;
+ int count_offset, limit, count;
+
+ retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1);
+ if (retval)
+ return retval;
+ limit = ext2fs_le16_to_cpu(c->limit);
+ count = ext2fs_le16_to_cpu(c->count);
+ if (count_offset + (limit * sizeof(struct ext2_dx_entry)) >
+ fs->blocksize - sizeof(struct ext2_dx_tail))
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+ t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit);
+
+ /* htree structs are accessed in LE order */
+ retval = ext2fs_dx_csum(fs, inum, dirent, &crc, count_offset, count, t);
+ if (retval)
+ return retval;
+ t->dt_checksum = ext2fs_cpu_to_le32(crc);
+ return retval;
+}
+
#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
(sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))

diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 9019026..1e51384 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -452,4 +452,7 @@ ec EXT2_ET_INODE_BITMAP_CSUM_INVALID,
ec EXT2_ET_EXTENT_CSUM_INVALID,
"Extent block checksum does not match extent block"

+ec EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
+ "Directory block does not have space for checksum"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 8dd038a..e224e74 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -952,6 +952,10 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+extern errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dx_countlimit **cc,
+ int *offset);
extern errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs,
ext2_ino_t inum,
struct ext3_extent_header *eh);


2012-03-07 00:01:04

by djwong

[permalink] [raw]
Subject: [PATCH 25/54] libext2fs: Introduce dir_entry_tail to provide checksums for directory leaf nodes

Introduce small structures for recording directory tree checksums, and some API
changes to support writing out directory blocks with checksums.

Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/htree.c | 9 ++-
e2fsck/pass1.c | 2 -
e2fsck/pass2.c | 14 +++-
e2fsck/pass3.c | 10 ++-
e2fsck/rehash.c | 37 +++++++++--
lib/ext2fs/csum.c | 148 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/dir_iterate.c | 14 +++-
lib/ext2fs/dirblock.c | 98 +++++++++++++-----------------
lib/ext2fs/expanddir.c | 5 +-
lib/ext2fs/ext2_err.et.in | 3 +
lib/ext2fs/ext2fs.h | 18 +++++
lib/ext2fs/link.c | 6 ++
lib/ext2fs/mkdir.c | 2 -
lib/ext2fs/newdir.c | 15 ++++-
lib/ext2fs/swapfs.c | 62 +++++++++++++++++++
15 files changed, 361 insertions(+), 82 deletions(-)


diff --git a/debugfs/htree.c b/debugfs/htree.c
index 2fb804e..1932962 100644
--- a/debugfs/htree.c
+++ b/debugfs/htree.c
@@ -44,6 +44,11 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
ext2_dirhash_t hash, minor_hash;
unsigned int rec_len;
int hash_alg;
+ int csum_size = 0;
+
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);

errcode = ext2fs_bmap2(fs, ino, inode, buf, 0, blk, 0, &pblk);
if (errcode) {
@@ -53,7 +58,7 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
}

printf("Reading directory block %llu, phys %llu\n", blk, pblk);
- errcode = ext2fs_read_dir_block2(current_fs, pblk, buf, 0);
+ errcode = ext2fs_read_dir_block4(current_fs, pblk, buf, 0, ino);
if (errcode) {
com_err("htree_dump_leaf_node", errcode,
"while reading block %llu (%llu)\n",
@@ -65,7 +70,7 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
(fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
hash_alg += 3;

- while (offset < fs->blocksize) {
+ while (offset < (fs->blocksize - csum_size)) {
dirent = (struct ext2_dir_entry *) (buf + offset);
errcode = ext2fs_get_rec_len(fs, dirent, &rec_len);
if (errcode) {
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 332cc22..42fca3e 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -473,7 +473,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,

/* read the first block */
ehandler_operation(_("reading directory block"));
- retval = ext2fs_read_dir_block3(ctx->fs, blk, buf, 0);
+ retval = ext2fs_read_dir_block4(ctx->fs, blk, buf, 0, pctx->ino);
ehandler_operation(0);
if (retval)
return;
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index e4a06f3..5de301f 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -754,6 +754,7 @@ static int check_dir_block(ext2_filsys fs,
int dups_found = 0;
int ret;
int dx_csum_size = 0;
+ int failed_csum = 0;

cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -804,10 +805,14 @@ static int check_dir_block(ext2_filsys fs,
#endif

ehandler_operation(_("reading directory block"));
- cd->pctx.errcode = ext2fs_read_dir_block3(fs, block_nr, buf, 0);
+ cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino);
ehandler_operation(0);
if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
cd->pctx.errcode = 0; /* We'll handle this ourselves */
+ else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) {
+ cd->pctx.errcode = 0; /* We'll handle this ourselves */
+ failed_csum = 1;
+ }
if (cd->pctx.errcode) {
if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
ctx->flags |= E2F_FLAG_ABORT;
@@ -1145,7 +1150,7 @@ out_htree:
cd->pctx.dir = cd->pctx.ino;
if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
(dx_db->type == DX_DIRBLOCK_NODE))
- parse_int_node(fs, db, cd, dx_dir, buf, 0);
+ parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
}
#endif /* ENABLE_HTREE */
if (offset != fs->blocksize) {
@@ -1156,7 +1161,8 @@ out_htree:
}
}
if (dir_modified) {
- cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
+ cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
+ 0, ino);
if (cd->pctx.errcode) {
if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
&cd->pctx))
@@ -1476,7 +1482,7 @@ static int allocate_dir_block(e2fsck_t ctx,
return 1;
}

- pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
+ pctx->errcode = ext2fs_write_dir_block4(fs, blk, block, 0, db->ino);
ext2fs_free_mem(&block);
if (pctx->errcode) {
pctx->str = "ext2fs_write_dir_block";
diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 565b8e3..4ae3a33 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -198,7 +198,8 @@ static void check_root(e2fsck_t ctx)
return;
}

- pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
+ pctx.errcode = ext2fs_write_dir_block4(fs, blk, block, 0,
+ EXT2_ROOT_INO);
if (pctx.errcode) {
pctx.str = "ext2fs_write_dir_block";
fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
@@ -444,7 +445,7 @@ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
return 0;
}

- retval = ext2fs_write_dir_block(fs, blk, block);
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
ext2fs_free_mem(&block);
if (retval) {
pctx.errcode = retval;
@@ -685,6 +686,7 @@ struct expand_dir_struct {
blk64_t last_block;
errcode_t err;
e2fsck_t ctx;
+ ext2_ino_t dir;
};

static int expand_dir_proc(ext2_filsys fs,
@@ -725,7 +727,8 @@ static int expand_dir_proc(ext2_filsys fs,
return BLOCK_ABORT;
}
es->num--;
- retval = ext2fs_write_dir_block(fs, new_blk, block);
+ retval = ext2fs_write_dir_block4(fs, new_blk, block, 0,
+ es->dir);
} else {
retval = ext2fs_get_mem(fs->blocksize, &block);
if (retval) {
@@ -778,6 +781,7 @@ errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
es.err = 0;
es.newblocks = 0;
es.ctx = ctx;
+ es.dir = dir;

retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
0, expand_dir_proc, &es);
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 610b3bf..a77ef03 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -81,6 +81,7 @@ struct fill_dir_struct {
int dir_size;
int compress;
ino_t parent;
+ ext2_ino_t dir;
};

struct hash_entry {
@@ -125,7 +126,10 @@ static int fill_dir_block(ext2_filsys fs,
dirent = (struct ext2_dir_entry *) dir;
(void) ext2fs_set_rec_len(fs, fs->blocksize, dirent);
} else {
- fd->err = ext2fs_read_dir_block3(fs, *block_nr, dir, 0);
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0,
+ fd->dir);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (fd->err)
return BLOCK_ABORT;
}
@@ -416,7 +420,8 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,

static errcode_t copy_dir_entries(e2fsck_t ctx,
struct fill_dir_struct *fd,
- struct out_dir *outdir)
+ struct out_dir *outdir,
+ ext2_ino_t ino)
{
ext2_filsys fs = ctx->fs;
errcode_t retval;
@@ -427,6 +432,8 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
int i, left;
ext2_dirhash_t prev_hash;
int offset, slack;
+ int csum_size = 0;
+ struct ext2_dir_entry_tail *t;

if (ctx->htree_slack_percentage == 255) {
profile_get_uint(ctx->profile, "options",
@@ -437,6 +444,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
ctx->htree_slack_percentage = 20;
}

+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
outdir->max = 0;
retval = alloc_size_dir(fs, outdir,
(fd->dir_size / fs->blocksize) + 2);
@@ -451,9 +462,9 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
dirent = (struct ext2_dir_entry *) block_start;
prev_rec_len = 0;
rec_len = 0;
- left = fs->blocksize;
+ left = fs->blocksize - csum_size;
slack = fd->compress ? 12 :
- (fs->blocksize * ctx->htree_slack_percentage)/100;
+ ((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100;
if (slack < 12)
slack = 12;
for (i = 0; i < fd->num_array; i++) {
@@ -468,12 +479,17 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
if (retval)
return retval;
}
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(block_start,
+ fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
if ((retval = get_next_block(fs, outdir,
&block_start)))
return retval;
offset = 0;
}
- left = fs->blocksize - offset;
+ left = (fs->blocksize - csum_size) - offset;
dirent = (struct ext2_dir_entry *) (block_start + offset);
if (offset == 0) {
if (ent->hash == prev_hash)
@@ -502,6 +518,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
}
if (left)
retval = ext2fs_set_rec_len(fs, rec_len + left, dirent);
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(block_start, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }

return retval;
}
@@ -659,6 +679,7 @@ struct write_dir_struct {
errcode_t err;
e2fsck_t ctx;
int cleared;
+ ext2_ino_t dir;
};

/*
@@ -690,7 +711,7 @@ static int write_dir_block(ext2_filsys fs,
return 0;

dir = wd->outdir->buf + (blockcnt * fs->blocksize);
- wd->err = ext2fs_write_dir_block3(fs, *block_nr, dir, 0);
+ wd->err = ext2fs_write_dir_block4(fs, *block_nr, dir, 0, wd->dir);
if (wd->err)
return BLOCK_ABORT;
return 0;
@@ -712,6 +733,7 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
wd.err = 0;
wd.ctx = ctx;
wd.cleared = 0;
+ wd.dir = ino;

retval = ext2fs_block_iterate3(fs, ino, 0, 0,
write_dir_block, &wd);
@@ -764,6 +786,7 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
fd.err = 0;
fd.dir_size = 0;
fd.compress = 0;
+ fd.dir = ino;
if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
(inode.i_size / fs->blocksize) < 2)
fd.compress = 1;
@@ -823,7 +846,7 @@ resort:
* Copy the directory entries. In a htree directory these
* will become the leaf nodes.
*/
- retval = copy_dir_entries(ctx, &fd, &outdir);
+ retval = copy_dir_entries(ctx, &fd, &outdir, ino);
if (retval)
goto errout;

diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 8907459..1c06f5e 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -93,6 +93,122 @@ errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
return __get_dx_countlimit(fs, dirent, cc, offset, 0);
}

+void ext2fs_initialize_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry_tail *t)
+{
+ memset(t, 0, sizeof(struct ext2_dir_entry_tail));
+ ext2fs_set_rec_len(fs, sizeof(struct ext2_dir_entry_tail),
+ (struct ext2_dir_entry *)t);
+ t->det_reserved_name_len = EXT2_DIR_NAME_LEN_CSUM;
+}
+
+static errcode_t __get_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dir_entry_tail **tt,
+ int need_swab)
+{
+ struct ext2_dir_entry *d;
+ void *top;
+ struct ext2_dir_entry_tail *t;
+ unsigned int rec_len;
+ errcode_t retval = 0;
+ __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+ d = dirent;
+ top = EXT2_DIRENT_TAIL(dirent, fs->blocksize);
+
+ rec_len = translate(d->rec_len);
+ while (rec_len && !(rec_len & 0x3)) {
+ d = (struct ext2_dir_entry *)(((void *)d) + rec_len);
+ if ((void *)d >= top)
+ break;
+ rec_len = translate(d->rec_len);
+ }
+
+ if (d != top)
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+ t = (struct ext2_dir_entry_tail *)d;
+ if (t->det_reserved_zero1 ||
+ translate(t->det_rec_len) != sizeof(struct ext2_dir_entry_tail) ||
+ translate(t->det_reserved_name_len) != EXT2_DIR_NAME_LEN_CSUM)
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+ if (tt)
+ *tt = t;
+ return retval;
+}
+
+int ext2fs_dirent_has_tail(ext2_filsys fs, struct ext2_dir_entry *dirent)
+{
+ return __get_dirent_tail(fs, dirent, NULL, 0) == 0;
+}
+
+static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent, __u32 *crc,
+ int size)
+{
+ errcode_t retval;
+ char *buf = (char *)dirent;
+ __u32 gen;
+ struct ext2_inode inode;
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size);
+
+ return 0;
+}
+
+int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ errcode_t retval;
+ __u32 calculated;
+ struct ext2_dir_entry_tail *t;
+
+ retval = __get_dirent_tail(fs, dirent, &t, 1);
+ if (retval)
+ return 1;
+
+ /*
+ * The checksum field is overlaid with the dirent->name field
+ * so the swapfs.c functions won't change the endianness.
+ */
+ retval = ext2fs_dirent_csum(fs, inum, dirent, &calculated,
+ (void *)t - (void *)dirent);
+ if (retval)
+ return 0;
+ return ext2fs_le32_to_cpu(t->det_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dirent_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ errcode_t retval;
+ __u32 crc;
+ struct ext2_dir_entry_tail *t;
+
+ retval = __get_dirent_tail(fs, dirent, &t, 1);
+ if (retval)
+ return retval;
+
+ /* swapfs.c functions don't change the checksum endianness */
+ retval = ext2fs_dirent_csum(fs, inum, dirent, &crc,
+ (void *)t - (void *)dirent);
+ if (retval)
+ return retval;
+ t->det_checksum = ext2fs_cpu_to_le32(crc);
+ return 0;
+}
+
static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_dir_entry *dirent,
__u32 *crc, int count_offset, int count,
@@ -179,6 +295,38 @@ static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
return retval;
}

+int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+ return ext2fs_dirent_csum_verify(fs, inum, dirent);
+ if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+ return ext2fs_dx_csum_verify(fs, inum, dirent);
+
+ return 0;
+}
+
+errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+ return ext2fs_dirent_csum_set(fs, inum, dirent);
+ if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+ return ext2fs_dx_csum_set(fs, inum, dirent);
+
+ if (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)
+ return 0;
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+}
+
#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
(sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))

diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
index 5125d19..c24015c 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -192,17 +192,23 @@ int ext2fs_process_dir_block(ext2_filsys fs,
unsigned int rec_len, size;
int entry;
struct ext2_dir_entry *dirent;
+ int csum_size = 0;

if (blockcnt < 0)
return 0;

entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;

- ctx->errcode = ext2fs_read_dir_block3(fs, *blocknr, ctx->buf, 0);
+ ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
+ ctx->dir);
if (ctx->errcode)
return BLOCK_ABORT;

- while (offset < fs->blocksize) {
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ while (offset < (fs->blocksize - csum_size)) {
dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
if (ext2fs_get_rec_len(fs, dirent, &rec_len))
return BLOCK_ABORT;
@@ -259,8 +265,8 @@ next:
}

if (changed) {
- ctx->errcode = ext2fs_write_dir_block3(fs, *blocknr, ctx->buf,
- 0);
+ ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
+ 0, ctx->dir);
if (ctx->errcode)
return BLOCK_ABORT;
}
diff --git a/lib/ext2fs/dirblock.c b/lib/ext2fs/dirblock.c
index cb3a104..54b2777 100644
--- a/lib/ext2fs/dirblock.c
+++ b/lib/ext2fs/dirblock.c
@@ -20,45 +20,36 @@
#include "ext2_fs.h"
#include "ext2fs.h"

-errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
- void *buf, int flags EXT2FS_ATTR((unused)))
+errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block,
+ void *buf, int flags EXT2FS_ATTR((unused)),
+ ext2_ino_t ino)
{
errcode_t retval;
- char *p, *end;
- struct ext2_dir_entry *dirent;
- unsigned int name_len, rec_len;
-
+ int corrupt = 0;

retval = io_channel_read_blk64(fs->io, block, 1, buf);
if (retval)
return retval;

- p = (char *) buf;
- end = (char *) buf + fs->blocksize;
- while (p < end-8) {
- dirent = (struct ext2_dir_entry *) p;
-#ifdef WORDS_BIGENDIAN
- dirent->inode = ext2fs_swab32(dirent->inode);
- dirent->rec_len = ext2fs_swab16(dirent->rec_len);
- dirent->name_len = ext2fs_swab16(dirent->name_len);
-#endif
- name_len = dirent->name_len;
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_dir_block_csum_verify(fs, ino,
+ (struct ext2_dir_entry *)buf))
+ corrupt = 1;
+
#ifdef WORDS_BIGENDIAN
- if (flags & EXT2_DIRBLOCK_V2_STRUCT)
- dirent->name_len = ext2fs_swab16(dirent->name_len);
+ retval = ext2fs_dirent_swab_in(fs, buf, flags);
#endif
- if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
- return retval;
- if ((rec_len < 8) || (rec_len % 4)) {
- rec_len = 8;
- retval = EXT2_ET_DIR_CORRUPTED;
- } else if (((name_len & 0xFF) + 8) > rec_len)
- retval = EXT2_ET_DIR_CORRUPTED;
- p += rec_len;
- }
+ if (!retval && corrupt)
+ retval = EXT2_ET_DIR_CSUM_INVALID;
return retval;
}

+errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
+ void *buf, int flags EXT2FS_ATTR((unused)))
+{
+ return ext2fs_read_dir_block4(fs, block, buf, flags, 0);
+}
+
errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
void *buf, int flags EXT2FS_ATTR((unused)))
{
@@ -72,45 +63,40 @@ errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
}


-errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
- void *inbuf, int flags EXT2FS_ATTR((unused)))
+errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block,
+ void *inbuf, int flags EXT2FS_ATTR((unused)),
+ ext2_ino_t ino)
{
-#ifdef WORDS_BIGENDIAN
errcode_t retval;
- char *p, *end;
- char *buf = 0;
- unsigned int rec_len;
- struct ext2_dir_entry *dirent;
+ char *buf = inbuf;

+#ifdef WORDS_BIGENDIAN
retval = ext2fs_get_mem(fs->blocksize, &buf);
if (retval)
return retval;
memcpy(buf, inbuf, fs->blocksize);
- p = buf;
- end = buf + fs->blocksize;
- while (p < end) {
- dirent = (struct ext2_dir_entry *) p;
- if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
- return retval;
- if ((rec_len < 8) ||
- (rec_len % 4)) {
- ext2fs_free_mem(&buf);
- return (EXT2_ET_DIR_CORRUPTED);
- }
- p += rec_len;
- dirent->inode = ext2fs_swab32(dirent->inode);
- dirent->rec_len = ext2fs_swab16(dirent->rec_len);
- dirent->name_len = ext2fs_swab16(dirent->name_len);
-
- if (flags & EXT2_DIRBLOCK_V2_STRUCT)
- dirent->name_len = ext2fs_swab16(dirent->name_len);
- }
+ retval = ext2fs_dirent_swab_out(fs, buf, flags);
+ if (retval)
+ return retval;
+#endif
+ retval = ext2fs_dir_block_csum_set(fs, ino,
+ (struct ext2_dir_entry *)buf);
+ if (retval)
+ goto out;
+
retval = io_channel_write_blk64(fs->io, block, 1, buf);
+
+out:
+#ifdef WORDS_BIGENDIAN
ext2fs_free_mem(&buf);
- return retval;
-#else
- return io_channel_write_blk64(fs->io, block, 1, (char *) inbuf);
#endif
+ return retval;
+}
+
+errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
+ void *inbuf, int flags EXT2FS_ATTR((unused)))
+{
+ return ext2fs_write_dir_block4(fs, block, inbuf, flags, 0);
}

errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
index 41c4088..22558d6 100644
--- a/lib/ext2fs/expanddir.c
+++ b/lib/ext2fs/expanddir.c
@@ -24,6 +24,7 @@ struct expand_dir_struct {
int newblocks;
blk64_t goal;
errcode_t err;
+ ext2_ino_t dir;
};

static int expand_dir_proc(ext2_filsys fs,
@@ -62,7 +63,8 @@ static int expand_dir_proc(ext2_filsys fs,
return BLOCK_ABORT;
}
es->done = 1;
- retval = ext2fs_write_dir_block(fs, new_blk, block);
+ retval = ext2fs_write_dir_block4(fs, new_blk, block, 0,
+ es->dir);
} else {
retval = ext2fs_get_mem(fs->blocksize, &block);
if (retval) {
@@ -110,6 +112,7 @@ errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
es.err = 0;
es.goal = 0;
es.newblocks = 0;
+ es.dir = dir;

retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
0, expand_dir_proc, &es);
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 1e51384..ad0cb7a 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -455,4 +455,7 @@ ec EXT2_ET_EXTENT_CSUM_INVALID,
ec EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
"Directory block does not have space for checksum"

+ec EXT2_ET_DIR_CSUM_INVALID,
+ "Directory block checksum does not match directory block"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index e224e74..b75c712 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -952,6 +952,18 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+#define EXT2_DIRENT_TAIL(block, blocksize) \
+ ((struct ext2_dir_entry_tail *)(((void *)(block)) + \
+ (blocksize) - sizeof(struct ext2_dir_entry_tail)))
+
+extern void ext2fs_initialize_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry_tail *t);
+extern int ext2fs_dirent_has_tail(ext2_filsys fs,
+ struct ext2_dir_entry *dirent);
+extern int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent);
+extern errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent);
extern errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
struct ext2_dir_entry *dirent,
struct ext2_dx_countlimit **cc,
@@ -1035,12 +1047,16 @@ extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
void *buf, int flags);
extern errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
void *buf, int flags);
+extern errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block,
+ void *buf, int flags, ext2_ino_t ino);
extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
void *buf);
extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
void *buf, int flags);
extern errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
void *buf, int flags);
+extern errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block,
+ void *buf, int flags, ext2_ino_t ino);

/* dirhash.c */
extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
@@ -1434,6 +1450,8 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);

/* swapfs.c */
+extern errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags);
+extern errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags);
extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
int has_header);
extern void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header,
diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
index 2d03b57..2dec5dc 100644
--- a/lib/ext2fs/link.c
+++ b/lib/ext2fs/link.c
@@ -41,6 +41,7 @@ static int link_proc(struct ext2_dir_entry *dirent,
struct ext2_dir_entry *next;
unsigned int rec_len, min_rec_len, curr_rec_len;
int ret = 0;
+ int csum_size = 0;

rec_len = EXT2_DIR_REC_LEN(ls->namelen);

@@ -48,12 +49,15 @@ static int link_proc(struct ext2_dir_entry *dirent,
if (ls->err)
return DIRENT_ABORT;

+ if (EXT2_HAS_RO_COMPAT_FEATURE(ls->fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
/*
* See if the following directory entry (if any) is unused;
* if so, absorb it into this one.
*/
next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
- if ((offset + (int) curr_rec_len < blocksize - 8) &&
+ if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
(next->inode == 0) &&
(offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
curr_rec_len += next->rec_len;
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index 861ddab..4a85439 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -100,7 +100,7 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
retval = ext2fs_write_new_inode(fs, ino, &inode);
if (retval)
goto cleanup;
- retval = ext2fs_write_dir_block(fs, blk, block);
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
if (retval)
goto cleanup;

diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c
index b0a1e47..2cd541d 100644
--- a/lib/ext2fs/newdir.c
+++ b/lib/ext2fs/newdir.c
@@ -34,6 +34,8 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
char *buf;
int rec_len;
int filetype = 0;
+ struct ext2_dir_entry_tail *t;
+ int csum_size = 0;

EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

@@ -43,7 +45,11 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
memset(buf, 0, fs->blocksize);
dir = (struct ext2_dir_entry *) buf;

- retval = ext2fs_set_rec_len(fs, fs->blocksize, dir);
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ retval = ext2fs_set_rec_len(fs, fs->blocksize - csum_size, dir);
if (retval)
return retval;

@@ -57,7 +63,7 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
dir->inode = dir_ino;
dir->name_len = 1 | filetype;
dir->name[0] = '.';
- rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1);
+ rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
dir->rec_len = EXT2_DIR_REC_LEN(1);

/*
@@ -73,6 +79,11 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
dir->name[1] = '.';

}
+
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(buf, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
*block = buf;
return 0;
}
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 7c99373..69916e5 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -350,4 +350,66 @@ void ext2fs_swap_mmp(struct mmp_struct *mmp)
mmp->mmp_check_interval = ext2fs_swab16(mmp->mmp_check_interval);
}

+errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
+{
+ errcode_t retval;
+ char *p, *end;
+ struct ext2_dir_entry *dirent;
+ unsigned int name_len, rec_len;
+
+ p = (char *) buf;
+ end = (char *) buf + fs->blocksize;
+ while (p < end-8) {
+ dirent = (struct ext2_dir_entry *) p;
+ dirent->inode = ext2fs_swab32(dirent->inode);
+ dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ name_len = dirent->name_len;
+ if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+ if (retval)
+ return retval;
+ if ((rec_len < 8) || (rec_len % 4)) {
+ rec_len = 8;
+ retval = EXT2_ET_DIR_CORRUPTED;
+ } else if (((name_len & 0xFF) + 8) > rec_len)
+ retval = EXT2_ET_DIR_CORRUPTED;
+ p += rec_len;
+ }
+
+ return 0;
+}
+
+errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags)
+{
+ errcode_t retval;
+ char *p, *end;
+ unsigned int rec_len;
+ struct ext2_dir_entry *dirent;
+
+ p = buf;
+ end = buf + fs->blocksize;
+ while (p < end) {
+ dirent = (struct ext2_dir_entry *) p;
+ retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+ if (retval)
+ return retval;
+ if ((rec_len < 8) ||
+ (rec_len % 4)) {
+ ext2fs_free_mem(&buf);
+ return EXT2_ET_DIR_CORRUPTED;
+ }
+ p += rec_len;
+ dirent->inode = ext2fs_swab32(dirent->inode);
+ dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+
+ if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ }
+
+ return 0;
+}
+
#endif


2012-03-07 00:01:10

by djwong

[permalink] [raw]
Subject: [PATCH 31/54] libext2fs: Calculate and verify superblock checksums

Calculate and verify the superblock checksums. Each copy of the superblock
records the number of the group it's in and the FS UUID, so we can simply
checksum the whole block.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/closefs.c | 19 ++++++++++++-------
lib/ext2fs/csum.c | 35 +++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2fs.h | 4 ++++
lib/ext2fs/openfs.c | 6 ++++++
5 files changed, 60 insertions(+), 7 deletions(-)


diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 1867be3..a0e28ba 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -246,15 +246,19 @@ static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group,
blk_t group_block,
struct ext2_super_block *super_shadow)
{
+ errcode_t retval;
dgrp_t sgrp = group;

if (sgrp > ((1 << 16) - 1))
sgrp = (1 << 16) - 1;
+
+ super_shadow->s_block_group_nr = sgrp;
#ifdef WORDS_BIGENDIAN
- super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
-#else
- fs->super->s_block_group_nr = sgrp;
+ ext2fs_swap_super(super_shadow);
#endif
+ retval = ext2fs_superblock_csum_set(fs, super_shadow);
+ if (retval)
+ return retval;

return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE,
super_shadow);
@@ -314,6 +318,7 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
&group_shadow);
if (retval)
goto errout;
+ memcpy(super_shadow, fs->super, sizeof(struct ext2_super_block));
memcpy(group_shadow, fs->group_desc, (size_t) fs->blocksize *
fs->desc_blocks);

@@ -334,10 +339,6 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
*/
fs->super->s_state &= ~EXT2_VALID_FS;
fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
-#ifdef WORDS_BIGENDIAN
- *super_shadow = *fs->super;
- ext2fs_swap_super(super_shadow);
-#endif

/*
* If this is an external journal device, don't write out the
@@ -412,6 +413,10 @@ write_primary_superblock_only:
ext2fs_swap_super(super_shadow);
#endif

+ retval = ext2fs_superblock_csum_set(fs, super_shadow);
+ if (retval)
+ return retval;
+
if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC))
retval = io_channel_flush(fs->io);
retval = write_primary_superblock(fs, super_shadow);
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 46f0cb5..4d01bcd 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,41 @@
#define STATIC static
#endif

+static __u32 ext2fs_superblock_csum(ext2_filsys fs, struct ext2_super_block *sb)
+{
+ int offset = offsetof(struct ext2_super_block, s_checksum);
+
+ return ext2fs_crc32c_le(~0, (unsigned char *)sb, offset);
+}
+
+int ext2fs_superblock_csum_verify(ext2_filsys fs, struct ext2_super_block *sb)
+{
+ __u32 calculated;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ calculated = ext2fs_superblock_csum(fs, sb);
+
+ return ext2fs_le32_to_cpu(sb->s_checksum) == calculated;
+}
+
+errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
+ struct ext2_super_block *sb)
+{
+ __u32 crc;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ crc = ext2fs_superblock_csum(fs, sb);
+ sb->s_checksum = ext2fs_cpu_to_le32(crc);
+
+ return 0;
+}
+
static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum,
blk64_t block,
struct ext2_ext_attr_header *hdr,
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 177a97f..0fab4e0 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -461,4 +461,7 @@ ec EXT2_ET_DIR_CSUM_INVALID,
ec EXT2_ET_EXT_ATTR_CSUM_INVALID,
"Extended attribute block checksum does not match block"

+ec EXT2_ET_SB_CSUM_INVALID,
+ "Superblock checksum does not match superblock"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 5684df5..3c52f4b 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -952,6 +952,10 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+extern errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
+ struct ext2_super_block *sb);
+extern int ext2fs_superblock_csum_verify(ext2_filsys fs,
+ struct ext2_super_block *sb);
extern errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs,
ext2_ino_t inum, blk64_t block,
struct ext2_ext_attr_header *hdr);
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 10beb06..8b7e750 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -193,6 +193,12 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
if (fs->orig_super)
memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE);

+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_superblock_csum_verify(fs, fs->super)) {
+ retval = EXT2_ET_SB_CSUM_INVALID;
+ goto cleanup;
+ }
+
#ifdef WORDS_BIGENDIAN
fs->flags |= EXT2_FLAG_SWAP_BYTES;
ext2fs_swap_super(fs->super);


2012-03-07 00:01:13

by djwong

[permalink] [raw]
Subject: [PATCH 28/54] libext2fs: Verify and calculate extended attribute block checksums

Calculate and verify the checksum for separate (i.e. not in the inode) extended
attribute blocks; the checksum lives in the header.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 10 ++++---
e2fsck/pass1b.c | 4 +--
e2fsck/pass2.c | 6 ++--
e2fsck/super.c | 6 ++--
lib/ext2fs/csum.c | 68 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2fs.h | 14 +++++++++
lib/ext2fs/ext_attr.c | 59 ++++++++++++++++++++++++++++++---------
lib/ext2fs/swapfs.c | 3 +-
9 files changed, 146 insertions(+), 27 deletions(-)


diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 42fca3e..ce03e46 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1514,7 +1514,8 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
break;
pctx.blk = blk;
- pctx.errcode = ext2fs_read_ext_attr2(fs, blk, block_buf);
+ pctx.errcode = ext2fs_read_ext_attr3(fs, blk, block_buf,
+ pctx.ino);
if (pctx.errcode) {
fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
return;
@@ -1525,8 +1526,9 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
pctx.num = should_be;
if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
header->h_refcount = should_be;
- pctx.errcode = ext2fs_write_ext_attr2(fs, blk,
- block_buf);
+ pctx.errcode = ext2fs_write_ext_attr3(fs, blk,
+ block_buf,
+ pctx.ino);
if (pctx.errcode) {
fix_problem(ctx, PR_1_EXTATTR_WRITE_ABORT,
&pctx);
@@ -1624,7 +1626,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
* validate it
*/
pctx->blk = blk;
- pctx->errcode = ext2fs_read_ext_attr2(fs, blk, block_buf);
+ pctx->errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, pctx->ino);
if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
goto clear_extattr;
header = (struct ext2_ext_attr_header *) block_buf;
diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c
index 93fb630..05cbd10 100644
--- a/e2fsck/pass1b.c
+++ b/e2fsck/pass1b.c
@@ -653,9 +653,9 @@ static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
if (ext2fs_file_acl_block(fs, &inode) &&
(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
count = 1;
- pctx.errcode = ext2fs_adjust_ea_refcount2(fs,
+ pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
ext2fs_file_acl_block(fs, &inode),
- block_buf, -1, &count);
+ block_buf, -1, &count, ino);
if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
pctx.errcode = 0;
count = 1;
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index be950de..e28af61 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -1290,9 +1290,9 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)

if (ext2fs_file_acl_block(fs, &inode) &&
(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
- pctx.errcode = ext2fs_adjust_ea_refcount2(fs,
- ext2fs_file_acl_block(fs, &inode),
- block_buf, -1, &count);
+ pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
+ ext2fs_file_acl_block(fs, &inode),
+ block_buf, -1, &count, ino);
if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
pctx.errcode = 0;
count = 1;
diff --git a/e2fsck/super.c b/e2fsck/super.c
index 94648c3..dbd337c 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -198,9 +198,9 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
ext2fs_iblk_sub_blocks(fs, inode, pb.truncated_blocks);

if (ext2fs_file_acl_block(fs, inode)) {
- retval = ext2fs_adjust_ea_refcount2(fs,
- ext2fs_file_acl_block(fs, inode),
- block_buf, -1, &count);
+ retval = ext2fs_adjust_ea_refcount3(fs,
+ ext2fs_file_acl_block(fs, inode),
+ block_buf, -1, &count, ino);
if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
retval = 0;
count = 1;
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 1c06f5e..46f0cb5 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,74 @@
#define STATIC static
#endif

+static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr,
+ __u32 *crc)
+{
+ errcode_t retval;
+ char *buf = (char *)hdr;
+ __u32 gen, old_crc = hdr->h_checksum;
+ struct ext2_inode inode;
+
+ hdr->h_checksum = 0;
+ if (ext2fs_le32_to_cpu(hdr->h_refcount) != 1) {
+ block = ext2fs_cpu_to_le64(block);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&block,
+ sizeof(block));
+ } else {
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen,
+ sizeof(gen));
+ }
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, fs->blocksize);
+ hdr->h_checksum = old_crc;
+
+ return 0;
+}
+
+int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr)
+{
+ __u32 calculated;
+ errcode_t retval;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &calculated);
+ if (retval)
+ return 0;
+
+ return ext2fs_le32_to_cpu(hdr->h_checksum) == calculated;
+}
+
+errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr)
+{
+ errcode_t retval;
+ __u32 crc;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &crc);
+ if (retval)
+ return retval;
+ hdr->h_checksum = ext2fs_cpu_to_le32(crc);
+ return 0;
+}
+
static __u16 do_nothing16(__u16 x)
{
return x;
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index ad0cb7a..177a97f 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -458,4 +458,7 @@ ec EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
ec EXT2_ET_DIR_CSUM_INVALID,
"Directory block checksum does not match directory block"

+ec EXT2_ET_EXT_ATTR_CSUM_INVALID,
+ "Extended attribute block checksum does not match block"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index b75c712..5684df5 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -952,6 +952,12 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+extern errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs,
+ ext2_ino_t inum, blk64_t block,
+ struct ext2_ext_attr_header *hdr);
+extern int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr);
#define EXT2_DIRENT_TAIL(block, blocksize) \
((struct ext2_dir_entry_tail *)(((void *)(block)) + \
(blocksize) - sizeof(struct ext2_dir_entry_tail)))
@@ -1107,16 +1113,24 @@ extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry,
extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf);
extern errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block,
void *buf);
+extern errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block,
+ void *buf, ext2_ino_t inum);
extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block,
void *buf);
extern errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block,
void *buf);
+extern errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block,
+ void *buf, ext2_ino_t inum);
extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
char *block_buf,
int adjust, __u32 *newcount);
extern errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
char *block_buf,
int adjust, __u32 *newcount);
+extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
+ char *block_buf,
+ int adjust, __u32 *newcount,
+ ext2_ino_t inum);

/* extent.c */
extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 1889824..9649a14 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -61,17 +61,29 @@ __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data)
#undef NAME_HASH_SHIFT
#undef VALUE_HASH_SHIFT

-errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
+errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, void *buf,
+ ext2_ino_t inum)
{
errcode_t retval;

retval = io_channel_read_blk64(fs->io, block, 1, buf);
if (retval)
return retval;
+
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_ext_attr_block_csum_verify(fs, inum, block, buf))
+ retval = EXT2_ET_EXT_ATTR_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
#endif
- return 0;
+
+ return retval;
+}
+
+errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
+{
+ return ext2fs_read_ext_attr3(fs, block, buf, 0);
}

errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
@@ -79,30 +91,40 @@ errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
return ext2fs_read_ext_attr2(fs, block, buf);
}

-errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
+errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block, void *inbuf,
+ ext2_ino_t inum)
{
errcode_t retval;
char *write_buf;
-#ifdef WORDS_BIGENDIAN
- char *buf = NULL;

- retval = ext2fs_get_mem(fs->blocksize, &buf);
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_get_mem(fs->blocksize, &write_buf);
if (retval)
return retval;
- write_buf = buf;
- ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1);
+ ext2fs_swap_ext_attr(write_buf, inbuf, fs->blocksize, 1);
#else
write_buf = (char *) inbuf;
#endif
+
+ retval = ext2fs_ext_attr_block_csum_set(fs, inum, block,
+ (struct ext2_ext_attr_header *)write_buf);
+ if (retval)
+ return retval;
+
retval = io_channel_write_blk64(fs->io, block, 1, write_buf);
#ifdef WORDS_BIGENDIAN
- ext2fs_free_mem(&buf);
+ ext2fs_free_mem(&write_buf);
#endif
if (!retval)
ext2fs_mark_changed(fs);
return retval;
}

+errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
+{
+ return ext2fs_write_ext_attr3(fs, block, inbuf, 0);
+}
+
errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
{
return ext2fs_write_ext_attr2(fs, block, inbuf);
@@ -111,9 +133,9 @@ errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
/*
* This function adjusts the reference count of the EA block.
*/
-errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
+errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
char *block_buf, int adjust,
- __u32 *newcount)
+ __u32 *newcount, ext2_ino_t inum)
{
errcode_t retval;
struct ext2_ext_attr_header *header;
@@ -130,7 +152,7 @@ errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
block_buf = buf;
}

- retval = ext2fs_read_ext_attr2(fs, blk, block_buf);
+ retval = ext2fs_read_ext_attr3(fs, blk, block_buf, inum);
if (retval)
goto errout;

@@ -139,7 +161,7 @@ errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
if (newcount)
*newcount = header->h_refcount;

- retval = ext2fs_write_ext_attr2(fs, blk, block_buf);
+ retval = ext2fs_write_ext_attr3(fs, blk, block_buf, inum);
if (retval)
goto errout;

@@ -149,9 +171,18 @@ errout:
return retval;
}

+errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
+ char *block_buf, int adjust,
+ __u32 *newcount)
+{
+ return ext2fs_adjust_ea_refcount3(fs, blk, block_buf, adjust,
+ newcount, 0);
+}
+
errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
char *block_buf, int adjust,
__u32 *newcount)
{
- return ext2fs_adjust_ea_refcount(fs, blk, block_buf, adjust, newcount);
+ return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
+ newcount);
}
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 69916e5..de178a4 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -157,7 +157,8 @@ void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header,
to_header->h_blocks = ext2fs_swab32(from_header->h_blocks);
to_header->h_refcount = ext2fs_swab32(from_header->h_refcount);
to_header->h_hash = ext2fs_swab32(from_header->h_hash);
- for (n = 0; n < 4; n++)
+ to_header->h_checksum = ext2fs_swab32(from_header->h_checksum);
+ for (n = 0; n < 3; n++)
to_header->h_reserved[n] =
ext2fs_swab32(from_header->h_reserved[n]);
}


2012-03-07 00:01:14

by djwong

[permalink] [raw]
Subject: [PATCH 26/54] e2fsck: Check directory leaf block checksums

Checks that directory leaf blocks have the necessary fake dir_entry at the end
of the block to hold a checksum and that the checksum is valid. It will resize
the block and/or rebuild the directory if necessary.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass2.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++-------
e2fsck/pass3.c | 4 +++
e2fsck/problem.c | 15 ++++++++++
e2fsck/problem.h | 9 ++++++
4 files changed, 97 insertions(+), 10 deletions(-)


diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 5de301f..be950de 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -753,8 +753,9 @@ static int check_dir_block(ext2_filsys fs,
struct problem_context pctx;
int dups_found = 0;
int ret;
- int dx_csum_size = 0;
+ int dx_csum_size = 0, de_csum_size = 0;
int failed_csum = 0;
+ int is_leaf = 1;

cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -767,8 +768,10 @@ static int check_dir_block(ext2_filsys fs,
return DIRENT_ABORT;

if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
dx_csum_size = sizeof(struct ext2_dx_tail);
+ de_csum_size = sizeof(struct ext2_dir_entry_tail);
+ }

/*
* Make sure the inode is still in use (could have been
@@ -814,11 +817,15 @@ static int check_dir_block(ext2_filsys fs,
failed_csum = 1;
}
if (cd->pctx.errcode) {
+ char *buf2;
if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
ctx->flags |= E2F_FLAG_ABORT;
return DIRENT_ABORT;
}
- memset(buf, 0, fs->blocksize);
+ ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0,
+ EXT2_ROOT_INO, &buf2);
+ memcpy(buf, buf2, fs->blocksize);
+ ext2fs_free_mem(&buf2);
}
#ifdef ENABLE_HTREE
dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
@@ -866,10 +873,43 @@ static int check_dir_block(ext2_filsys fs,
((fs->blocksize - (8 + dx_csum_size)) /
sizeof(struct ext2_dx_entry))))
dx_db->type = DX_DIRBLOCK_NODE;
+ is_leaf = 0;
}
out_htree:
#endif /* ENABLE_HTREE */

+ /* Verify checksum. */
+ if (is_leaf && de_csum_size) {
+ /* No space for csum? Rebuild dirs in pass 3A. */
+ if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
+ de_csum_size = 0;
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ goto skip_checksum;
+ if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM,
+ &cd->pctx))
+ goto skip_checksum;
+ e2fsck_rehash_dir_later(ctx, ino);
+ goto skip_checksum;
+ }
+ if (failed_csum) {
+ char *buf2;
+ if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_CSUM_INVALID,
+ &cd->pctx))
+ goto skip_checksum;
+ ext2fs_new_dir_block(fs,
+ db->blockcnt == 0 ? ino : 0,
+ EXT2_ROOT_INO, &buf2);
+ memcpy(buf, buf2, fs->blocksize);
+ ext2fs_free_mem(&buf2);
+ dir_modified++;
+ failed_csum = 0;
+ }
+ }
+ /* htree nodes don't use fake dirents to store checksums */
+ if (!is_leaf)
+ de_csum_size = 0;
+
+skip_checksum:
dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
prev = 0;
do {
@@ -1117,10 +1157,7 @@ out_htree:
pctx.ino = ino;
pctx.dirent = dirent;
fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
- if (!ctx->dirs_to_hash)
- ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
- if (ctx->dirs_to_hash)
- ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+ e2fsck_rehash_dir_later(ctx, ino);
dups_found++;
} else
dict_alloc_insert(&de_dict, dirent, dirent);
@@ -1136,7 +1173,7 @@ out_htree:
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
offset += rec_len;
dot_state++;
- } while (offset < fs->blocksize);
+ } while (offset < fs->blocksize - de_csum_size);
#if 0
printf("\n");
#endif
@@ -1153,22 +1190,44 @@ out_htree:
parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
}
#endif /* ENABLE_HTREE */
- if (offset != fs->blocksize) {
- cd->pctx.num = rec_len - fs->blocksize + offset;
+
+ if (offset != fs->blocksize - de_csum_size) {
+ cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
+ offset;
if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
dirent->rec_len = cd->pctx.num;
dir_modified++;
}
}
if (dir_modified) {
+ /* leaf block with no tail? Rehash dirs later. */
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ is_leaf &&
+ !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf))
+ e2fsck_rehash_dir_later(ctx, ino);
+
+write_and_fix:
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
0, ino);
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (cd->pctx.errcode) {
if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
&cd->pctx))
goto abort_free_dict;
}
ext2fs_mark_changed(fs);
+ } else if (is_leaf && failed_csum && !dir_modified) {
+ /*
+ * If a leaf node that fails csum makes it this far without
+ * alteration, ask the user if the checksum should be fixed.
+ */
+ if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID,
+ &cd->pctx))
+ goto write_and_fix;
}
dict_free_nodes(&de_dict);
return 0;
diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 4ae3a33..ad733c2 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -659,8 +659,12 @@ static void fix_dotdot(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)

clear_problem_context(&pctx);
pctx.ino = ino;
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_dir_iterate(fs, ino, DIRENT_FLAG_INCLUDE_EMPTY,
0, fix_dotdot_proc, &fp);
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval || !fp.done) {
pctx.errcode = retval;
fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 25db675..d910629 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1393,6 +1393,21 @@ static struct e2fsck_problem problem_table[] = {
N_("@p @h %d: internal node fails checksum\n"),
PROMPT_CLEAR_HTREE, PR_PREEN_OK },

+ /* leaf node fails checksum */
+ { PR_2_LEAF_NODE_CSUM_INVALID,
+ N_("@d @i %i, %B, offset %N: @d fails checksum\n"),
+ PROMPT_SALVAGE, PR_PREEN_OK },
+
+ /* leaf node has no checksum */
+ { PR_2_LEAF_NODE_MISSING_CSUM,
+ N_("@d @i %i, %B, offset %N: @d has no checksum\n"),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* leaf node passes checks but fails checksum */
+ { PR_2_LEAF_NODE_ONLY_CSUM_INVALID,
+ N_("@d @i %i, %B, offset %N: @d passes checks but fails checksum\n"),
+ PROMPT_FIX, PR_PREEN_OK },
+
/* Pass 3 errors */

/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 5cf80fb..eb70d61 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -833,6 +833,15 @@ struct problem_context {
/* htree node fails checksum */
#define PR_2_HTREE_NODE_CSUM_INVALID 0x02004A

+/* dir leaf node fails checksum */
+#define PR_2_LEAF_NODE_CSUM_INVALID 0x02004B
+
+/* no space in leaf for checksum */
+#define PR_2_LEAF_NODE_MISSING_CSUM 0x02004C
+
+/* dir leaf node passes checks, but fails checksum */
+#define PR_2_LEAF_NODE_ONLY_CSUM_INVALID 0x02004D
+
/*
* Pass 3 errors
*/


2012-03-07 00:01:20

by djwong

[permalink] [raw]
Subject: [PATCH 30/54] tune2fs: Rewrite extended attribute block checksums

When enabling metadata checksums, rewrite separate extended attribute blocks.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 26 ++++++++++++++++++++++++++
1 files changed, 26 insertions(+), 0 deletions(-)


diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 801e6ea..2ae436e 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -610,9 +610,11 @@ static void rewrite_inodes(ext2_filsys fs)
{
int length = EXT2_INODE_SIZE(fs->super);
struct ext2_inode *inode;
+ char *ea_buf;
ext2_inode_scan scan;
errcode_t retval;
ext2_ino_t ino;
+ blk64_t file_acl_block;

if (fs->super->s_creator_os != EXT2_OS_LINUX)
return;
@@ -629,6 +631,12 @@ static void rewrite_inodes(ext2_filsys fs)
exit(1);
}

+ retval = ext2fs_get_mem(fs->blocksize, &ea_buf);
+ if (retval) {
+ com_err("set_csum", retval, "while allocating memory");
+ exit(1);
+ }
+
do {
retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
if (retval) {
@@ -659,9 +667,27 @@ static void rewrite_inodes(ext2_filsys fs)
exit(1);
}
}
+
+ file_acl_block = ext2fs_file_acl_block(fs, inode);
+ if (!file_acl_block)
+ continue;
+ retval = ext2fs_read_ext_attr3(fs, file_acl_block, ea_buf, ino);
+ if (retval) {
+ com_err("rewrite_eablock", retval,
+ "while rewriting extended attribute");
+ exit(1);
+ }
+ retval = ext2fs_write_ext_attr3(fs, file_acl_block, ea_buf,
+ ino);
+ if (retval) {
+ com_err("rewrite_eablock", retval,
+ "while rewriting extended attribute");
+ exit(1);
+ }
} while (ino);

ext2fs_free_mem(&inode);
+ ext2fs_free_mem(&ea_buf);
ext2fs_close_inode_scan(scan);
}



2012-03-07 00:01:23

by djwong

[permalink] [raw]
Subject: [PATCH 24/54] e2fsck: Verify htree root/node checksums

Check htree internal node checksums. If broken, ask user to clear the htree
index and recreate it later.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/e2fsck.h | 2 ++
e2fsck/pass2.c | 34 +++++++++++++++++++++++++++++-----
e2fsck/problem.c | 10 ++++++++++
e2fsck/problem.h | 6 ++++++
e2fsck/rehash.c | 41 +++++++++++++++++++++++++++++++++++++++--
5 files changed, 86 insertions(+), 7 deletions(-)


diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index ed8b0c7..c6dab54 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -483,6 +483,8 @@ extern void region_free(region_t region);
extern int region_allocate(region_t region, region_addr_t start, int n);

/* rehash.c */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino);
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino);
errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino);
void e2fsck_rehash_directories(e2fsck_t ctx);

diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 882950d..e4a06f3 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -527,7 +527,7 @@ static void parse_int_node(ext2_filsys fs,
struct ext2_db_entry2 *db,
struct check_dir_struct *cd,
struct dx_dir_info *dx_dir,
- char *block_buf)
+ char *block_buf, int failed_csum)
{
struct ext2_dx_root_info *root;
struct ext2_dx_entry *ent;
@@ -538,6 +538,7 @@ static void parse_int_node(ext2_filsys fs,
ext2_dirhash_t min_hash = 0xffffffff;
ext2_dirhash_t max_hash = 0;
ext2_dirhash_t hash = 0, prev_hash;
+ int csum_size = 0;

if (db->blockcnt == 0) {
root = (struct ext2_dx_root_info *) (block_buf + 24);
@@ -552,9 +553,22 @@ static void parse_int_node(ext2_filsys fs,
#endif

ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+
+ if (failed_csum &&
+ (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
+ fix_problem(cd->ctx, PR_2_HTREE_ROOT_CSUM_INVALID,
+ &cd->pctx)))
+ goto clear_and_exit;
} else {
ent = (struct ext2_dx_entry *) (block_buf+8);
+
+ if (failed_csum &&
+ (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
+ fix_problem(cd->ctx, PR_2_HTREE_NODE_CSUM_INVALID,
+ &cd->pctx)))
+ goto clear_and_exit;
}
+
limit = (struct ext2_dx_countlimit *) ent;

#ifdef DX_DEBUG
@@ -565,8 +579,12 @@ static void parse_int_node(ext2_filsys fs,
#endif

count = ext2fs_le16_to_cpu(limit->count);
- expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
- sizeof(struct ext2_dx_entry);
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+ expect_limit = (fs->blocksize -
+ (csum_size + ((char *) ent - block_buf))) /
+ sizeof(struct ext2_dx_entry);
if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
@@ -632,6 +650,7 @@ static void parse_int_node(ext2_filsys fs,
clear_and_exit:
clear_htree(cd->ctx, cd->pctx.ino);
dx_dir->numblocks = 0;
+ e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino);
}
#endif /* ENABLE_HTREE */

@@ -734,6 +753,7 @@ static int check_dir_block(ext2_filsys fs,
struct problem_context pctx;
int dups_found = 0;
int ret;
+ int dx_csum_size = 0;

cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -745,6 +765,10 @@ static int check_dir_block(ext2_filsys fs,
if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
return DIRENT_ABORT;

+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ dx_csum_size = sizeof(struct ext2_dx_tail);
+
/*
* Make sure the inode is still in use (could have been
* deleted in the duplicate/bad blocks pass.
@@ -834,7 +858,7 @@ static int check_dir_block(ext2_filsys fs,
(rec_len == fs->blocksize) &&
(dirent->name_len == 0) &&
(ext2fs_le16_to_cpu(limit->limit) ==
- ((fs->blocksize-8) /
+ ((fs->blocksize - (8 + dx_csum_size)) /
sizeof(struct ext2_dx_entry))))
dx_db->type = DX_DIRBLOCK_NODE;
}
@@ -1121,7 +1145,7 @@ out_htree:
cd->pctx.dir = cd->pctx.ino;
if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
(dx_db->type == DX_DIRBLOCK_NODE))
- parse_int_node(fs, db, cd, dx_dir, buf);
+ parse_int_node(fs, db, cd, dx_dir, buf, 0);
}
#endif /* ENABLE_HTREE */
if (offset != fs->blocksize) {
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 6ceee11..25db675 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1383,6 +1383,16 @@ static struct e2fsck_problem problem_table[] = {
N_("i_file_acl_hi @F %N, @s zero.\n"),
PROMPT_CLEAR, PR_PREEN_OK },

+ /* htree root node fails checksum */
+ { PR_2_HTREE_ROOT_CSUM_INVALID,
+ N_("@p @h %d: root node fails checksum\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* htree internal node fails checksum */
+ { PR_2_HTREE_NODE_CSUM_INVALID,
+ N_("@p @h %d: internal node fails checksum\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
/* Pass 3 errors */

/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 2adb4b9..5cf80fb 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -827,6 +827,12 @@ struct problem_context {
/* i_file_acl_hi should be zero */
#define PR_2_I_FILE_ACL_HI_ZERO 0x020048

+/* htree root node fails checksum */
+#define PR_2_HTREE_ROOT_CSUM_INVALID 0x020049
+
+/* htree node fails checksum */
+#define PR_2_HTREE_NODE_CSUM_INVALID 0x02004A
+
/*
* Pass 3 errors
*/
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 15993b3..610b3bf 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -52,6 +52,25 @@
#include "e2fsck.h"
#include "problem.h"

+/* Schedule a dir to be rebuilt during pass 3A. */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino)
+{
+ if (!ctx->dirs_to_hash)
+ ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
+ if (ctx->dirs_to_hash)
+ ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+}
+
+/* Ask if a dir will be rebuilt during pass 3A. */
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino)
+{
+ if (ctx->options & E2F_OPT_COMPRESS_DIRS)
+ return 1;
+ if (!ctx->dirs_to_hash)
+ return 0;
+ return ext2fs_u32_list_test(ctx->dirs_to_hash, ino);
+}
+
struct fill_dir_struct {
char *buf;
struct ext2_inode *inode;
@@ -495,6 +514,7 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
struct ext2_dx_root_info *root;
struct ext2_dx_countlimit *limits;
int filetype = 0;
+ int csum_size = 0;

if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
filetype = EXT2_FT_DIR << 8;
@@ -519,8 +539,13 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
root->indirect_levels = 0;
root->unused_flags = 0;

+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+
limits = (struct ext2_dx_countlimit *) (buf+32);
- limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
+ limits->limit = (fs->blocksize - (32 + csum_size)) /
+ sizeof(struct ext2_dx_entry);
limits->count = 0;

return root;
@@ -531,14 +556,20 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
{
struct ext2_dir_entry *dir;
struct ext2_dx_countlimit *limits;
+ int csum_size = 0;

memset(buf, 0, fs->blocksize);
dir = (struct ext2_dir_entry *) buf;
dir->inode = 0;
(void) ext2fs_set_rec_len(fs, fs->blocksize, dir);

+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+
limits = (struct ext2_dx_countlimit *) (buf+8);
- limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
+ limits->limit = (fs->blocksize - (8 + csum_size)) /
+ sizeof(struct ext2_dx_entry);
limits->count = 0;

return (struct ext2_dx_entry *) limits;
@@ -865,8 +896,14 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
if (!ext2fs_u32_list_iterate(iter, &ino))
break;
}
+#if 0
+ /*
+ * lost+found must not be excluded from cleanups or else
+ * checksum errors won't get fixed.
+ */
if (ino == ctx->lost_and_found)
continue;
+#endif
pctx.dir = ino;
if (first) {
fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);


2012-03-07 00:01:34

by djwong

[permalink] [raw]
Subject: [PATCH 32/54] e2fsck: Handle superblock checksum errors gracefully

If e2fsck finds a superblock with an invalid checksum, try the backups.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/unix.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)


diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 6f97b0f..a8e3775 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1198,6 +1198,7 @@ restart:
if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
!(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
((retval == EXT2_ET_BAD_MAGIC) ||
+ (retval == EXT2_ET_SB_CSUM_INVALID) ||
(retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
((retval == 0) && (retval2 = ext2fs_check_desc(fs))))) {
if (retval2 == ENOMEM) {


2012-03-07 00:01:35

by djwong

[permalink] [raw]
Subject: [PATCH 29/54] e2fsck: Check extended attribute block checksums

Verify the checksums of separate extended attribute blocks and offer to clear
it if there is a mismatch.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 18 ++++++++++++++++++
e2fsck/problem.c | 15 +++++++++++++++
e2fsck/problem.h | 6 ++++++
3 files changed, 39 insertions(+), 0 deletions(-)


diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index ce03e46..debc365 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1553,6 +1553,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
struct ext2_ext_attr_entry *entry;
int count;
region_t region = 0;
+ int failed_csum = 0;

blk = ext2fs_file_acl_block(fs, inode);
if (blk == 0)
@@ -1627,6 +1628,11 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
*/
pctx->blk = blk;
pctx->errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, pctx->ino);
+ if (pctx->errcode == EXT2_ET_EXT_ATTR_CSUM_INVALID) {
+ if (fix_problem(ctx, PR_1_EA_BLOCK_CSUM_INVALID, pctx))
+ goto clear_extattr;
+ failed_csum = 1;
+ }
if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
goto clear_extattr;
header = (struct ext2_ext_attr_header *) block_buf;
@@ -1708,6 +1714,18 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
}
region_free(region);

+ /*
+ * We only get here if there was no other errors that were fixed.
+ * If there was a checksum fail, ask to correct it.
+ */
+ if (failed_csum &&
+ fix_problem(ctx, PR_1_EA_BLOCK_ONLY_CSUM_INVALID, pctx)) {
+ pctx->errcode = ext2fs_write_ext_attr3(fs, blk, block_buf,
+ pctx->ino);
+ if (pctx->errcode)
+ return 0;
+ }
+
count = header->h_refcount - 1;
if (count)
ea_refcount_store(ctx->refcount, blk, count);
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index d910629..ad6887e 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -961,6 +961,21 @@ static struct e2fsck_problem problem_table[] = {
"extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"),
PROMPT_FIX, 0 },

+ /* Extended attribute block checksum for inode does not match. */
+ { PR_1_EA_BLOCK_CSUM_INVALID,
+ N_("Extended attribute @a @b %b checksum for @i %i does not "
+ "match. "),
+ PROMPT_CLEAR, 0 },
+
+ /*
+ * Extended attribute block passes checks, but checksum for inode does
+ * not match.
+ */
+ { PR_1_EA_BLOCK_CSUM_INVALID,
+ N_("Extended attribute @a @b %b passes checks, but checksum for "
+ "@i %i does not match. "),
+ PROMPT_FIX, 0 },
+
/* Pass 1b errors */

/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index eb70d61..b8a7548 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -562,6 +562,12 @@ struct problem_context {
/* extent block passes checks, but checksum does not match extent block */
#define PR_1_EXTENT_ONLY_CSUM_INVALID 0x010069

+/* ea block checksum invalid */
+#define PR_1_EA_BLOCK_CSUM_INVALID 0x01006A
+
+/* ea block passes checks, but checksum invalid */
+#define PR_1_EA_BLOCK_ONLY_CSUM_INVALID 0x01006B
+
/*
* Pass 1b errors
*/


2012-03-07 00:01:48

by djwong

[permalink] [raw]
Subject: [PATCH 36/54] libext2fs: Block group checksum should use metadata_csum algorithm

Change the block group algorithm to use the same algorithm as the rest of the
metadata_csum. This mostly involves providing a helper function to tell if
group descriptors should have checksums set or verified, and modifying the gdt
checksum code to use the correct algorithm.

Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/debugfs.c | 3 +-
lib/ext2fs/alloc.c | 6 +---
lib/ext2fs/alloc_stats.c | 3 +-
lib/ext2fs/csum.c | 67 ++++++++++++++++++++++++++++------------------
lib/ext2fs/ext2fs.h | 6 ++++
lib/ext2fs/initialize.c | 3 +-
lib/ext2fs/inode.c | 9 ++----
lib/ext2fs/openfs.c | 3 +-
lib/ext2fs/rw_bitmaps.c | 12 +++-----
misc/dumpe2fs.c | 4 +--
resize/resize2fs.c | 12 +++-----
11 files changed, 66 insertions(+), 62 deletions(-)


diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 36dd730..2e46cd5 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -357,8 +357,7 @@ void do_show_super_stats(int argc, char *argv[])
return;
}

- gdt_csum = EXT2_HAS_RO_COMPAT_FEATURE(current_fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ gdt_csum = ext2fs_has_group_desc_csum(current_fs);
for (i = 0; i < current_fs->group_desc_count; i++) {
fprintf(out, " Group %2d: block bitmap at %llu, "
"inode bitmap at %llu, "
diff --git a/lib/ext2fs/alloc.c b/lib/ext2fs/alloc.c
index 948a0ec..e62ed68 100644
--- a/lib/ext2fs/alloc.c
+++ b/lib/ext2fs/alloc.c
@@ -36,8 +36,7 @@ static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map,
blk64_t blk, super_blk, old_desc_blk, new_desc_blk;
int old_desc_blocks;

- if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
+ if (!ext2fs_has_group_desc_csum(fs) ||
!(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)))
return;

@@ -83,8 +82,7 @@ static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map,
{
ext2_ino_t i, ino;

- if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
+ if (!ext2fs_has_group_desc_csum(fs) ||
!(ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)))
return;

diff --git a/lib/ext2fs/alloc_stats.c b/lib/ext2fs/alloc_stats.c
index adec363..4229084 100644
--- a/lib/ext2fs/alloc_stats.c
+++ b/lib/ext2fs/alloc_stats.c
@@ -38,8 +38,7 @@ void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
/* We don't strictly need to be clearing the uninit flag if inuse < 0
* (i.e. freeing inodes) but it also means something is bad. */
ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT);
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+ if (ext2fs_has_group_desc_csum(fs)) {
ext2_ino_t first_unused_inode = fs->super->s_inodes_per_group -
ext2fs_bg_itable_unused(fs, group) +
group * fs->super->s_inodes_per_group + 1;
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 11a24f0..d4f98e0 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -696,39 +696,56 @@ STATIC __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group)

desc = ext2fs_group_desc(fs, fs->group_desc, group);

- if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
- size_t offset = offsetof(struct ext2_group_desc, bg_checksum);
-
#ifdef WORDS_BIGENDIAN
- struct ext4_group_desc swabdesc;
+ struct ext4_group_desc swabdesc;

- /* Have to swab back to little-endian to do the checksum */
- memcpy(&swabdesc, desc, size);
- ext2fs_swap_group_desc2(fs,
- (struct ext2_group_desc *) &swabdesc);
- desc = (struct ext2_group_desc *) &swabdesc;
+ /* Have to swab back to little-endian to do the checksum */
+ memcpy(&swabdesc, desc, size);
+ ext2fs_swap_group_desc2(fs,
+ (struct ext2_group_desc *) &swabdesc);
+ desc = (struct ext2_group_desc *) &swabdesc;

- group = ext2fs_swab32(group);
+ group = ext2fs_swab32(group);
#endif
- crc = ext2fs_crc16(~0, fs->super->s_uuid,
- sizeof(fs->super->s_uuid));
- crc = ext2fs_crc16(crc, &group, sizeof(group));
- crc = ext2fs_crc16(crc, desc, offset);
- offset += sizeof(desc->bg_checksum); /* skip checksum */
- /* for checksum of struct ext4_group_desc do the rest...*/
- if (offset < size) {
- crc = ext2fs_crc16(crc, (char *)desc + offset,
- size - offset);
- }
+
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ /* new metadata csum code */
+ __u16 old_crc;
+ __u32 crc32;
+
+ old_crc = desc->bg_checksum;
+ desc->bg_checksum = 0;
+ crc32 = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&group,
+ sizeof(group));
+ crc32 = ext2fs_crc32c_le(crc32, (unsigned char *)desc,
+ size);
+ desc->bg_checksum = old_crc;
+
+ crc = crc32 & 0xFFFF;
+ goto out;
}

+ /* old crc16 code */
+ size_t offset = offsetof(struct ext2_group_desc, bg_checksum);
+ crc = ext2fs_crc16(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ crc = ext2fs_crc16(crc, &group, sizeof(group));
+ crc = ext2fs_crc16(crc, desc, offset);
+ offset += sizeof(desc->bg_checksum); /* skip checksum */
+ /* for checksum of struct ext4_group_desc do the rest...*/
+ if (offset < size) {
+ crc = ext2fs_crc16(crc, (char *)desc + offset,
+ size - offset);
+ }
+
+out:
return crc;
}

int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group)
{
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+ if (ext2fs_has_group_desc_csum(fs) &&
(ext2fs_bg_checksum(fs, group) !=
ext2fs_group_desc_csum(fs, group)))
return 0;
@@ -738,8 +755,7 @@ int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group)

void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group)
{
- if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ if (!ext2fs_has_group_desc_csum(fs))
return;

/* ext2fs_bg_checksum_set() sets the actual checksum field but
@@ -773,8 +789,7 @@ errcode_t ext2fs_set_gdt_csum(ext2_filsys fs)
if (!fs->inode_map)
return EXT2_ET_NO_INODE_BITMAP;

- if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ if (!ext2fs_has_group_desc_csum(fs))
return 0;

for (i = 0; i < fs->group_desc_count; i++) {
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 9574525..eeac04d 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -642,6 +642,12 @@ typedef struct stat ext2fs_struct_stat;
/*
* function prototypes
*/
+static inline int ext2fs_has_group_desc_csum(ext2_filsys fs)
+{
+ return EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
+}

/* alloc.c */
extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode,
diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index a63ea18..a22cab4 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -435,8 +435,7 @@ ipg_retry:
* bitmaps will be accounted for when allocated).
*/
free_blocks = 0;
- csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ csum_flag = ext2fs_has_group_desc_csum(fs);
for (i = 0; i < fs->group_desc_count; i++) {
/*
* Don't set the BLOCK_UNINIT group for the last group
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 74703c5..3e6d853 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -157,8 +157,7 @@ errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
scan->current_group);
scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
scan->blocks_left = scan->fs->inode_blocks_per_group;
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+ if (ext2fs_has_group_desc_csum(fs)) {
scan->inodes_left -=
ext2fs_bg_itable_unused(fs, scan->current_group);
scan->blocks_left =
@@ -183,8 +182,7 @@ errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
}
if (scan->fs->badblocks && scan->fs->badblocks->num)
scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ if (ext2fs_has_group_desc_csum(fs))
scan->scan_flags |= EXT2_SF_DO_LAZY;
*ret_scan = scan;
return 0;
@@ -250,8 +248,7 @@ static errcode_t get_next_blockgroup(ext2_inode_scan scan)
scan->bytes_left = 0;
scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super);
scan->blocks_left = fs->inode_blocks_per_group;
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+ if (ext2fs_has_group_desc_csum(fs)) {
scan->inodes_left -=
ext2fs_bg_itable_unused(fs, scan->current_group);
scan->blocks_left =
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index d2b64f4..2dc9b94 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -382,8 +382,7 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
* If recovery is from backup superblock, Clear _UNININT flags &
* reset bg_itable_unused to zero
*/
- if (superblock > 1 && EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+ if (superblock > 1 && ext2fs_has_group_desc_csum(fs)) {
dgrp_t group;

for (group = 0; group < fs->group_desc_count; group++) {
diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
index a5097c1..18e18aa 100644
--- a/lib/ext2fs/rw_bitmaps.c
+++ b/lib/ext2fs/rw_bitmaps.c
@@ -36,7 +36,7 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
unsigned int nbits;
errcode_t retval;
char *block_buf = NULL, *inode_buf = NULL;
- int csum_flag = 0;
+ int csum_flag;
blk64_t blk;
blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
ext2_ino_t ino_itr = 1;
@@ -46,9 +46,7 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
if (!(fs->flags & EXT2_FLAG_RW))
return EXT2_ET_RO_FILSYS;

- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
- csum_flag = 1;
+ csum_flag = ext2fs_has_group_desc_csum(fs);

inode_nbytes = block_nbytes = 0;
if (do_block) {
@@ -168,7 +166,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
errcode_t retval;
int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8;
- int csum_flag = 0;
+ int csum_flag;
int do_image = fs->flags & EXT2_FLAG_IMAGE_FILE;
unsigned int cnt;
blk64_t blk;
@@ -181,9 +179,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)

fs->write_bitmaps = ext2fs_write_bitmaps;

- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
- csum_flag = 1;
+ csum_flag = ext2fs_has_group_desc_csum(fs);

retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
if (retval)
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 23dec30..40398a7 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -114,7 +114,7 @@ static void print_bg_opts(ext2_filsys fs, dgrp_t i)
{
int first = 1, bg_flags = 0;

- if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM)
+ if (ext2fs_has_group_desc_csum(fs))
bg_flags = ext2fs_bg_flags(fs, i);

print_bg_opt(bg_flags, EXT2_BG_INODE_UNINIT, "INODE_UNINIT",
@@ -190,7 +190,7 @@ static void list_desc (ext2_filsys fs)
print_range(first_block, last_block);
fputs(")", stdout);
print_bg_opts(fs, i);
- if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM)
+ if (ext2fs_has_group_desc_csum(fs))
printf(_(" Checksum 0x%04x, unused inodes %u\n"),
ext2fs_bg_checksum(fs, i),
ext2fs_bg_itable_unused(fs, i));
diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index dc2805d..8a02ff4 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -191,8 +191,7 @@ static void fix_uninit_block_bitmaps(ext2_filsys fs)
int old_desc_blocks;
dgrp_t g;

- if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)))
+ if (!ext2fs_has_group_desc_csum(fs))
return;

for (g=0; g < fs->group_desc_count; g++) {
@@ -482,8 +481,7 @@ retry:
group_block = fs->super->s_first_data_block +
old_fs->group_desc_count * fs->super->s_blocks_per_group;

- csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ csum_flag = ext2fs_has_group_desc_csum(fs);
adj = old_fs->group_desc_count;
max_group = fs->group_desc_count - adj;
if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
@@ -743,8 +741,7 @@ static void mark_fs_metablock(ext2_resize_t rfs,
} else if (IS_INODE_TB(fs, group, blk)) {
ext2fs_inode_table_loc_set(fs, group, 0);
rfs->needed_blocks++;
- } else if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+ } else if (ext2fs_has_group_desc_csum(fs) &&
(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) {
/*
* If the block bitmap is uninitialized, which means
@@ -804,8 +801,7 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
for (blk = ext2fs_blocks_count(fs->super);
blk < ext2fs_blocks_count(old_fs->super); blk++) {
g = ext2fs_group_of_blk2(fs, blk);
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+ if (ext2fs_has_group_desc_csum(fs) &&
ext2fs_bg_flags_test(old_fs, g, EXT2_BG_BLOCK_UNINIT)) {
/*
* The block bitmap is uninitialized, so skip


2012-03-07 00:01:49

by djwong

[permalink] [raw]
Subject: [PATCH 33/54] libext2fs: Record the checksum algorithm in use in the superblock

Record the type of checksum algorithm we're using for metadata in the
superblock, in case we ever want/need to change the algorithm.

Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/set_fields.c | 1 +
lib/e2p/ls.c | 15 ++++++++++++++-
lib/ext2fs/csum.c | 9 +++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/openfs.c | 12 ++++++++----
6 files changed, 36 insertions(+), 5 deletions(-)


diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index 08bfd8d..871bf20 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -151,6 +151,7 @@ static struct field_set_info super_fields[] = {
{ "grp_quota_inum", &set_sb.s_grp_quota_inum, NULL, 4, parse_uint },
{ "overhead_blocks", &set_sb.s_overhead_blocks, NULL, 4, parse_uint },
{ "checksum", &set_sb.s_checksum, NULL, 4, parse_uint },
+ { "checksum_type", &set_sb.s_checksum_type, NULL, 1, parse_uint },
{ 0, 0, 0, 0 }
};

diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index f05e16d..d2e84eb 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -196,6 +196,16 @@ static __u64 e2p_free_blocks_count(struct ext2_super_block *super)
#define EXT2_GOOD_OLD_REV 0
#endif

+static const char *checksum_type(__u8 type)
+{
+ switch (type) {
+ case EXT2_CRC32C_CHKSUM:
+ return "crc32c";
+ default:
+ return "unknown";
+ }
+}
+
void list_super2(struct ext2_super_block * sb, FILE *f)
{
int inode_blocks_per_group;
@@ -421,9 +431,12 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
fprintf(f, "Group quota inode: %u\n",
sb->s_grp_quota_inum);

- if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+ fprintf(f, "Checksum type: %s\n",
+ checksum_type(sb->s_checksum_type));
fprintf(f, "Checksum: 0x%08x\n",
sb->s_checksum);
+ }
}

void list_super (struct ext2_super_block * s)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 4d01bcd..11a24f0 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,15 @@
#define STATIC static
#endif

+int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ return sb->s_checksum_type == EXT2_CRC32C_CHKSUM;
+}
+
static __u32 ext2fs_superblock_csum(ext2_filsys fs, struct ext2_super_block *sb)
{
int offset = offsetof(struct ext2_super_block, s_checksum);
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 0fab4e0..01f9f7b 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -464,4 +464,7 @@ ec EXT2_ET_EXT_ATTR_CSUM_INVALID,
ec EXT2_ET_SB_CSUM_INVALID,
"Superblock checksum does not match superblock"

+ec EXT2_ET_UNKNOWN_CSUM,
+ "Unknown checksum algorithm"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 3c52f4b..9574525 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -952,6 +952,7 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+extern int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb);
extern errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
struct ext2_super_block *sb);
extern int ext2fs_superblock_csum_verify(ext2_filsys fs,
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 8b7e750..d2b64f4 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -193,10 +193,14 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
if (fs->orig_super)
memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE);

- if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
- !ext2fs_superblock_csum_verify(fs, fs->super)) {
- retval = EXT2_ET_SB_CSUM_INVALID;
- goto cleanup;
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
+ retval = 0;
+ if (!ext2fs_verify_csum_type(fs, fs->super))
+ retval = EXT2_ET_UNKNOWN_CSUM;
+ if (!ext2fs_superblock_csum_verify(fs, fs->super))
+ retval = EXT2_ET_SB_CSUM_INVALID;
+ if (retval)
+ goto cleanup;
}

#ifdef WORDS_BIGENDIAN


2012-03-07 00:02:13

by djwong

[permalink] [raw]
Subject: [PATCH 39/54] mke2fs: Write new group descriptors with the appropriate checksum

Update mke2fs to use the helper function to determine if group descriptors
should have checksums calculated. Since metadata_csum supersedes uninit_bg,
quietly drop uninit_bg if metadata_csum is set, so that older kernels don't
get confused.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/mke2fs.c | 14 +++++++++++---
1 files changed, 11 insertions(+), 3 deletions(-)


diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 92b80e9..61b332a 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -1936,6 +1936,13 @@ profile_error:
if (extended_opts)
parse_extended_opts(&fs_param, extended_opts);

+ /* Don't allow user to set both metadata_csum and uninit_bg bits. */
+ if ((fs_param.s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ (fs_param.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ fs_param.s_feature_ro_compat &=
+ ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
+
/* Since sparse_super is the default, we would only have a problem
* here if it was explicitly disabled.
*/
@@ -2048,7 +2055,8 @@ static int should_do_undo(const char *name)
int csum_flag, force_undo;

csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(&fs_param,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
force_undo = get_int_from_profile(fs_types, "force_undo", 0);
if (!force_undo && (!csum_flag || !lazy_itable_init))
return 0;
@@ -2325,6 +2333,7 @@ int main (int argc, char *argv[])
(fs_param.s_feature_ro_compat &
(EXT4_FEATURE_RO_COMPAT_HUGE_FILE|EXT4_FEATURE_RO_COMPAT_GDT_CSUM|
EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)))
fs->super->s_kbytes_written = 1;

@@ -2472,8 +2481,7 @@ int main (int argc, char *argv[])
* inodes as unused; we want e2fsck to consider all
* inodes as potentially containing recoverable data.
*/
- if (fs->super->s_feature_ro_compat &
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
+ if (ext2fs_has_group_desc_csum(fs)) {
for (i = 1; i < fs->group_desc_count; i++)
ext2fs_bg_itable_unused_set(fs, i, 0);
}


2012-03-07 00:02:07

by djwong

[permalink] [raw]
Subject: [PATCH 37/54] e2fsck: Ensure block group checksum uses metadata_csum algorithm

Use the helper function to determine if group descriptors have a checksum.
Ensure that metadata_csum and uninit_bg flags are not set simultaneously, as
part of pass 0.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass5.c | 18 ++++++------------
e2fsck/problem.c | 9 +++++++++
e2fsck/problem.h | 5 +++++
e2fsck/super.c | 16 ++++++++++++++--
e2fsck/unix.c | 2 +-
5 files changed, 35 insertions(+), 15 deletions(-)


diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index f1ce6d7..c5dba0b 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -88,7 +88,7 @@ static void check_inode_bitmap_checksum(e2fsck_t ctx)
int nbytes;
ext2_ino_t ino_itr;
errcode_t retval;
- int csum_flag = 0;
+ int csum_flag;

/* If bitmap is dirty from being fixed, checksum will be corrected */
if (ext2fs_test_ib_dirty(ctx->fs))
@@ -103,9 +103,7 @@ static void check_inode_bitmap_checksum(e2fsck_t ctx)
fatal_error(ctx, 0);
}

- if (EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
- csum_flag = 1;
+ csum_flag = ext2fs_has_group_desc_csum(ctx->fs);

clear_problem_context(&pctx);
for (i = 0; i < ctx->fs->group_desc_count; i++) {
@@ -149,7 +147,7 @@ static void check_block_bitmap_checksum(e2fsck_t ctx)
int nbytes;
blk64_t blk_itr;
errcode_t retval;
- int csum_flag = 0;
+ int csum_flag;

/* If bitmap is dirty from being fixed, checksum will be corrected */
if (ext2fs_test_bb_dirty(ctx->fs))
@@ -164,9 +162,7 @@ static void check_block_bitmap_checksum(e2fsck_t ctx)
fatal_error(ctx, 0);
}

- if (EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
- csum_flag = 1;
+ csum_flag = ext2fs_has_group_desc_csum(ctx->fs);

clear_problem_context(&pctx);
for (i = 0; i < ctx->fs->group_desc_count; i++) {
@@ -322,8 +318,7 @@ static void check_block_bitmaps(e2fsck_t ctx)
goto errout;
}

- csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ csum_flag = ext2fs_has_group_desc_csum(fs);
redo_counts:
had_problem = 0;
save_problem = 0;
@@ -599,8 +594,7 @@ static void check_inode_bitmaps(e2fsck_t ctx)
goto errout;
}

- csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ csum_flag = ext2fs_has_group_desc_csum(fs);
redo_counts:
had_problem = 0;
save_problem = 0;
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index ad6887e..8c55721 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -423,6 +423,15 @@ static struct e2fsck_problem problem_table[] = {
N_("@S has invalid MMP magic. "),
PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},

+ /*
+ * metadata_csum implies uninit_bg; both feature bits cannot
+ * be set simultaneously.
+ */
+ { PR_0_META_AND_GDT_CSUM_SET,
+ N_("@S metadata_csum supersedes uninit_bg; both feature "
+ "bits cannot be set simultaneously."),
+ PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},
+
/* Pass 1 errors */

/* Pass 1: Checking inodes, blocks, and sizes */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index b8a7548..e47984c 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -242,6 +242,11 @@ struct problem_context {
/* Superblock has invalid MMP magic. */
#define PR_0_MMP_INVALID_MAGIC 0x000043

+/*
+ * metadata_csum supersedes uninit_bg; both feature bits cannot be set
+ * simultaneously.
+ */
+#define PR_0_META_AND_GDT_CSUM_SET 0x000044

/*
* Pass 1 errors
diff --git a/e2fsck/super.c b/e2fsck/super.c
index dbd337c..939a71e 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -577,14 +577,26 @@ void check_super_block(e2fsck_t ctx)
}
}

+ /* Are metadata_csum and uninit_bg both set? */
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+ fix_problem(ctx, PR_0_META_AND_GDT_CSUM_SET, &pctx)) {
+ fs->super->s_feature_ro_compat &=
+ ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
+ ext2fs_mark_super_dirty(fs);
+ for (i = 0; i < fs->group_desc_count; i++)
+ ext2fs_group_desc_csum_set(fs, i);
+ }
+
/*
* Verify the group descriptors....
*/
first_block = sb->s_first_data_block;
last_block = ext2fs_blocks_count(sb)-1;

- csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ csum_flag = ext2fs_has_group_desc_csum(fs);
for (i = 0; i < fs->group_desc_count; i++) {
pctx.group = i;

diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index a8e3775..678ce9b 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1653,7 +1653,7 @@ no_journal:
}

if ((run_result & E2F_FLAG_CANCEL) == 0 &&
- sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM &&
+ ext2fs_has_group_desc_csum(ctx->fs) &&
!(ctx->options & E2F_OPT_READONLY)) {
retval = ext2fs_set_gdt_csum(ctx->fs);
if (retval) {


2012-03-07 00:02:26

by djwong

[permalink] [raw]
Subject: [PATCH 41/54] libext2fs: Add checksum to MMP block

Calculate and verify a checksum of the MMP block.

Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/debugfs.c | 1 +
debugfs/set_fields.c | 1 +
lib/ext2fs/csum.c | 34 ++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2fs.h | 3 +++
lib/ext2fs/mmp.c | 19 +++++++++++++++++--
lib/ext2fs/swapfs.c | 1 +
7 files changed, 60 insertions(+), 2 deletions(-)


diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 2e46cd5..9c8e48e 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -2218,6 +2218,7 @@ void do_dump_mmp(int argc EXT2FS_ATTR((unused)), char *argv[])
fprintf(stdout, "node_name: %s\n", mmp_s->mmp_nodename);
fprintf(stdout, "device_name: %s\n", mmp_s->mmp_bdevname);
fprintf(stdout, "magic: 0x%x\n", mmp_s->mmp_magic);
+ fprintf(stdout, "checksum: 0x%08x\n", mmp_s->mmp_checksum);
}

static int source_file(const char *cmd_file, int sci_idx)
diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index 871bf20..986e404 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -256,6 +256,7 @@ static struct field_set_info mmp_fields[] = {
{ "bdevname", &set_mmp.mmp_bdevname, NULL, sizeof(set_mmp.mmp_bdevname),
parse_string },
{ "check_interval", &set_mmp.mmp_check_interval, NULL, 2, parse_uint },
+ { "checksum", &set_mmp.mmp_checksum, NULL, 4, parse_uint },
};

static int check_suffix(const char *field)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index d4f98e0..425f736 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,40 @@
#define STATIC static
#endif

+static __u32 ext2fs_mmp_csum(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ int offset = offsetof(struct mmp_struct, mmp_checksum);
+
+ return ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)mmp, offset);
+}
+
+int ext2fs_mmp_csum_verify(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ __u32 calculated;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ calculated = ext2fs_mmp_csum(fs, mmp);
+
+ return ext2fs_le32_to_cpu(mmp->mmp_checksum) == calculated;
+}
+
+errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ __u32 crc;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ crc = ext2fs_mmp_csum(fs, mmp);
+ mmp->mmp_checksum = ext2fs_cpu_to_le32(crc);
+
+ return 0;
+}
+
int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb)
{
if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 01f9f7b..a42ed9a 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -467,4 +467,7 @@ ec EXT2_ET_SB_CSUM_INVALID,
ec EXT2_ET_UNKNOWN_CSUM,
"Unknown checksum algorithm"

+ec EXT2_ET_MMP_CSUM_INVALID,
+ "MMP block checksum does not match MMP block"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index eeac04d..49d9271 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -958,6 +958,8 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
+extern errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp);
+extern int ext2fs_mmp_csum_verify(ext2_filsys, struct mmp_struct *mmp);
extern int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb);
extern errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
struct ext2_super_block *sb);
@@ -1451,6 +1453,7 @@ errcode_t ext2fs_mmp_clear(ext2_filsys fs);
errcode_t ext2fs_mmp_init(ext2_filsys fs);
errcode_t ext2fs_mmp_start(ext2_filsys fs);
errcode_t ext2fs_mmp_update(ext2_filsys fs);
+errcode_t ext2fs_mmp_update2(ext2_filsys fs, int immediately);
errcode_t ext2fs_mmp_stop(ext2_filsys fs);
unsigned ext2fs_mmp_new_seq(void);

diff --git a/lib/ext2fs/mmp.c b/lib/ext2fs/mmp.c
index b27d9a4..20a98f5 100644
--- a/lib/ext2fs/mmp.c
+++ b/lib/ext2fs/mmp.c
@@ -91,6 +91,11 @@ errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf)
}

mmp_cmp = fs->mmp_cmp;
+
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_mmp_csum_verify(fs, mmp_cmp))
+ retval = EXT2_ET_MMP_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
ext2fs_swap_mmp(mmp_cmp);
#endif
@@ -125,6 +130,10 @@ errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf)
ext2fs_swap_mmp(mmp_s);
#endif

+ retval = ext2fs_mmp_csum_set(fs, mmp_s);
+ if (retval)
+ return retval;
+
/* I was tempted to make this use O_DIRECT and the mmp_fd, but
* this caused no end of grief, while leaving it as-is works. */
retval = io_channel_write_blk64(fs->io, mmp_blk, -(int)sizeof(struct mmp_struct), buf);
@@ -381,7 +390,7 @@ mmp_error:
/*
* Update the on-disk mmp buffer, after checking that it hasn't been changed.
*/
-errcode_t ext2fs_mmp_update(ext2_filsys fs)
+errcode_t ext2fs_mmp_update2(ext2_filsys fs, int immediately)
{
struct mmp_struct *mmp, *mmp_cmp;
struct timeval tv;
@@ -392,7 +401,8 @@ errcode_t ext2fs_mmp_update(ext2_filsys fs)
return 0;

gettimeofday(&tv, 0);
- if (tv.tv_sec - fs->mmp_last_written < EXT2_MIN_MMP_UPDATE_INTERVAL)
+ if (!immediately &&
+ tv.tv_sec - fs->mmp_last_written < EXT2_MIN_MMP_UPDATE_INTERVAL)
return 0;

retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, NULL);
@@ -412,3 +422,8 @@ errcode_t ext2fs_mmp_update(ext2_filsys fs)
mmp_error:
return retval;
}
+
+errcode_t ext2fs_mmp_update(ext2_filsys fs)
+{
+ return ext2fs_mmp_update2(fs, 0);
+}
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index de178a4..1295e81 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -349,6 +349,7 @@ void ext2fs_swap_mmp(struct mmp_struct *mmp)
mmp->mmp_seq = ext2fs_swab32(mmp->mmp_seq);
mmp->mmp_time = ext2fs_swab64(mmp->mmp_time);
mmp->mmp_check_interval = ext2fs_swab16(mmp->mmp_check_interval);
+ mmp->mmp_checksum = ext2fs_swab32(mmp->mmp_checksum);
}

errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)


2012-03-07 00:02:42

by djwong

[permalink] [raw]
Subject: [PATCH 34/54] tune2fs: Store checksum algorithm type in superblock

Actually records the checksum algorithm type in the superblock when enabling
checksumming.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)


diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 2ae436e..ac5e945 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -701,6 +701,12 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
ext2fs_mark_bb_dirty(fs);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ fs->super->s_checksum_type = EXT2_CRC32C_CHKSUM;
+ else
+ fs->super->s_checksum_type = 0;
+ ext2fs_mark_super_dirty(fs);
}

/*


2012-03-07 00:02:46

by djwong

[permalink] [raw]
Subject: [PATCH 38/54] tune2fs: Rewrite block group checksums when changing checksumming feature flags

When toggling the metadata_csum and uninit_bg feature flags, we should rewrite
the block groups with the desired checksum.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 116 +++++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 90 insertions(+), 26 deletions(-)


diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index ac5e945..79b49d9 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -693,8 +693,12 @@ static void rewrite_inodes(ext2_filsys fs)

static void rewrite_metadata_checksums(ext2_filsys fs)
{
+ int i;
+
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
ext2fs_init_csum_seed(fs);
+ for (i = 0; i < fs->group_desc_count; i++)
+ ext2fs_group_desc_csum_set(fs, i);
rewrite_inodes(fs);
ext2fs_read_bitmaps(fs);
ext2fs_mark_ib_dirty(fs);
@@ -709,6 +713,49 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
ext2fs_mark_super_dirty(fs);
}

+static void enable_uninit_bg(ext2_filsys fs)
+{
+ struct ext2_group_desc *gd;
+ int i;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ gd = ext2fs_group_desc(fs, fs->group_desc, i);
+ gd->bg_itable_unused = 0;
+ gd->bg_flags = EXT2_BG_INODE_ZEROED;
+ ext2fs_group_desc_csum_set(fs, i);
+ }
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+}
+
+static void disable_uninit_bg(ext2_filsys fs, __u32 csum_feature_flag)
+{
+ struct ext2_group_desc *gd;
+ int i;
+
+ /* Load bitmaps to ensure that the uninit ones get written out */
+ fs->super->s_feature_ro_compat |= csum_feature_flag;
+ ext2fs_read_bitmaps(fs);
+ ext2fs_mark_ib_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
+ fs->super->s_feature_ro_compat &= ~csum_feature_flag;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ gd = ext2fs_group_desc(fs, fs->group_desc, i);
+ if ((gd->bg_flags & EXT2_BG_INODE_ZEROED) == 0) {
+ /*
+ * XXX what we really should do is zap
+ * uninitialized inode tables instead.
+ */
+ request_fsck_afterwards(fs);
+ break;
+ }
+ gd->bg_itable_unused = 0;
+ gd->bg_flags = 0;
+ ext2fs_group_desc_csum_set(fs, i);
+ }
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+}
+
/*
* Update the feature set as provided by the user.
*/
@@ -886,6 +933,21 @@ mmp_error:
if (check_fsck_needed(fs))
exit(1);
rewrite_checksums = 1;
+ /* metadata_csum supersedes uninit_bg */
+ fs->super->s_feature_ro_compat &=
+ ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
+
+ /* if uninit_bg was previously off, rewrite group desc */
+ if (!(old_features[E2P_FEATURE_RO_INCOMPAT] &
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ enable_uninit_bg(fs);
+
+ /*
+ * Since metadata_csum supersedes uninit_bg, pretend like
+ * uninit_bg has been off all along.
+ */
+ old_features[E2P_FEATURE_RO_INCOMPAT] &=
+ ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
}

if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
@@ -893,37 +955,40 @@ mmp_error:
if (check_fsck_needed(fs))
exit(1);
rewrite_checksums = 1;
+ /*
+ * If we're turning off metadata_csum and not turning on
+ * uninit_bg, rewrite group desc.
+ */
+ if (!(fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ disable_uninit_bg(fs,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
+ else
+ /*
+ * metadata_csum previously provided uninit_bg, so if
+ * we're also setting the uninit_bg feature bit,
+ * pretend like it was previously enabled. Checksums
+ * will be rewritten with crc16 later.
+ */
+ old_features[E2P_FEATURE_RO_INCOMPAT] |=
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
}

if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
- for (i = 0; i < fs->group_desc_count; i++) {
- gd = ext2fs_group_desc(fs, fs->group_desc, i);
- gd->bg_itable_unused = 0;
- gd->bg_flags = EXT2_BG_INODE_ZEROED;
- ext2fs_group_desc_csum_set(fs, i);
- }
- fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ /* Do not enable uninit_bg when metadata_csum enabled */
+ if (fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ fs->super->s_feature_ro_compat &=
+ ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
+ else
+ enable_uninit_bg(fs);
}

if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
- for (i = 0; i < fs->group_desc_count; i++) {
- gd = ext2fs_group_desc(fs, fs->group_desc, i);
- if ((gd->bg_flags & EXT2_BG_INODE_ZEROED) == 0) {
- /*
- * XXX what we really should do is zap
- * uninitialized inode tables instead.
- */
- request_fsck_afterwards(fs);
- break;
- }
- gd->bg_itable_unused = 0;
- gd->bg_flags = 0;
- gd->bg_checksum = 0;
- }
- fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
- }
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ disable_uninit_bg(fs,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM);

if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_QUOTA)) {
@@ -2505,8 +2570,7 @@ retry_open:
exit(1);
}

- if (sb->s_feature_ro_compat &
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
+ if (ext2fs_has_group_desc_csum(fs)) {
/*
* Determine if the block group checksums are
* correct so we know whether or not to set


2012-03-07 00:02:55

by djwong

[permalink] [raw]
Subject: [PATCH 43/54] tune2fs: Force MMP update when changing metadata_csum flag

When changing the metadata_csum flag, always force out a new MMP block.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)


diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 79b49d9..bec2b6c 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -703,6 +703,7 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
ext2fs_read_bitmaps(fs);
ext2fs_mark_ib_dirty(fs);
ext2fs_mark_bb_dirty(fs);
+ ext2fs_mmp_update2(fs, 1);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,


2012-03-07 00:02:52

by djwong

[permalink] [raw]
Subject: [PATCH 45/54] libext2fs: Dump feature flags for jbd2 v2 checksums

Modify the dump code to print information about jbd2 v2 checksum data.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/e2p/feature.c | 2 ++
misc/dumpe2fs.c | 29 +++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 0 deletions(-)


diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
index 19e6f0c..486f846 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -98,6 +98,8 @@ static struct feature jrnl_feature_list[] = {
"journal_incompat_revoke" },
{ E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT,
"journal_async_commit" },
+ { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_CSUM_V2,
+ "journal_checksum_v2" },
{ 0, 0, 0 },
};

diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 40398a7..3ceb0f8 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -311,6 +311,16 @@ static void list_bad_blocks(ext2_filsys fs, int dump)
ext2fs_badblocks_list_free(bb_list);
}

+static char *journal_checksum_type_str(__u8 type)
+{
+ switch (type) {
+ case JBD2_CRC32C_CHKSUM:
+ return "crc32c";
+ default:
+ return "unknown";
+ }
+}
+
static void print_inline_journal_information(ext2_filsys fs)
{
journal_superblock_t *jsb;
@@ -377,6 +387,15 @@ static void print_inline_journal_information(ext2_filsys fs)
(unsigned int)ntohl(jsb->s_maxlen),
(unsigned int)ntohl(jsb->s_sequence),
(unsigned int)ntohl(jsb->s_start));
+ if (jsb->s_feature_compat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM))
+ printf(_("Journal checksum type: crc32\n"));
+ if (jsb->s_feature_incompat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2))
+ printf(_("Journal checksum type: %s\n"
+ "Journal checksum: 0x%08x\n"),
+ journal_checksum_type_str(jsb->s_checksum_type),
+ ext2fs_be32_to_cpu(jsb->s_checksum));
}

static void print_journal_information(ext2_filsys fs)
@@ -402,6 +421,16 @@ static void print_journal_information(ext2_filsys fs)
exit(1);
}

+ if (jsb->s_feature_compat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM))
+ printf(_("Journal checksum type: crc32\n"));
+ if (jsb->s_feature_incompat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2))
+ printf(_("Journal checksum type: %s\n"
+ "Journal checksum: 0x%08x\n"),
+ journal_checksum_type_str(jsb->s_checksum_type),
+ ext2fs_be32_to_cpu(jsb->s_checksum));
+
printf(_("\nJournal block size: %u\n"
"Journal length: %u\n"
"Journal first block: %u\n"


2012-03-07 00:03:10

by djwong

[permalink] [raw]
Subject: [PATCH 35/54] mke2fs: Record the checksum algorithm in use in the superblock

Record the type of checksum algorithm we're using for metadata in the
superblock when creating a filesystem.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/mke2fs.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)


diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 28485e3..92b80e9 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -2422,6 +2422,10 @@ int main (int argc, char *argv[])
sizeof(fs->super->s_last_mounted));
}

+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ fs->super->s_checksum_type = EXT2_CRC32C_CHKSUM;
+
if (!quiet || noaction)
show_stats(fs);



2012-03-07 00:03:11

by djwong

[permalink] [raw]
Subject: [PATCH 44/54] libext2fs: Change on-disk journal layout to support metadata checksumming

Define flags and change journal structure definitions to support v2 journal
checksumming.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/kernel-jbd.h | 30 +++++++++++++++++++++++++++---
1 files changed, 27 insertions(+), 3 deletions(-)


diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index 066c031..570bfa6 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -114,12 +114,24 @@ typedef struct journal_header_s
#define JBD2_CRC32_CHKSUM 1
#define JBD2_MD5_CHKSUM 2
#define JBD2_SHA1_CHKSUM 3
+#define JBD2_CRC32C_CHKSUM 4

#define JBD2_CRC32_CHKSUM_SIZE 4

#define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32))
/*
* Commit block header for storing transactional checksums:
+ *
+ * NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum*
+ * fields are used to store a checksum of the descriptor and data blocks.
+ *
+ * If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum
+ * field is used to store crc32c(uuid+commit_block). Each journal metadata
+ * block gets its own checksum, and data block checksums are stored in
+ * journal_block_tag (in the descriptor). The other h_chksum* fields are
+ * not used.
+ *
+ * Checksum v1 and v2 are mutually exclusive features.
*/
struct commit_header {
__u32 h_magic;
@@ -141,11 +153,17 @@ typedef struct journal_block_tag_s
__u32 t_blocknr; /* The on-disk block number */
__u32 t_flags; /* See below */
__u32 t_blocknr_high; /* most-significant high 32bits. */
+ __u32 t_checksum; /* crc32c(uuid+seq+block) */
} journal_block_tag_t;

#define JBD_TAG_SIZE64 (sizeof(journal_block_tag_t))
#define JBD_TAG_SIZE32 (8)

+/* Tail of descriptor block, for checksumming */
+struct journal_block_tail {
+ __u32 t_checksum;
+};
+
/*
* The revoke descriptor: used on disk to describe a series of blocks to
* be revoked from the log
@@ -156,6 +174,10 @@ typedef struct journal_revoke_header_s
int r_count; /* Count of bytes used in the block */
} journal_revoke_header_t;

+/* Tail of revoke block, for checksumming */
+struct journal_revoke_tail {
+ __u32 r_checksum;
+};

/* Definitions for the journal tag flags word: */
#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */
@@ -205,7 +227,10 @@ typedef struct journal_superblock_s
__u32 s_max_trans_data; /* Limit of data blocks per trans. */

/* 0x0050 */
- __u32 s_padding[44];
+ __u8 s_checksum_type; /* checksum type */
+ __u8 s_padding2[3];
+ __u32 s_padding[42];
+ __u32 s_checksum; /* crc32c(superblock) */

/* 0x0100 */
__u8 s_users[16*48]; /* ids of all fs'es sharing the log */
@@ -224,11 +249,10 @@ typedef struct journal_superblock_s

#define JFS_FEATURE_COMPAT_CHECKSUM 0x00000001

-#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001

2012-03-07 00:03:17

by djwong

[permalink] [raw]
Subject: [PATCH 40/54] mke2fs: Warn if not enabling all the features that metadata_csum wants

The metadata_csum feature works best when two features are enabled. These
features are "extents" (because the block map has no space for checksums) and
"64bit" (this enables storage of full 32-bit checksums in certain fields).
Print a warning if the user tries to create a filesystem without those
features.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/mke2fs.c | 19 +++++++++++++++++++
1 files changed, 19 insertions(+), 0 deletions(-)


diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 61b332a..e8c60ca 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -2309,6 +2309,25 @@ int main (int argc, char *argv[])
exit(1);
}

+ /* Check the user's mkfs options for metadata checksumming */
+ if (!quiet &&
+ EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT3_FEATURE_INCOMPAT_EXTENTS))
+ printf(_("Extents are not enabled. The file extent "
+ "tree can be checksummed, whereas block maps "
+ "cannot. Not enabling extents reduces the "
+ "coverage of metadata checksumming. "
+ "Pass -O extents to rectify.\n"));
+ if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_64BIT))
+ printf(_("64-bit filesystem support is not "
+ "enabled. The larger fields afforded by "
+ "this feature enable full-strength "
+ "checksumming. Pass -O 64bit to rectify.\n"));
+ }
+
/* Can't undo discard ... */
if (!noaction && discard && (io_ptr != undo_io_manager)) {
retval = mke2fs_discard_device(fs);


2012-03-07 00:03:14

by djwong

[permalink] [raw]
Subject: [PATCH 46/54] e2fsck: Check journal superblock checksum prior to recovery

Ensure that the journal superblock passes checksum before recovering the
filesystem.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/journal.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 59 insertions(+), 0 deletions(-)


diff --git a/e2fsck/journal.c b/e2fsck/journal.c
index 915b8bb..2ac2e54 100644
--- a/e2fsck/journal.c
+++ b/e2fsck/journal.c
@@ -40,6 +40,53 @@ static int bh_count = 0;
*/
#undef USE_INODE_IO

+/* Checksumming functions */
+int e2fsck_journal_verify_csum_type(journal_t *j, journal_superblock_t *jsb)
+{
+ if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+ return 1;
+
+ return jsb->s_checksum_type == JBD2_CRC32C_CHKSUM;
+}
+
+static __u32 e2fsck_journal_sb_csum(journal_t *j, journal_superblock_t *jsb)
+{
+ __u32 crc, old_crc;
+
+ old_crc = jsb->s_checksum;
+ jsb->s_checksum = 0;
+ crc = ext2fs_crc32c_le(~0, (unsigned char *)jsb,
+ sizeof(journal_superblock_t));
+ jsb->s_checksum = old_crc;
+
+ return crc;
+}
+
+int e2fsck_journal_sb_csum_verify(journal_t *j, journal_superblock_t *jsb)
+{
+ __u32 provided, calculated;
+
+ if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+ return 1;
+
+ provided = ext2fs_be32_to_cpu(jsb->s_checksum);
+ calculated = e2fsck_journal_sb_csum(j, jsb);
+
+ return provided == calculated;
+}
+
+errcode_t e2fsck_journal_sb_csum_set(journal_t *j, journal_superblock_t *jsb)
+{
+ __u32 crc;
+
+ if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+ return 0;
+
+ crc = e2fsck_journal_sb_csum(j, jsb);
+ jsb->s_checksum = ext2fs_cpu_to_be32(crc);
+ return 0;
+}
+
/* Kernel compatibility functions for handling the journal. These allow us
* to use the recovery.c file virtually unchanged from the kernel, so we
* don't have to do much to keep kernel and user recovery in sync.
@@ -560,6 +607,15 @@ static errcode_t e2fsck_journal_load(journal_t *journal)
if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
return EXT2_ET_RO_UNSUPP_FEATURE;

+ /* Checksum v1 and v2 are mutually exclusive features. */
+ if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_CSUM_V2) &&
+ JFS_HAS_COMPAT_FEATURE(journal, JFS_FEATURE_COMPAT_CHECKSUM))
+ return EXT2_ET_CORRUPT_SUPERBLOCK;
+
+ if (!e2fsck_journal_verify_csum_type(journal, jsb) ||
+ !e2fsck_journal_sb_csum_verify(journal, jsb))
+ return EXT2_ET_CORRUPT_SUPERBLOCK;
+
/* We have now checked whether we know enough about the journal
* format to be able to proceed safely, so any other checks that
* fail we should attempt to recover from. */
@@ -627,6 +683,7 @@ static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
for (i = 0; i < 4; i ++)
new_seq ^= u.val[i];
jsb->s_sequence = htonl(new_seq);
+ e2fsck_journal_sb_csum_set(journal, jsb);

mark_buffer_dirty(journal->j_sb_buffer);
ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
@@ -667,6 +724,7 @@ static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
jsb->s_sequence = htonl(journal->j_transaction_sequence);
if (reset)
jsb->s_start = 0; /* this marks the journal as empty */
+ e2fsck_journal_sb_csum_set(journal, jsb);
mark_buffer_dirty(journal->j_sb_buffer);
}
brelse(journal->j_sb_buffer);
@@ -843,6 +901,7 @@ static errcode_t recover_ext3_journal(e2fsck_t ctx)
ctx->fs->super->s_state |= EXT2_ERROR_FS;
ext2fs_mark_super_dirty(ctx->fs);
journal->j_superblock->s_errno = 0;
+ e2fsck_journal_sb_csum_set(journal, journal->j_superblock);
mark_buffer_dirty(journal->j_sb_buffer);
}



2012-03-07 00:03:13

by djwong

[permalink] [raw]
Subject: [PATCH 42/54] e2fsck: Verify and correct MMP checksum problems

Check and handle MMP checksum problems by resetting the block.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/problem.c | 5 +++++
e2fsck/problem.h | 3 +++
e2fsck/unix.c | 5 +++++
3 files changed, 13 insertions(+), 0 deletions(-)


diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 8c55721..91617bc 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -432,6 +432,11 @@ static struct e2fsck_problem problem_table[] = {
"bits cannot be set simultaneously."),
PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},

+ /* Superblock has invalid MMP checksum. */
+ { PR_0_MMP_CSUM_INVALID,
+ N_("@S MMP block checksum does not match MMP block. "),
+ PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},
+
/* Pass 1 errors */

/* Pass 1: Checking inodes, blocks, and sizes */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index e47984c..f6f1eee 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -248,6 +248,9 @@ struct problem_context {
*/
#define PR_0_META_AND_GDT_CSUM_SET 0x000044

+/* Superblock has invalid MMP checksum. */
+#define PR_0_MMP_CSUM_INVALID 0x000045
+
/*
* Pass 1 errors
*/
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 678ce9b..d3fb8f8 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1098,6 +1098,11 @@ check_error:
ext2fs_mmp_clear(fs);
retval = 0;
}
+ } else if (retval == EXT2_ET_MMP_CSUM_INVALID) {
+ if (fix_problem(ctx, PR_0_MMP_CSUM_INVALID, &pctx)) {
+ ext2fs_mmp_clear(fs);
+ retval = 0;
+ }
}
return retval;
}


2012-03-07 00:03:35

by djwong

[permalink] [raw]
Subject: [PATCH 49/54] e2fsck: Check commit block checksum during recovery

When recovering a journal with checksum v2, verify the commit block checksum.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/recovery.c | 33 +++++++++++++++++++++++++++++++++
1 files changed, 33 insertions(+), 0 deletions(-)


diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index f76e7d8..8f55411 100644
--- a/e2fsck/recovery.c
+++ b/e2fsck/recovery.c
@@ -375,6 +375,26 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh,
return 0;
}

+static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
+{
+ struct commit_header *h;
+ __u32 provided, calculated;
+
+ if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+ return 1;
+
+ h = buf;
+ provided = h->h_chksum[0];
+ h->h_chksum[0] = 0;
+ calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid,
+ sizeof(j->j_superblock->s_uuid));
+ calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize);
+ h->h_chksum[0] = provided;
+
+ provided = ext2fs_be32_to_cpu(provided);
+ return provided == calculated;
+}
+
static int do_one_pass(journal_t *journal,
struct recovery_info *info, enum passtype pass)
{
@@ -696,6 +716,19 @@ static int do_one_pass(journal_t *journal,
}
crc32_sum = ~0;
}
+ if (pass == PASS_SCAN &&
+ !jbd2_commit_block_csum_verify(journal,
+ bh->b_data)) {
+ info->end_transaction = next_commit_ID;
+
+ if (!JFS_HAS_INCOMPAT_FEATURE(journal,
+ JFS_FEATURE_INCOMPAT_ASYNC_COMMIT)) {
+ journal->j_failed_commit =
+ next_commit_ID;
+ brelse(bh);
+ break;
+ }
+ }
brelse(bh);
next_commit_ID++;
continue;


2012-03-07 00:03:36

by djwong

[permalink] [raw]
Subject: [PATCH 50/54] e2fsck: Verify data block checksums when recovering journal

Check the data block checksums when recovering the journal.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/jfs_user.h | 11 -----------
e2fsck/recovery.c | 32 ++++++++++++++++++++++++++++++++
lib/ext2fs/kernel-jbd.h | 30 ++++++++++++++++++++++++++++++
3 files changed, 62 insertions(+), 11 deletions(-)


diff --git a/e2fsck/jfs_user.h b/e2fsck/jfs_user.h
index 9e33306..4baba54 100644
--- a/e2fsck/jfs_user.h
+++ b/e2fsck/jfs_user.h
@@ -104,17 +104,6 @@ _INLINE_ void do_cache_destroy(lkmem_cache_t *cache)
free(cache);
}

-/*
- * helper functions to deal with 32 or 64bit block numbers.
- */
-_INLINE_ size_t journal_tag_bytes(journal_t *journal)
-{
- if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT))
- return JBD_TAG_SIZE64;
- else
- return JBD_TAG_SIZE32;
-}
-
#undef _INLINE_
#endif

diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index 8f55411..d7c470e 100644
--- a/e2fsck/recovery.c
+++ b/e2fsck/recovery.c
@@ -395,6 +395,25 @@ static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
return provided == calculated;
}

+static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag,
+ void *buf, __u32 sequence)
+{
+ __u32 provided, calculated;
+
+ if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+ return 1;
+
+ sequence = ext2fs_cpu_to_be32(sequence);
+ calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid,
+ sizeof(j->j_superblock->s_uuid));
+ calculated = ext2fs_crc32c_le(calculated, (__u8 *)&sequence,
+ sizeof(sequence));
+ calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize);
+ provided = ext2fs_be32_to_cpu(tag->t_checksum);
+
+ return provided == ext2fs_cpu_to_be32(calculated);
+}
+
static int do_one_pass(journal_t *journal,
struct recovery_info *info, enum passtype pass)
{
@@ -575,6 +594,19 @@ static int do_one_pass(journal_t *journal,
goto skip_write;
}

+ /* Look for block corruption */
+ if (!jbd2_block_tag_csum_verify(
+ journal, tag, obh->b_data,
+ be32_to_cpu(tmp->h_sequence))) {
+ brelse(obh);
+ success = -EIO;
+ printk(KERN_ERR "JBD: Invalid "
+ "checksum recovering "
+ "block %ld in log\n",
+ blocknr);
+ continue;
+ }
+
/* Find a buffer for the new
* data being restored */
nbh = __getblk(journal->j_fs_dev,
diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index 570bfa6..e9d725d 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -261,6 +261,36 @@ typedef struct journal_superblock_s
JFS_FEATURE_INCOMPAT_ASYNC_COMMIT|\
JFS_FEATURE_INCOMPAT_64BIT)

+#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
+#ifdef E2FSCK_INCLUDE_INLINE_FUNCS
+#define _INLINE_ extern
+#else
+#ifdef __GNUC__
+#define _INLINE_ extern __inline__
+#else /* For Watcom C */
+#define _INLINE_ extern inline
+#endif
+#endif
+
+/*
+ * helper functions to deal with 32 or 64bit block numbers.
+ */
+_INLINE_ size_t journal_tag_bytes(journal_t *journal)
+{
+ journal_block_tag_t tag;
+ size_t x = 0;
+
+ if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_CSUM_V2))
+ x += sizeof(tag.t_checksum);
+
+ if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT))
+ return x + JBD_TAG_SIZE64;
+ else
+ return x + JBD_TAG_SIZE32;
+}
+#undef _INLINE_
+#endif
+
#ifdef __KERNEL__

#include <linux/fs.h>


2012-03-07 00:03:47

by djwong

[permalink] [raw]
Subject: [PATCH 53/54] e2fsck: Refactor crc32_be code

Remove crc32_be in favor of the implementation in libext2fs.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/Makefile.in | 38 ---
e2fsck/crc32.c | 570 -----------------------------------------------
e2fsck/crc32defs.h | 64 -----
e2fsck/e2fsck.h | 3
e2fsck/gen_crc32table.c | 101 --------
e2fsck/recovery.c | 8 -
6 files changed, 10 insertions(+), 774 deletions(-)
delete mode 100644 e2fsck/crc32.c
delete mode 100644 e2fsck/crc32defs.h
delete mode 100644 e2fsck/gen_crc32table.c


diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in
index b5336a4..26a93cd 100644
--- a/e2fsck/Makefile.in
+++ b/e2fsck/Makefile.in
@@ -64,7 +64,7 @@ COMPILE_ET=$(top_builddir)/lib/et/compile_et --build-tree
#
#MCHECK= -DMCHECK

-OBJS= crc32.o dict.o unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o \
+OBJS= dict.o unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o \
pass3.o pass4.o pass5.o journal.o badblocks.o util.o dirinfo.o \
dx_dirinfo.o ehandler.o problem.o message.o quota.o recovery.o \
region.o revoke.o ea_refcount.o rehash.o profile.o prof_err.o \
@@ -78,11 +78,9 @@ PROFILED_OBJS= profiled/dict.o profiled/unix.o profiled/e2fsck.o \
profiled/message.o profiled/problem.o profiled/quota.o \
profiled/recovery.o profiled/region.o profiled/revoke.o \
profiled/ea_refcount.o profiled/rehash.o profiled/profile.o \
- profiled/crc32.o profiled/prof_err.o profiled/sigcatcher.o
+ profiled/prof_err.o profiled/sigcatcher.o

SRCS= $(srcdir)/e2fsck.c \
- $(srcdir)/crc32.c \
- $(srcdir)/gen_crc32table.c \
$(srcdir)/dict.c \
$(srcdir)/super.c \
$(srcdir)/pass1.c \
@@ -132,15 +130,6 @@ e2fsck.profiled: $(PROFILED_OBJS) $(PROFILED_DEPLIBS)
$(Q) $(LD) $(ALL_LDFLAGS) -g -pg -o e2fsck.profiled $(PROFILED_OBJS) \
$(PROFILED_LIBS)

-gen_crc32table: $(srcdir)/gen_crc32table.c
- $(E) " CC $@"
- $(Q) $(BUILD_CC) $(BUILD_CFLAGS) -o gen_crc32table \
- $(srcdir)/gen_crc32table.c
-
-crc32table.h: gen_crc32table
- $(E) " GEN32TABLE $@"
- $(Q) ./gen_crc32table > crc32table.h
-
tst_sigcatcher: $(srcdir)/sigcatcher.c
$(Q) $(CC) $(BUILD_LDFLAGS) $(ALL_CFLAGS) $(RDYNAMIC) \
$(srcdir)/sigcatcher.c -DDEBUG -o tst_sigcatcher
@@ -151,10 +140,6 @@ tst_problem: $(srcdir)/problem.c $(srcdir)/problem.h $(LIBEXT2FS) \
$(srcdir)/problem.c -DUNITTEST $(LIBEXT2FS) $(LIBCOM_ERR) \
$(LIBINTL)

-tst_crc32: $(srcdir)/crc32.c $(LIBEXT2FS) $(DEPLIBCOM_ERR)
- $(Q) $(CC) $(BUILD_LDFLAGS) $(ALL_CFLAGS) -o tst_crc32 $(srcdir)/crc32.c \
- -DUNITTEST $(LIBEXT2FS) $(LIBCOM_ERR)
-
tst_refcount: ea_refcount.c $(DEPLIBCOM_ERR)
$(E) " LD $@"
$(Q) $(CC) -o tst_refcount $(srcdir)/ea_refcount.c \
@@ -165,10 +150,9 @@ tst_region: region.c $(DEPLIBCOM_ERR)
$(Q) $(CC) -o tst_region $(srcdir)/region.c \
$(ALL_CFLAGS) -DTEST_PROGRAM $(LIBCOM_ERR)

-check:: tst_refcount tst_region tst_crc32 tst_problem
+check:: tst_refcount tst_region tst_problem
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_refcount
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_region
- LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_crc32
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_problem

extend: extend.o
@@ -265,9 +249,8 @@ uninstall:
clean:
$(RM) -f $(PROGS) \#* *\# *.s *.o *.a *~ core e2fsck.static \
e2fsck.shared e2fsck.profiled flushb e2fsck.8 \
- tst_problem tst_crc32 tst_region tst_refcount gen_crc32table \
- crc32table.h e2fsck.conf.5 prof_err.c prof_err.h \
- test_profile
+ tst_problem tst_region tst_refcount e2fsck.conf.5 \
+ prof_err.c prof_err.h test_profile
$(RM) -rf profiled

mostlyclean: clean
@@ -288,17 +271,6 @@ e2fsck.o: $(srcdir)/e2fsck.c $(top_builddir)/lib/config.h $(srcdir)/e2fsck.h \
$(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/quota/mkquota.h \
$(top_srcdir)/lib/quota/quota.h $(top_srcdir)/lib/../e2fsck/dict.h \
$(srcdir)/problem.h
-crc32.o: $(srcdir)/crc32.c $(top_builddir)/lib/config.h $(srcdir)/e2fsck.h \
- $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
- $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
- $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
- $(top_builddir)/lib/ext2fs/ext2_err.h \
- $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
- $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/quota/mkquota.h \
- $(top_srcdir)/lib/quota/quota.h $(top_srcdir)/lib/../e2fsck/dict.h \
- $(srcdir)/crc32defs.h crc32table.h
-gen_crc32table.o: $(srcdir)/gen_crc32table.c $(top_builddir)/lib/config.h \
- $(srcdir)/crc32defs.h
dict.o: $(srcdir)/dict.c $(top_builddir)/lib/config.h $(srcdir)/dict.h
super.o: $(srcdir)/super.c $(top_builddir)/lib/config.h $(srcdir)/e2fsck.h \
$(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
diff --git a/e2fsck/crc32.c b/e2fsck/crc32.c
deleted file mode 100644
index 0497a38..0000000
--- a/e2fsck/crc32.c
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * crc32.c --- CRC32 function
- *
- * Copyright (C) 2008 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-/*
- * Oct 15, 2000 Matt Domsch <[email protected]>
- * Nicer crc32 functions/docs submitted by [email protected]. Thanks!
- * Code was from the public domain, copyright abandoned. Code was
- * subsequently included in the kernel, thus was re-licensed under the
- * GNU GPL v2.
- *
- * Oct 12, 2000 Matt Domsch <[email protected]>
- * Same crc32 function was used in 5 other places in the kernel.
- * I made one version, and deleted the others.
- * There are various incantations of crc32(). Some use a seed of 0 or ~0.
- * Some xor at the end with ~0. The generic crc32() function takes
- * seed as an argument, and doesn't xor at the end. Then individual
- * users can do whatever they need.
- * drivers/net/smc9194.c uses seed ~0, doesn't xor with ~0.
- * fs/jffs2 uses seed 0, doesn't xor with ~0.
- * fs/partitions/efi.c uses seed ~0, xor's with ~0.
- *
- * This source code is licensed under the GNU General Public License,
- * Version 2. See the file COPYING for more details.
- */
-
-#include "config.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-
-#ifdef UNITTEST
-#undef ENABLE_NLS
-#endif
-#include "e2fsck.h"
-
-#include "crc32defs.h"
-#if CRC_LE_BITS == 8
-#define tole(x) __constant_cpu_to_le32(x)
-#define tobe(x) __constant_cpu_to_be32(x)
-#else
-#define tole(x) (x)
-#define tobe(x) (x)
-#endif
-#include "crc32table.h"
-
-#ifdef UNITTEST
-
-/**
- * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
- * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
- * other uses, or the previous crc32 value if computing incrementally.
- * @p: pointer to buffer over which CRC is run
- * @len: length of buffer @p
- */
-__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len);
-
-#if CRC_LE_BITS == 1
-/*
- * In fact, the table-based code will work in this case, but it can be
- * simplified by inlining the table in ?: form.
- */
-
-__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len)
-{
- int i;
- while (len--) {
- crc ^= *p++;
- for (i = 0; i < 8; i++)
- crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
- }
- return crc;
-}
-#else /* Table-based approach */
-
-__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len)
-{
-# if CRC_LE_BITS == 8
- const __u32 *b =(__u32 *)p;
- const __u32 *tab = crc32table_le;
-
-# ifdef WORDS_BIGENDIAN
-# define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8)
-# else
-# define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8)
-# endif
-
- crc = __cpu_to_le32(crc);
- /* Align it */
- if(unlikely(((long)b)&3 && len)){
- do {
- __u8 *p = (__u8 *)b;
- DO_CRC(*p++);
- b = (void *)p;
- } while ((--len) && ((long)b)&3 );
- }
- if(likely(len >= 4)){
- /* load data 32 bits wide, xor data 32 bits wide. */
- size_t save_len = len & 3;
- len = len >> 2;
- --b; /* use pre increment below(*++b) for speed */
- do {
- crc ^= *++b;
- DO_CRC(0);
- DO_CRC(0);
- DO_CRC(0);
- DO_CRC(0);
- } while (--len);
- b++; /* point to next byte(s) */
- len = save_len;
- }
- /* And the last few bytes */
- if(len){
- do {
- __u8 *p = (__u8 *)b;
- DO_CRC(*p++);
- b = (void *)p;
- } while (--len);
- }
-
- return __le32_to_cpu(crc);
-#undef ENDIAN_SHIFT
-#undef DO_CRC
-
-# elif CRC_LE_BITS == 4
- while (len--) {
- crc ^= *p++;
- crc = (crc >> 4) ^ crc32table_le[crc & 15];
- crc = (crc >> 4) ^ crc32table_le[crc & 15];
- }
- return crc;
-# elif CRC_LE_BITS == 2
- while (len--) {
- crc ^= *p++;
- crc = (crc >> 2) ^ crc32table_le[crc & 3];
- crc = (crc >> 2) ^ crc32table_le[crc & 3];
- crc = (crc >> 2) ^ crc32table_le[crc & 3];
- crc = (crc >> 2) ^ crc32table_le[crc & 3];
- }
- return crc;
-# endif
-}
-#endif
-
-#endif /* UNITTEST */
-
-/**
- * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
- * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
- * other uses, or the previous crc32 value if computing incrementally.
- * @p: pointer to buffer over which CRC is run
- * @len: length of buffer @p
- */
-__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len);
-
-#if CRC_BE_BITS == 1
-/*
- * In fact, the table-based code will work in this case, but it can be
- * simplified by inlining the table in ?: form.
- */
-
-__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len)
-{
- int i;
- while (len--) {
- crc ^= *p++ << 24;
- for (i = 0; i < 8; i++)
- crc =
- (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE :
- 0);
- }
- return crc;
-}
-
-#else /* Table-based approach */
-__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len)
-{
-# if CRC_BE_BITS == 8
- const __u32 *b =(const __u32 *)p;
- const __u32 *tab = crc32table_be;
-
-# ifdef WORDS_BIGENDIAN
-# define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8)
-# else
-# define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8)
-# endif
-
- crc = __cpu_to_be32(crc);
- /* Align it */
- if(unlikely(((long)b)&3 && len)){
- do {
- const __u8 *q = (const __u8 *)b;
- DO_CRC(*q++);
- b = (const __u32 *)q;
- } while ((--len) && ((long)b)&3 );
- }
- if(likely(len >= 4)){
- /* load data 32 bits wide, xor data 32 bits wide. */
- size_t save_len = len & 3;
- len = len >> 2;
- --b; /* use pre increment below(*++b) for speed */
- do {
- crc ^= *++b;
- DO_CRC(0);
- DO_CRC(0);
- DO_CRC(0);
- DO_CRC(0);
- } while (--len);
- b++; /* point to next byte(s) */
- len = save_len;
- }
- /* And the last few bytes */
- if(len){
- do {
- const __u8 *q = (const __u8 *)b;
- DO_CRC(*q++);
- b = (const void *)q;
- } while (--len);
- }
- return __be32_to_cpu(crc);
-#undef ENDIAN_SHIFT
-#undef DO_CRC
-
-# elif CRC_BE_BITS == 4
- while (len--) {
- crc ^= *p++ << 24;
- crc = (crc << 4) ^ crc32table_be[crc >> 28];
- crc = (crc << 4) ^ crc32table_be[crc >> 28];
- }
- return crc;
-# elif CRC_BE_BITS == 2
- while (len--) {
- crc ^= *p++ << 24;
- crc = (crc << 2) ^ crc32table_be[crc >> 30];
- crc = (crc << 2) ^ crc32table_be[crc >> 30];
- crc = (crc << 2) ^ crc32table_be[crc >> 30];
- crc = (crc << 2) ^ crc32table_be[crc >> 30];
- }
- return crc;
-# endif
-}
-#endif
-
-/*
- * A brief CRC tutorial.
- *
- * A CRC is a long-division remainder. You add the CRC to the message,
- * and the whole thing (message+CRC) is a multiple of the given
- * CRC polynomial. To check the CRC, you can either check that the
- * CRC matches the recomputed value, *or* you can check that the
- * remainder computed on the message+CRC is 0. This latter approach
- * is used by a lot of hardware implementations, and is why so many
- * protocols put the end-of-frame flag after the CRC.
- *
- * It's actually the same long division you learned in school, except that
- * - We're working in binary, so the digits are only 0 and 1, and
- * - When dividing polynomials, there are no carries. Rather than add and
- * subtract, we just xor. Thus, we tend to get a bit sloppy about
- * the difference between adding and subtracting.
- *
- * A 32-bit CRC polynomial is actually 33 bits long. But since it's
- * 33 bits long, bit 32 is always going to be set, so usually the CRC
- * is written in hex with the most significant bit omitted. (If you're
- * familiar with the IEEE 754 floating-point format, it's the same idea.)
- *
- * Note that a CRC is computed over a string of *bits*, so you have
- * to decide on the endianness of the bits within each byte. To get
- * the best error-detecting properties, this should correspond to the
- * order they're actually sent. For example, standard RS-232 serial is
- * little-endian; the most significant bit (sometimes used for parity)
- * is sent last. And when appending a CRC word to a message, you should
- * do it in the right order, matching the endianness.
- *
- * Just like with ordinary division, the remainder is always smaller than
- * the divisor (the CRC polynomial) you're dividing by. Each step of the
- * division, you take one more digit (bit) of the dividend and append it
- * to the current remainder. Then you figure out the appropriate multiple
- * of the divisor to subtract to being the remainder back into range.
- * In binary, it's easy - it has to be either 0 or 1, and to make the
- * XOR cancel, it's just a copy of bit 32 of the remainder.
- *
- * When computing a CRC, we don't care about the quotient, so we can
- * throw the quotient bit away, but subtract the appropriate multiple of
- * the polynomial from the remainder and we're back to where we started,
- * ready to process the next bit.
- *
- * A big-endian CRC written this way would be coded like:
- * for (i = 0; i < input_bits; i++) {
- * multiple = remainder & 0x80000000 ? CRCPOLY : 0;
- * remainder = (remainder << 1 | next_input_bit()) ^ multiple;
- * }
- * Notice how, to get at bit 32 of the shifted remainder, we look
- * at bit 31 of the remainder *before* shifting it.
- *
- * But also notice how the next_input_bit() bits we're shifting into
- * the remainder don't actually affect any decision-making until
- * 32 bits later. Thus, the first 32 cycles of this are pretty boring.
- * Also, to add the CRC to a message, we need a 32-bit-long hole for it at
- * the end, so we have to add 32 extra cycles shifting in zeros at the
- * end of every message,
- *
- * So the standard trick is to rearrage merging in the next_input_bit()
- * until the moment it's needed. Then the first 32 cycles can be precomputed,
- * and merging in the final 32 zero bits to make room for the CRC can be
- * skipped entirely.
- * This changes the code to:
- * for (i = 0; i < input_bits; i++) {
- * remainder ^= next_input_bit() << 31;
- * multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
- * remainder = (remainder << 1) ^ multiple;
- * }
- * With this optimization, the little-endian code is simpler:
- * for (i = 0; i < input_bits; i++) {
- * remainder ^= next_input_bit();
- * multiple = (remainder & 1) ? CRCPOLY : 0;
- * remainder = (remainder >> 1) ^ multiple;
- * }
- *
- * Note that the other details of endianness have been hidden in CRCPOLY
- * (which must be bit-reversed) and next_input_bit().
- *
- * However, as long as next_input_bit is returning the bits in a sensible
- * order, we can actually do the merging 8 or more bits at a time rather
- * than one bit at a time:
- * for (i = 0; i < input_bytes; i++) {
- * remainder ^= next_input_byte() << 24;
- * for (j = 0; j < 8; j++) {
- * multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
- * remainder = (remainder << 1) ^ multiple;
- * }
- * }
- * Or in little-endian:
- * for (i = 0; i < input_bytes; i++) {
- * remainder ^= next_input_byte();
- * for (j = 0; j < 8; j++) {
- * multiple = (remainder & 1) ? CRCPOLY : 0;
- * remainder = (remainder << 1) ^ multiple;
- * }
- * }
- * If the input is a multiple of 32 bits, you can even XOR in a 32-bit
- * word at a time and increase the inner loop count to 32.
- *
- * You can also mix and match the two loop styles, for example doing the
- * bulk of a message byte-at-a-time and adding bit-at-a-time processing
- * for any fractional bytes at the end.
- *
- * The only remaining optimization is to the byte-at-a-time table method.
- * Here, rather than just shifting one bit of the remainder to decide
- * in the correct multiple to subtract, we can shift a byte at a time.
- * This produces a 40-bit (rather than a 33-bit) intermediate remainder,
- * but again the multiple of the polynomial to subtract depends only on
- * the high bits, the high 8 bits in this case.
- *
- * The multiple we need in that case is the low 32 bits of a 40-bit
- * value whose high 8 bits are given, and which is a multiple of the
- * generator polynomial. This is simply the CRC-32 of the given
- * one-byte message.
- *
- * Two more details: normally, appending zero bits to a message which
- * is already a multiple of a polynomial produces a larger multiple of that
- * polynomial. To enable a CRC to detect this condition, it's common to
- * invert the CRC before appending it. This makes the remainder of the
- * message+crc come out not as zero, but some fixed non-zero value.
- *
- * The same problem applies to zero bits prepended to the message, and
- * a similar solution is used. Instead of starting with a remainder of
- * 0, an initial remainder of all ones is used. As long as you start
- * the same way on decoding, it doesn't make a difference.
- */
-
-#ifdef UNITTEST
-
-#include <stdlib.h>
-#include <stdio.h>
-
-const __u8 byte_rev_table[256] = {
- 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
- 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
- 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
- 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
- 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
- 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
- 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
- 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
- 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
- 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
- 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
- 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
- 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
- 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
- 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
- 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
- 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
- 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
- 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
- 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
- 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
- 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
- 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
- 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
- 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
- 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
- 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
- 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
- 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
- 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
- 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
- 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
-};
-
-static inline __u8 bitrev8(__u8 byte)
-{
- return byte_rev_table[byte];
-}
-
-static inline __u16 bitrev16(__u16 x)
-{
- return (bitrev8(x & 0xff) << 8) | bitrev8(x >> 8);
-}
-
-/**
- * bitrev32 - reverse the order of bits in a u32 value
- * @x: value to be bit-reversed
- */
-static __u32 bitrev32(__u32 x)
-{
- return (bitrev16(x & 0xffff) << 16) | bitrev16(x >> 16);
-}
-
-#if 0 /*Not used at present */
-
-static void
-buf_dump(char const *prefix, unsigned char const *buf, size_t len)
-{
- fputs(prefix, stdout);
- while (len--)
- printf(" %02x", *buf++);
- putchar('\n');
-
-}
-#endif
-
-static void bytereverse(unsigned char *buf, size_t len)
-{
- while (len--) {
- unsigned char x = bitrev8(*buf);
- *buf++ = x;
- }
-}
-
-static void random_garbage(unsigned char *buf, size_t len)
-{
- while (len--)
- *buf++ = (unsigned char) random();
-}
-
-#if 0 /* Not used at present */
-static void store_le(__u32 x, unsigned char *buf)
-{
- buf[0] = (unsigned char) x;
- buf[1] = (unsigned char) (x >> 8);
- buf[2] = (unsigned char) (x >> 16);
- buf[3] = (unsigned char) (x >> 24);
-}
-#endif
-
-static void store_be(__u32 x, unsigned char *buf)
-{
- buf[0] = (unsigned char) (x >> 24);
- buf[1] = (unsigned char) (x >> 16);
- buf[2] = (unsigned char) (x >> 8);
- buf[3] = (unsigned char) x;
-}
-
-/*
- * This checks that CRC(buf + CRC(buf)) = 0, and that
- * CRC commutes with bit-reversal. This has the side effect
- * of bytewise bit-reversing the input buffer, and returns
- * the CRC of the reversed buffer.
- */
-static __u32 test_step(__u32 init, unsigned char *buf, size_t len)
-{
- __u32 crc1, crc2;
- size_t i;
-
- crc1 = crc32_be(init, buf, len);
- store_be(crc1, buf + len);
- crc2 = crc32_be(init, buf, len + 4);
- if (crc2)
- printf("\nCRC cancellation fail: 0x%08x should be 0\n",
- crc2);
-
- for (i = 0; i <= len + 4; i++) {
- crc2 = crc32_be(init, buf, i);
- crc2 = crc32_be(crc2, buf + i, len + 4 - i);
- if (crc2)
- printf("\nCRC split fail: 0x%08x\n", crc2);
- }
-
- /* Now swap it around for the other test */
-
- bytereverse(buf, len + 4);
- init = bitrev32(init);
- crc2 = bitrev32(crc1);
- if (crc1 != bitrev32(crc2))
- printf("\nBit reversal fail: 0x%08x -> 0x%08x -> 0x%08x\n",
- crc1, crc2, bitrev32(crc2));
- crc1 = crc32_le(init, buf, len);
- if (crc1 != crc2)
- printf("\nCRC endianness fail: 0x%08x != 0x%08x\n", crc1,
- crc2);
- crc2 = crc32_le(init, buf, len + 4);
- if (crc2)
- printf("\nCRC cancellation fail: 0x%08x should be 0\n",
- crc2);
-
- for (i = 0; i <= len + 4; i++) {
- crc2 = crc32_le(init, buf, i);
- crc2 = crc32_le(crc2, buf + i, len + 4 - i);
- if (crc2)
- printf("\nCRC split fail: 0x%08x\n", crc2);
- }
-
- return crc1;
-}
-
-#define SIZE 64
-#define INIT1 0
-#define INIT2 0
-
-int main(int argc, char **argv)
-{
- unsigned char buf1[SIZE + 4];
- unsigned char buf2[SIZE + 4];
- unsigned char buf3[SIZE + 4];
- int i, j;
- __u32 crc1, crc2, crc3;
- int exit_status = 0;
-
- for (i = 0; i <= SIZE; i++) {
- printf("\rTesting length %d...", i);
- fflush(stdout);
- random_garbage(buf1, i);
- random_garbage(buf2, i);
- for (j = 0; j < i; j++)
- buf3[j] = buf1[j] ^ buf2[j];
-
- crc1 = test_step(INIT1, buf1, i);
- crc2 = test_step(INIT2, buf2, i);
- /* Now check that CRC(buf1 ^ buf2) = CRC(buf1) ^ CRC(buf2) */
- crc3 = test_step(INIT1 ^ INIT2, buf3, i);
- if (crc3 != (crc1 ^ crc2)) {
- printf("CRC XOR fail: 0x%08x != 0x%08x ^ 0x%08x\n",
- crc3, crc1, crc2);
- exit_status++;
- }
- }
- printf("\nAll test complete. No failures expected.\n");
- return 0;
-}
-
-#endif /* UNITTEST */
diff --git a/e2fsck/crc32defs.h b/e2fsck/crc32defs.h
deleted file mode 100644
index 27414d2..0000000
--- a/e2fsck/crc32defs.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * There are multiple 16-bit CRC polynomials in common use, but this is
- * *the* standard CRC-32 polynomial, first popularized by Ethernet.
- * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
- */
-#define CRCPOLY_LE 0xedb88320
-#define CRCPOLY_BE 0x04c11db7
-
-/* How many bits at a time to use. Requires a table of 4<<CRC_xx_BITS bytes. */
-/* For less performance-sensitive, use 4 */
-#ifndef CRC_LE_BITS
-# define CRC_LE_BITS 8
-#endif
-#ifndef CRC_BE_BITS
-# define CRC_BE_BITS 8
-#endif
-
-/*
- * Little-endian CRC computation. Used with serial bit streams sent
- * lsbit-first. Be sure to use cpu_to_le32() to append the computed CRC.
- */
-#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1
-# error CRC_LE_BITS must be a power of 2 between 1 and 8
-#endif
-
-/*
- * Big-endian CRC computation. Used with serial bit streams sent
- * msbit-first. Be sure to use cpu_to_be32() to append the computed CRC.
- */
-#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1
-# error CRC_BE_BITS must be a power of 2 between 1 and 8
-#endif
-
-#define ___constant_swab32(x) \
- ((__u32)( \
- (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
- (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \
- (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \
- (((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
-
-
-#ifdef WORDS_BIGENDIAN
-#define __constant_cpu_to_le32(x) ___constant_swab32((x))
-#define __constant_cpu_to_be32(x) (x)
-#define __be32_to_cpu(x) (x)
-#define __cpu_to_be32(x) (x)
-#define __cpu_to_le32(x) (ext2fs_swab32((x)))
-#define __le32_to_cpu(x) (ext2fs_swab32((x)))
-#else
-#define __constant_cpu_to_le32(x) (x)
-#define __constant_cpu_to_be32(x) ___constant_swab32((x))
-#define __be32_to_cpu(x) (ext2fs_swab32((x)))
-#define __cpu_to_be32(x) (ext2fs_swab32((x)))
-#define __cpu_to_le32(x) (x)
-#define __le32_to_cpu(x) (x)
-#endif
-
-#if (__GNUC__ >= 3)
-#define likely(x) __builtin_expect(!!(x), 1)
-#define unlikely(x) __builtin_expect(!!(x), 0)
-#else
-#define likely(x) (x)
-#define unlikely(x) (x)
-#endif
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index c6dab54..18c5f89 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -397,9 +397,6 @@ extern int e2fsck_run(e2fsck_t ctx);
extern void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
int replace_bad_blocks);

-/* crc32.c */
-extern __u32 crc32_be(__u32 crc, unsigned char const *p, size_t len);
-
/* dirinfo.c */
extern void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent);
extern void e2fsck_free_dir_info(e2fsck_t ctx);
diff --git a/e2fsck/gen_crc32table.c b/e2fsck/gen_crc32table.c
deleted file mode 100644
index 2c1aa8e..0000000
--- a/e2fsck/gen_crc32table.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * gen_crc32table.c --- Generate CRC32 tables.
- *
- * Copyright (C) 2008 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include "crc32defs.h"
-#include <inttypes.h>
-
-#define ENTRIES_PER_LINE 4
-
-#define LE_TABLE_SIZE (1 << CRC_LE_BITS)
-#define BE_TABLE_SIZE (1 << CRC_BE_BITS)
-
-static uint32_t crc32table_le[LE_TABLE_SIZE];
-static uint32_t crc32table_be[BE_TABLE_SIZE];
-
-/**
- * crc32init_le() - allocate and initialize LE table data
- *
- * crc is the crc of the byte i; other entries are filled in based on the
- * fact that crctable[i^j] = crctable[i] ^ crctable[j].
- *
- */
-static void crc32init_le(void)
-{
- unsigned i, j;
- uint32_t crc = 1;
-
- crc32table_le[0] = 0;
-
- for (i = 1 << (CRC_LE_BITS - 1); i; i >>= 1) {
- crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
- for (j = 0; j < LE_TABLE_SIZE; j += 2 * i)
- crc32table_le[i + j] = crc ^ crc32table_le[j];
- }
-}
-
-/**
- * crc32init_be() - allocate and initialize BE table data
- */
-static void crc32init_be(void)
-{
- unsigned i, j;
- uint32_t crc = 0x80000000;
-
- crc32table_be[0] = 0;
-
- for (i = 1; i < BE_TABLE_SIZE; i <<= 1) {
- crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
- for (j = 0; j < i; j++)
- crc32table_be[i + j] = crc ^ crc32table_be[j];
- }
-}
-
-static void output_table(uint32_t table[], int len, const char *trans)
-{
- int i;
-
- for (i = 0; i < len - 1; i++) {
- if (i % ENTRIES_PER_LINE == 0)
- printf("\n");
- printf("%s(0x%8.8xL), ", trans, table[i]);
- }
- printf("%s(0x%8.8xL)\n", trans, table[len - 1]);
-}
-
-#ifdef __GNUC__
-#define ATTR(x) __attribute__(x)
-#else
-#define ATTR(x)
-#endif
-
-int main(int argc ATTR((unused)), char** argv ATTR((unused)))
-{
- printf("/* this file is generated - do not edit */\n\n");
-
- printf("#ifdef UNITTEST\n");
- if (CRC_LE_BITS > 1) {
- crc32init_le();
- printf("static const __u32 crc32table_le[] = {");
- output_table(crc32table_le, LE_TABLE_SIZE, "tole");
- printf("};\n");
- }
- printf("#endif /* UNITTEST */\n");
-
- if (CRC_BE_BITS > 1) {
- crc32init_be();
- printf("static const __u32 crc32table_be[] = {");
- output_table(crc32table_be, BE_TABLE_SIZE, "tobe");
- printf("};\n");
- }
-
- return 0;
-}
diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index d7c470e..4a8d850 100644
--- a/e2fsck/recovery.c
+++ b/e2fsck/recovery.c
@@ -356,7 +356,8 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh,

num_blks = count_tags(journal, bh);
/* Calculate checksum of the descriptor block. */
- *crc32_sum = crc32_be(*crc32_sum, (void *)bh->b_data, bh->b_size);
+ *crc32_sum = ext2fs_crc32_be(*crc32_sum, (void *)bh->b_data,
+ bh->b_size);

for (i = 0; i < num_blks; i++) {
io_block = (*next_log_block)++;
@@ -367,8 +368,9 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh,
"%lu in log\n", err, io_block);
return 1;
} else {
- *crc32_sum = crc32_be(*crc32_sum, (void *)obh->b_data,
- obh->b_size);
+ *crc32_sum = ext2fs_crc32_be(*crc32_sum,
+ (void *)obh->b_data,
+ obh->b_size);
}
brelse(obh);
}


2012-03-07 00:04:00

by djwong

[permalink] [raw]
Subject: [PATCH 47/54] e2fsck: Check revoke block checksum during recovery

Verify the revoke block checksum when recovering the journal.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/recovery.c | 24 ++++++++++++++++++++++++
1 files changed, 24 insertions(+), 0 deletions(-)


diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index b669941..60c8ceb 100644
--- a/e2fsck/recovery.c
+++ b/e2fsck/recovery.c
@@ -715,6 +715,27 @@ static int do_one_pass(journal_t *journal,
return err;
}

+static int jbd2_revoke_block_csum_verify(journal_t *j,
+ void *buf)
+{
+ struct journal_revoke_tail *tail;
+ __u32 provided, calculated;
+
+ if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+ return 1;
+
+ tail = (struct journal_revoke_tail *)(buf + j->j_blocksize -
+ sizeof(struct journal_revoke_tail));
+ provided = tail->r_checksum;
+ tail->r_checksum = 0;
+ calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid,
+ sizeof(j->j_superblock->s_uuid));
+ calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize);
+ tail->r_checksum = provided;
+
+ provided = ext2fs_be32_to_cpu(provided);
+ return provided == calculated;
+}

/* Scan a revoke record, marking all blocks mentioned as revoked. */

@@ -729,6 +750,9 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
offset = sizeof(journal_revoke_header_t);
max = be32_to_cpu(header->r_count);

+ if (!jbd2_revoke_block_csum_verify(journal, header))
+ return -EINVAL;
+
if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT))
record_len = 8;



2012-03-07 00:03:53

by djwong

[permalink] [raw]
Subject: [PATCH 52/54] libext2fs: Bring the CRC32c implementation up to date with the kernel implementation

The crc32c implementation in the kernel has been refactored a bit to reduce the
amount of code that needs to be maintained, and to speed up tune2fs/e2fsck on
PowerPC by 5-10%. Port the crc32c changes over, and provide a crc32_be so that
we can remove the duplicate functionality from e2fsck. Also drop crc32c_be and
crc32_le since neither got used.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/crc32c.c | 692 +++++++++++++++---------------------------
lib/ext2fs/crc32c_defs.h | 12 +
lib/ext2fs/ext2fs.h | 2
lib/ext2fs/gen_crc32ctable.c | 96 +++---
4 files changed, 296 insertions(+), 506 deletions(-)


diff --git a/lib/ext2fs/crc32c.c b/lib/ext2fs/crc32c.c
index 6be4336..1ad068f 100644
--- a/lib/ext2fs/crc32c.c
+++ b/lib/ext2fs/crc32c.c
@@ -32,7 +32,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
-#define __force
#define min(x, y) ((x) > (y) ? (y) : (x))
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
@@ -58,400 +57,189 @@
#endif

#if CRC_LE_BITS > 8
-# define tole(x) (__force uint32_t) __constant_cpu_to_le32(x)
+# define tole(x) __constant_cpu_to_le32(x)
#else
# define tole(x) (x)
#endif

#if CRC_BE_BITS > 8
-# define tobe(x) (__force uint32_t) __constant_cpu_to_be32(x)
+# define tobe(x) __constant_cpu_to_be32(x)
#else
# define tobe(x) (x)
#endif

#include "crc32c_table.h"

-#if CRC_LE_BITS == 32
-/* slice by 4 algorithm */
-static uint32_t crc32c_le_body(uint32_t crc, uint8_t const *buf, size_t len)
-{
- const uint8_t *p8;
- const uint32_t *p32;
- size_t init_bytes;
- size_t words;
- size_t end_bytes;
- size_t i;
- uint32_t q;
- uint8_t i0, i1, i2, i3;
-
- crc = (__force uint32_t) __cpu_to_le32(crc);
-
- /* unroll loop into 'init_bytes' odd bytes followed by
- * 'words' aligned 4 byte words followed by
- * 'end_bytes' odd bytes at the end */
- p8 = buf;
- p32 = (uint32_t *)PTR_ALIGN(p8, 4);
- init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
- words = (len - init_bytes) >> 2;
- end_bytes = (len - init_bytes) & 3;
-
- for (i = 0; i < init_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
- i0 = *p8++ ^ crc;
- crc = t0_le[i0] ^ (crc >> 8);
-#else
- i0 = *p8++ ^ (crc >> 24);
- crc = t0_le[i0] ^ (crc << 8);
-#endif
- }
-
- /* using pre-increment below slightly faster */
- p32--;
-
- for (i = 0; i < words; i++) {
-#ifndef WORDS_BIGENDIAN
- q = *++p32 ^ crc;
- i3 = q;
- i2 = q >> 8;
- i1 = q >> 16;
- i0 = q >> 24;
- crc = t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0];
-#else
- q = *++p32 ^ crc;
- i3 = q >> 24;
- i2 = q >> 16;
- i1 = q >> 8;
- i0 = q;
- crc = t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0];
-#endif
- }
+#if CRC_LE_BITS > 8 || CRC_BE_BITS > 8

- p8 = (uint8_t *)(++p32);
-
- for (i = 0; i < end_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
- i0 = *p8++ ^ crc;
- crc = t0_le[i0] ^ (crc >> 8);
-#else
- i0 = *p8++ ^ (crc >> 24);
- crc = t0_le[i0] ^ (crc << 8);
-#endif
- }
-
- return __le32_to_cpu((__force __le32)crc);
-}
-#endif
-
-#if CRC_BE_BITS == 32
-static uint32_t crc32c_be_body(uint32_t crc, uint8_t const *buf, size_t len)
+/* implements slicing-by-4 or slicing-by-8 algorithm */
+static inline uint32_t
+crc32_body(uint32_t crc, unsigned char const *buf, size_t len,
+ const uint32_t (*tab)[256])
{
- const uint8_t *p8;
- const uint32_t *p32;
- size_t init_bytes;
- size_t words;
- size_t end_bytes;
- size_t i;
- uint32_t q;
- uint8_t i0, i1, i2, i3;
-
- crc = (__force uint32_t) __cpu_to_be32(crc);
-
- p8 = buf;
- p32 = (uint32_t *)PTR_ALIGN(p8, 4);
- init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
- words = (len - init_bytes) >> 2;
- end_bytes = (len - init_bytes) & 3;
-
- for (i = 0; i < init_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
- i0 = *p8++ ^ crc;
- crc = t0_be[i0] ^ (crc >> 8);
-#else
- i0 = *p8++ ^ (crc >> 24);
- crc = t0_be[i0] ^ (crc << 8);
-#endif
- }
-
- p32--;
-
- for (i = 0; i < words; i++) {
-#ifndef WORDS_BIGENDIAN
- q = *++p32 ^ crc;
- i3 = q;
- i2 = q >> 8;
- i1 = q >> 16;
- i0 = q >> 24;
- crc = t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0];
-#else
- q = *++p32 ^ crc;
- i3 = q >> 24;
- i2 = q >> 16;
- i1 = q >> 8;
- i0 = q;
- crc = t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0];
-#endif
- }
-
- p8 = (uint8_t *)(++p32);
-
- for (i = 0; i < end_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
- i0 = *p8++ ^ crc;
- crc = t0_be[i0] ^ (crc >> 8);
-#else
- i0 = *p8++ ^ (crc >> 24);
- crc = t0_be[i0] ^ (crc << 8);
-#endif
- }
-
- return __be32_to_cpu((__force __be32)crc);
-}
-#endif
-
-#if CRC_LE_BITS == 64
-/* slice by 8 algorithm */
-static uint32_t crc32c_le_body(uint32_t crc, uint8_t const *buf, size_t len)
-{
- const uint8_t *p8;
- const uint32_t *p32;
- size_t init_bytes;
- size_t words;
- size_t end_bytes;
- size_t i;
- uint32_t q;
- uint8_t i0, i1, i2, i3;
-
- crc = (__force uint32_t) __cpu_to_le32(crc);
-
- p8 = buf;
- p32 = (uint32_t *)PTR_ALIGN(p8, 8);
- init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
- words = (len - init_bytes) >> 3;
- end_bytes = (len - init_bytes) & 7;
-
- for (i = 0; i < init_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
- i0 = *p8++ ^ crc;
- crc = t0_le[i0] ^ (crc >> 8);
-#else
- i0 = *p8++ ^ (crc >> 24);
- crc = t0_le[i0] ^ (crc << 8);
-#endif
- }
-
- p32--;
-
- for (i = 0; i < words; i++) {
-#ifndef WORDS_BIGENDIAN
- q = *++p32 ^ crc;
- i3 = q;
- i2 = q >> 8;
- i1 = q >> 16;
- i0 = q >> 24;
- crc = t7_le[i3] ^ t6_le[i2] ^ t5_le[i1] ^ t4_le[i0];
-
- q = *++p32;
- i3 = q;
- i2 = q >> 8;
- i1 = q >> 16;
- i0 = q >> 24;
- crc ^= t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0];
-#else
- q = *++p32 ^ crc;
- i3 = q >> 24;
- i2 = q >> 16;
- i1 = q >> 8;
- i0 = q;
- crc = t7_le[i3] ^ t6_le[i2] ^ t5_le[i1] ^ t4_le[i0];
-
- q = *++p32;
- i3 = q >> 24;
- i2 = q >> 16;
- i1 = q >> 8;
- i0 = q;
- crc ^= t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0];
-#endif
- }
-
- p8 = (uint8_t *)(++p32);
-
- for (i = 0; i < end_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
- i0 = *p8++ ^ crc;
- crc = t0_le[i0] ^ (crc >> 8);
-#else
- i0 = *p8++ ^ (crc >> 24);
- crc = t0_le[i0] ^ (crc << 8);
-#endif
- }
-
- return __le32_to_cpu(crc);
-}
-#endif
-
-#if CRC_BE_BITS == 64
-static uint32_t crc32c_be_body(uint32_t crc, uint8_t const *buf, size_t len)
-{
- const uint8_t *p8;
- const uint32_t *p32;
- size_t init_bytes;
- size_t words;
- size_t end_bytes;
- size_t i;
+# ifndef WORDS_BIGENDIAN
+# define DO_CRC(x) (crc = t0[(crc ^ (x)) & 255] ^ (crc >> 8))
+# define DO_CRC4 (t3[(q) & 255] ^ t2[(q >> 8) & 255] ^ \
+ t1[(q >> 16) & 255] ^ t0[(q >> 24) & 255])
+# define DO_CRC8 (t7[(q) & 255] ^ t6[(q >> 8) & 255] ^ \
+ t5[(q >> 16) & 255] ^ t4[(q >> 24) & 255])
+# else
+# define DO_CRC(x) (crc = t0[((crc >> 24) ^ (x)) & 255] ^ (crc << 8))
+# define DO_CRC4 (t0[(q) & 255] ^ t1[(q >> 8) & 255] ^ \
+ t2[(q >> 16) & 255] ^ t3[(q >> 24) & 255])
+# define DO_CRC8 (t4[(q) & 255] ^ t5[(q >> 8) & 255] ^ \
+ t6[(q >> 16) & 255] ^ t7[(q >> 24) & 255])
+# endif
+ const uint32_t *b;
+ size_t rem_len;
+ const uint32_t *t0 = tab[0], *t1 = tab[1], *t2 = tab[2], *t3 = tab[3];
+ const uint32_t *t4 = tab[4], *t5 = tab[5], *t6 = tab[6], *t7 = tab[7];
uint32_t q;
- uint8_t i0, i1, i2, i3;
-
- crc = (__force uint32_t) __cpu_to_be32(crc);

- p8 = buf;
- p32 = (uint32_t *)PTR_ALIGN(p8, 8);
- init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
- words = (len - init_bytes) >> 3;
- end_bytes = (len - init_bytes) & 7;
-
- for (i = 0; i < init_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
- i0 = *p8++ ^ crc;
- crc = t0_be[i0] ^ (crc >> 8);
-#else
- i0 = *p8++ ^ (crc >> 24);
- crc = t0_be[i0] ^ (crc << 8);
-#endif
+ /* Align it */
+ if (unlikely((long)buf & 3 && len)) {
+ do {
+ DO_CRC(*buf++);
+ } while ((--len) && ((long)buf)&3);
}

- p32--;
-
- for (i = 0; i < words; i++) {
-#ifndef WORDS_BIGENDIAN
- q = *++p32 ^ crc;
- i3 = q;
- i2 = q >> 8;
- i1 = q >> 16;
- i0 = q >> 24;
- crc = t7_be[i3] ^ t6_be[i2] ^ t5_be[i1] ^ t4_be[i0];
-
- q = *++p32;
- i3 = q;
- i2 = q >> 8;
- i1 = q >> 16;
- i0 = q >> 24;
- crc ^= t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0];
-#else
- q = *++p32 ^ crc;
- i3 = q >> 24;
- i2 = q >> 16;
- i1 = q >> 8;
- i0 = q;
- crc = t7_be[i3] ^ t6_be[i2] ^ t5_be[i1] ^ t4_be[i0];
+# if CRC_LE_BITS == 32
+ rem_len = len & 3;
+ len = len >> 2;
+# else
+ rem_len = len & 7;
+ len = len >> 3;
+# endif

- q = *++p32;
- i3 = q >> 24;
- i2 = q >> 16;
- i1 = q >> 8;
- i0 = q;
- crc ^= t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0];
-#endif
+ b = (const uint32_t *)buf;
+ for (--b; len; --len) {
+ q = crc ^ *++b; /* use pre increment for speed */
+# if CRC_LE_BITS == 32
+ crc = DO_CRC4;
+# else
+ crc = DO_CRC8;
+ q = *++b;
+ crc ^= DO_CRC4;
+# endif
}
-
- p8 = (uint8_t *)(++p32);
-
- for (i = 0; i < end_bytes; i++) {
-#ifndef WORDS_BIGENDIAN
- i0 = *p8++ ^ crc;
- crc = t0_be[i0] ^ (crc >> 8);
-#else
- i0 = *p8++ ^ (crc >> 24);
- crc = t0_be[i0] ^ (crc << 8);
-#endif
+ len = rem_len;
+ /* And the last few bytes */
+ if (len) {
+ uint8_t *p = (uint8_t *)(b + 1) - 1;
+ do {
+ DO_CRC(*++p); /* use pre increment for speed */
+ } while (--len);
}
-
- return __be32_to_cpu(crc);
+ return crc;
+#undef DO_CRC
+#undef DO_CRC4
+#undef DO_CRC8
}
#endif

/**
- * crc32c_le() - Calculate bitwise little-endian CRC32c.
- * @crc: seed value for computation. ~0 for ext4, sometimes 0 for
- * other uses, or the previous crc32c value if computing incrementally.
+ * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
+ * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
+ * other uses, or the previous crc32 value if computing incrementally.
* @p: pointer to buffer over which CRC is run
* @len: length of buffer @p
*/
-uint32_t ext2fs_crc32c_le(uint32_t crc, unsigned char const *p, size_t len)
+static inline uint32_t crc32_le_generic(uint32_t crc, unsigned char const *p,
+ size_t len, const uint32_t (*tab)[256],
+ uint32_t polynomial)
{
#if CRC_LE_BITS == 1
int i;
while (len--) {
crc ^= *p++;
for (i = 0; i < 8; i++)
- crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+ crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0);
}
# elif CRC_LE_BITS == 2
while (len--) {
crc ^= *p++;
- crc = (crc >> 2) ^ t0_le[crc & 0x03];
- crc = (crc >> 2) ^ t0_le[crc & 0x03];
- crc = (crc >> 2) ^ t0_le[crc & 0x03];
- crc = (crc >> 2) ^ t0_le[crc & 0x03];
+ crc = (crc >> 2) ^ tab[0][crc & 3];
+ crc = (crc >> 2) ^ tab[0][crc & 3];
+ crc = (crc >> 2) ^ tab[0][crc & 3];
+ crc = (crc >> 2) ^ tab[0][crc & 3];
}
# elif CRC_LE_BITS == 4
while (len--) {
crc ^= *p++;
- crc = (crc >> 4) ^ t0_le[crc & 0x0f];
- crc = (crc >> 4) ^ t0_le[crc & 0x0f];
+ crc = (crc >> 4) ^ tab[0][crc & 15];
+ crc = (crc >> 4) ^ tab[0][crc & 15];
}
# elif CRC_LE_BITS == 8
+ /* aka Sarwate algorithm */
while (len--) {
crc ^= *p++;
- crc = (crc >> 8) ^ t0_le[crc & 0xff];
+ crc = (crc >> 8) ^ tab[0][crc & 255];
}
# else
- crc = crc32c_le_body(crc, p, len);
-# endif
+ crc = __cpu_to_le32(crc);
+ crc = crc32_body(crc, p, len, tab);
+ crc = __le32_to_cpu(crc);
+#endif
return crc;
}

+uint32_t ext2fs_crc32c_le(uint32_t crc, unsigned char const *p, size_t len)
+{
+ return crc32_le_generic(crc, p, len, crc32ctable_le, CRC32C_POLY_LE);
+}
+
/**
- * crc32c_be() - Calculate bitwise big-endian CRC32c.
- * @crc: seed value for computation. ~0 for ext4, sometimes 0 for
- * other uses, or the previous crc32c value if computing incrementally.
+ * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
+ * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
+ * other uses, or the previous crc32 value if computing incrementally.
* @p: pointer to buffer over which CRC is run
* @len: length of buffer @p
*/
-uint32_t ext2fs_crc32c_be(uint32_t crc, unsigned char const *p, size_t len)
+static inline uint32_t crc32_be_generic(uint32_t crc, unsigned char const *p,
+ size_t len, const uint32_t (*tab)[256],
+ uint32_t polynomial)
{
#if CRC_BE_BITS == 1
int i;
while (len--) {
crc ^= *p++ << 24;
for (i = 0; i < 8; i++)
- crc = (crc << 1) ^
- ((crc & 0x80000000) ? CRCPOLY_BE : 0);
+ crc =
+ (crc << 1) ^ ((crc & 0x80000000) ? polynomial :
+ 0);
}
# elif CRC_BE_BITS == 2
while (len--) {
crc ^= *p++ << 24;
- crc = (crc << 2) ^ t0_be[crc >> 30];
- crc = (crc << 2) ^ t0_be[crc >> 30];
- crc = (crc << 2) ^ t0_be[crc >> 30];
- crc = (crc << 2) ^ t0_be[crc >> 30];
+ crc = (crc << 2) ^ tab[0][crc >> 30];
+ crc = (crc << 2) ^ tab[0][crc >> 30];
+ crc = (crc << 2) ^ tab[0][crc >> 30];
+ crc = (crc << 2) ^ tab[0][crc >> 30];
}
# elif CRC_BE_BITS == 4
while (len--) {
crc ^= *p++ << 24;
- crc = (crc << 4) ^ t0_be[crc >> 28];
- crc = (crc << 4) ^ t0_be[crc >> 28];
+ crc = (crc << 4) ^ tab[0][crc >> 28];
+ crc = (crc << 4) ^ tab[0][crc >> 28];
}
# elif CRC_BE_BITS == 8
while (len--) {
crc ^= *p++ << 24;
- crc = (crc << 8) ^ t0_be[crc >> 24];
+ crc = (crc << 8) ^ tab[0][crc >> 24];
}
# else
- crc = crc32c_be_body(crc, p, len);
+ crc = __cpu_to_be32(crc);
+ crc = crc32_body(crc, p, len, tab);
+ crc = __be32_to_cpu(crc);
# endif
return crc;
}

+uint32_t ext2fs_crc32_be(uint32_t crc, unsigned char const *p, size_t len)
+{
+ return crc32_be_generic(crc, p, len, crc32table_be, CRCPOLY_BE);
+}
+
#ifdef UNITTEST
static uint8_t test_buf[] = {
0xd9, 0xd7, 0x6a, 0x13, 0x3a, 0xb1, 0x05, 0x48,
@@ -972,137 +760,137 @@ static struct crc_test {
uint32_t crc; /* random starting crc */
uint32_t start; /* random offset in buf */
uint32_t length; /* random length of test */
- uint32_t crc_le; /* expected crc32_le result */
- uint32_t crc_be; /* expected crc32_be result */
+ uint32_t crc32c_le; /* expected crc32c_le result */
+ uint32_t crc32_be; /* expected crc32_be result */
} test[] = {
- {0xffffffff, 0x00000000, 0x00001000, 0x13934bef, 0x14f3b75f},
- {0xfe7328ea, 0x00000763, 0x00000717, 0xed2c0d70, 0x57531214},
- {0x4c40684e, 0x00000721, 0x0000011e, 0xd7f46ccc, 0xedf12ec3},
- {0x6b487f90, 0x00000264, 0x000007bc, 0x759e9939, 0x9af8e387},
- {0x9f5810db, 0x00000afa, 0x00000255, 0x2685197f, 0x716de6ed},
- {0xb15c4755, 0x00000d5b, 0x000002a4, 0xd8fadcb5, 0xfc34ae3f},
- {0x06518253, 0x00000ffb, 0x00000004, 0xabee2433, 0xfa30ac9e},
- {0xd9e71c55, 0x00000a2a, 0x00000259, 0x96682af2, 0xe5907ea3},
- {0x0c1ae843, 0x00000ce4, 0x0000031b, 0x7b637c43, 0xe7f71b04},
- {0xec3cd517, 0x000002ff, 0x00000566, 0x5d719a77, 0xed16e045},
- {0x77828e95, 0x0000067f, 0x0000038f, 0x43ee5b6c, 0x35999927},
- {0xec87b4e3, 0x00000d1c, 0x000002e3, 0x2ddd2eee, 0x9452d3f8},
- {0x412158bb, 0x00000eee, 0x00000111, 0x67b38ba2, 0x177976d0},
- {0x2e52de3e, 0x00000c4a, 0x000003b5, 0xbcc5d61d, 0xf60fee71},
- {0x6ddaae8b, 0x00000d99, 0x00000266, 0x8b535544, 0x1dab8596},
- {0x049b6cb1, 0x000009c5, 0x000000b0, 0xfc22cabc, 0x47ebc954},
- {0x77d4b954, 0x0000028a, 0x000007fa, 0x71d00923, 0x905585ef},
- {0x5e192355, 0x00000ac1, 0x000001fa, 0xb966b81a, 0x33c12903},
- {0x7d80b71d, 0x00000213, 0x000001e0, 0x2bba371a, 0x5f4bd8d9},
- {0x01f6f1e4, 0x000001d6, 0x00000395, 0xb7e8a647, 0x2a7943a1},
- {0x1dfabb13, 0x00000e14, 0x000001eb, 0x53917fba, 0x8dee1e5d},
- {0xb00a4449, 0x00000bf6, 0x00000409, 0xedecb577, 0x628e087d},
- {0x7ecd3981, 0x0000083f, 0x0000016b, 0xefef62b9, 0xda4f94e6},
- {0xf8f330d2, 0x000004be, 0x00000757, 0x9357c9f3, 0x8e2d5c2f},
- {0x03c38af2, 0x00000d23, 0x000002dc, 0x360fa8c0, 0x6294c0d6},
- {0x687bb79b, 0x00000f3d, 0x000000c2, 0x448d3be2, 0x08f48f3a},
- {0x6710f550, 0x000009e9, 0x00000603, 0xdbfd1998, 0xc950ac29},
- {0x873171d1, 0x00000787, 0x000004d5, 0xab7f1b62, 0xe66896ab},
- {0x373b1314, 0x00000f0f, 0x000000f0, 0x184098ab, 0x4038e674},
- {0x90fad9cd, 0x00000ead, 0x00000152, 0x23ce52ff, 0x9eff3974},
- {0x19676fe7, 0x0000007d, 0x0000070d, 0xf8a76f1e, 0xfbc5c8a9},
- {0x89facd45, 0x000005f3, 0x00000473, 0x4331a006, 0xb8f0f0cc},
- {0x6f173747, 0x00000fc3, 0x0000003c, 0xb012f08e, 0x5126e378},
- {0x4b44a106, 0x0000075a, 0x0000008b, 0xf6f7ac38, 0xf9b1781b},
- {0xb620ad06, 0x00000774, 0x0000017e, 0xd34558e6, 0xb175edd3},
- {0x976f21e9, 0x000008d7, 0x0000034a, 0xe533aa3a, 0x1e4367b9},
- {0x687628c0, 0x000006c5, 0x0000061b, 0x3a840b15, 0xfb5989a0},
- {0xe24ac108, 0x00000cd0, 0x0000032f, 0x51010ae8, 0xcdd8f182},
- {0x361c44a3, 0x00000304, 0x00000719, 0xfd7bd481, 0x12de540f},
- {0xd93ff95e, 0x00000db7, 0x0000008e, 0xcfbbc304, 0x42eecd5a},
- {0xed752d12, 0x00000883, 0x00000091, 0x65a6c868, 0x9ebfa578},
- {0xb4ff4b54, 0x000003d3, 0x000001c1, 0xf82597e7, 0xa8ad2b19},
- {0x111b520f, 0x00000708, 0x000000eb, 0xc3e109f3, 0x323ace17},
- {0x62c806f2, 0x00000ba3, 0x0000045c, 0x874d3a72, 0xaf1a1360},
- {0x40d97470, 0x000005e1, 0x0000058d, 0x87a9684f, 0x524244a8},
- {0x4312179c, 0x00000056, 0x0000070e, 0x809a00f5, 0xf9e940b0},
- {0x13d5f84c, 0x00000a2d, 0x00000104, 0xf3d27578, 0x5d33341c},
- {0x1f302cb2, 0x00000151, 0x00000014, 0x1e162693, 0x53c3cfc3},
- {0xe491db24, 0x00000600, 0x000006f6, 0x7ff09615, 0xa300ecf7},
- {0xf9a98069, 0x000002ba, 0x000002ad, 0x01af7387, 0x31c0911e},
- {0xe9c477ad, 0x0000015f, 0x00000778, 0x6facf9a0, 0x1993b688},
- {0x353f32b2, 0x0000087c, 0x00000783, 0x6cc964ea, 0x418db561},
- {0x78e1b24f, 0x00000650, 0x000006a8, 0xb3bb7c27, 0xf2aad006},
- {0x61aa400e, 0x00000049, 0x00000254, 0xb8cd1681, 0x79150b15},
- {0xb84b10b0, 0x00000f73, 0x0000008c, 0x406a6450, 0x0c705222},
- {0x9fa99c9c, 0x00000a7c, 0x000004d7, 0xfb3d21b4, 0xe4e789df},
- {0x3fc9ebe3, 0x00000cd9, 0x000000d6, 0x43803f9c, 0x5a152be5},
- {0x529879cd, 0x000002f2, 0x00000595, 0x78b4c6a6, 0xf7236ec4},
- {0x3a933019, 0x00000516, 0x00000266, 0xdcb45436, 0x2c7935f5},
- {0x887b4977, 0x00000227, 0x0000038d, 0xc5f7c3d9, 0x0d6d7df6},
- {0x770745de, 0x000008c6, 0x00000739, 0xf69145e8, 0x47d5efc9},
- {0x28be3b47, 0x00000c46, 0x0000032b, 0x764c028f, 0x1eb70d64},
- {0x5013a050, 0x00000cf6, 0x00000309, 0xea8fe164, 0x186affa4},
- {0x2ec4c9ba, 0x000006e8, 0x0000078d, 0xa35557a9, 0xb41f49ec},
- {0xa9f950c9, 0x00000d33, 0x000002cc, 0x41ea8618, 0xab8dfae3},
- {0x5b520229, 0x000007b2, 0x00000484, 0x44569f1f, 0x607a8052},
- {0xd8dcbbfc, 0x0000002f, 0x0000048c, 0xdb88ab8b, 0xf1c411f1},
- {0x25529792, 0x00000d1d, 0x000002e2, 0x20cda404, 0x32683a2d},
- {0x9f3f6d71, 0x00000238, 0x0000079a, 0x0720443e, 0x4b8ba2ff},
- {0x64121215, 0x000007ff, 0x0000038f, 0x6aacff2c, 0x3b84233b},
- {0xfb6cdde0, 0x00000ef8, 0x00000107, 0xbd43a0f1, 0x926624d0},
- {0x221c9d6f, 0x000007b6, 0x0000014f, 0xb67f834b, 0x2bdedda4},
- {0x030e1de4, 0x00000836, 0x000004b4, 0x0d67d26a, 0x75a73b73},
- {0xb56fa6cf, 0x00000c07, 0x000003f8, 0x60601ac1, 0x10a43f35},
- {0xb55c89f5, 0x0000098e, 0x000001d4, 0x2400efbe, 0x006e28eb},
- {0x5e90b6d5, 0x0000070b, 0x000003ea, 0x3bb5d6ea, 0xb175fa6b},
- {0x2a7045ae, 0x00000961, 0x00000633, 0xfca89e4b, 0x962cd6d2},
- {0x8b374ea9, 0x000006ba, 0x00000780, 0xbce036ed, 0x4dc8279b},
- {0x8bd90bc9, 0x00000562, 0x00000369, 0xcb26a24b, 0x50dee743},
- {0x5b1b1762, 0x000000fd, 0x0000051a, 0x33cdda07, 0xee75ff7b},
- {0xa4153555, 0x0000058f, 0x000005c7, 0xbe50eeca, 0xe73fffcc},
- {0x0be1f931, 0x00000651, 0x00000672, 0x95a25753, 0x4ad6270f},
- {0xb7e78618, 0x00000a7f, 0x000002bb, 0xe06bcc1c, 0x1a35ee59},
- {0x4a9bc41b, 0x00000e51, 0x000001ae, 0x709e8d2c, 0x75080ca8},
- {0xfc359d13, 0x00000440, 0x000002f8, 0x0a58451f, 0x6fa3cfbf},
- {0x5aa48619, 0x000006d1, 0x00000284, 0x928ead83, 0xbd600efc},
- {0xa609afa8, 0x0000053e, 0x00000272, 0xb048c141, 0x184f80bb},
- {0x3f108afb, 0x00000949, 0x00000150, 0x9a6bb5bc, 0x0ea02be1},
- {0x79bec2d3, 0x000008ed, 0x00000712, 0x32692d57, 0x2eb13289},
- {0x9429e067, 0x00000bc3, 0x0000043c, 0x5295ceff, 0x8a9014a7},
- {0xae58b96a, 0x0000082d, 0x000007d2, 0xc2a681ba, 0x6af94089},
- {0x95df24be, 0x00000985, 0x000004c1, 0x3a287765, 0x379fcb42},
- {0x5e94976f, 0x00000596, 0x000004ed, 0xff00c489, 0x991fc1f5},
- {0xf5e5f1de, 0x00000d31, 0x000002ce, 0x35f28e91, 0x543def1a},
- {0xa2c219cf, 0x00000a3c, 0x00000374, 0x707d21eb, 0xa6d28bc1},
- {0xf21b6ceb, 0x00000919, 0x00000135, 0x0847fb8b, 0x224468c2},
- {0xaa988728, 0x00000787, 0x00000771, 0x885aeaa4, 0x814db00b},
- {0xaa5dfaac, 0x000003e5, 0x0000051b, 0x52c48ab7, 0x725bef8a},
- {0x0a053968, 0x00000d2a, 0x000002d5, 0x7a90256d, 0xc53b9402},
- {0x1421dc20, 0x00000eef, 0x00000110, 0x97d6da24, 0x10846935},
- {0xb47c2166, 0x00000a6a, 0x00000209, 0xcfd6cc52, 0x46e2797e},
- {0x77dd1955, 0x000000de, 0x00000266, 0xba74bcaa, 0x4fa3fe9c},
- {0x68a03cc2, 0x0000082f, 0x000007b0, 0x752bd5d8, 0x4f760c63},
- {0x0226b0a3, 0x00000a5f, 0x000005a0, 0x82de4970, 0x8ee1310e},
- {0x637bf3b1, 0x00000d93, 0x0000026c, 0x5c7115cb, 0x9f6a0ced},
- {0x3b120edf, 0x00000c13, 0x000003ec, 0x80d7d20f, 0x241657d5},
- {0xe2456780, 0x000002eb, 0x00000641, 0xc0a5d289, 0x74df96b4},
- {0x9b2e7125, 0x00000c0c, 0x000003f3, 0xcc15f57e, 0x03e290bf},
- {0x153033ef, 0x00000787, 0x000006b6, 0x3cde443b, 0x7bf1d121},
- {0x18458b3f, 0x0000066c, 0x00000561, 0x9a2bd8c6, 0x9d564bef},
- {0x4ff9d4b9, 0x00000c8f, 0x0000033a, 0xd0ee6d6d, 0xee00aa0b},
- {0xdf84b5d9, 0x00000802, 0x0000029a, 0xdab0d74a, 0xd0cb63dc},
- {0x81ee15df, 0x000003ce, 0x00000725, 0x9942e2de, 0xe48fb26b},
- {0x5c768e04, 0x00000afd, 0x00000160, 0x36110831, 0x8dc74483},
- {0xe5e18094, 0x00000b4b, 0x000000a0, 0xffa3e4a7, 0xc0145e1b},
- {0xed7263b6, 0x00000d0d, 0x000002f2, 0xb0006a35, 0x5468ae3a},
- {0x5bfde7d7, 0x000006fb, 0x00000554, 0xa4193b76, 0xb73d34b2},
- {0x67f4a743, 0x00000b85, 0x0000047a, 0xf05c8d8f, 0x4f843e49},
- {0xf13bdf22, 0x00000ff7, 0x00000008, 0x816351eb, 0x41f537f6},
- {0x08ecc608, 0x00000d5d, 0x00000098, 0x90492772, 0xf5172204},
- {0x296f52ba, 0x000004f9, 0x00000788, 0x5e5a4896, 0xe01d5b46},
- {0xbe4624c2, 0x00000427, 0x000004ef, 0xcd267b94, 0x7b9069f4},
- {0x906f7c7c, 0x00000a05, 0x0000003f, 0x03fcfc33, 0x7b6ff563},
- {0x8f7b323e, 0x00000458, 0x000004c7, 0xcd4969c8, 0xd4c22ada},
- {0x88d6593d, 0x00000597, 0x000005b5, 0xf199cd3b, 0x5c3e8ca2},
- {0x978a7768, 0x00000268, 0x000001d3, 0xb28c95bd, 0x49a2cc67},
- {0x857a621e, 0x000007a7, 0x000003a8, 0xf4bf84ab, 0xde26f369},
- {0xb0e121ef, 0x000005be, 0x00000644, 0x28747c14, 0x61d4dc6b},
+ {0xffffffff, 0x00000000, 0x00001000, 0x13934bef, 0xd8ddcdc3},
+ {0xfe7328ea, 0x00000763, 0x00000717, 0xed2c0d70, 0xc863aef8},
+ {0x4c40684e, 0x00000721, 0x0000011e, 0xd7f46ccc, 0x173a11c4},
+ {0x6b487f90, 0x00000264, 0x000007bc, 0x759e9939, 0xd6307c56},
+ {0x9f5810db, 0x00000afa, 0x00000255, 0x2685197f, 0x2e5c9201},
+ {0xb15c4755, 0x00000d5b, 0x000002a4, 0xd8fadcb5, 0xf682c4be},
+ {0x06518253, 0x00000ffb, 0x00000004, 0xabee2433, 0x3d8abdf9},
+ {0xd9e71c55, 0x00000a2a, 0x00000259, 0x96682af2, 0x47b4d26c},
+ {0x0c1ae843, 0x00000ce4, 0x0000031b, 0x7b637c43, 0x62b47e8b},
+ {0xec3cd517, 0x000002ff, 0x00000566, 0x5d719a77, 0xff5bc5b7},
+ {0x77828e95, 0x0000067f, 0x0000038f, 0x43ee5b6c, 0x1a0cfacd},
+ {0xec87b4e3, 0x00000d1c, 0x000002e3, 0x2ddd2eee, 0x275118a7},
+ {0x412158bb, 0x00000eee, 0x00000111, 0x67b38ba2, 0xa74ecff5},
+ {0x2e52de3e, 0x00000c4a, 0x000003b5, 0xbcc5d61d, 0xbd800707},
+ {0x6ddaae8b, 0x00000d99, 0x00000266, 0x8b535544, 0xecbde1a1},
+ {0x049b6cb1, 0x000009c5, 0x000000b0, 0xfc22cabc, 0xfb78eb9f},
+ {0x77d4b954, 0x0000028a, 0x000007fa, 0x71d00923, 0x8c116f85},
+ {0x5e192355, 0x00000ac1, 0x000001fa, 0xb966b81a, 0x5aa17bbe},
+ {0x7d80b71d, 0x00000213, 0x000001e0, 0x2bba371a, 0xb5906aa6},
+ {0x01f6f1e4, 0x000001d6, 0x00000395, 0xb7e8a647, 0x3ad112b1},
+ {0x1dfabb13, 0x00000e14, 0x000001eb, 0x53917fba, 0xbaee0339},
+ {0xb00a4449, 0x00000bf6, 0x00000409, 0xedecb577, 0x6f3a3979},
+ {0x7ecd3981, 0x0000083f, 0x0000016b, 0xefef62b9, 0xe3e52eed},
+ {0xf8f330d2, 0x000004be, 0x00000757, 0x9357c9f3, 0x0835bc1b},
+ {0x03c38af2, 0x00000d23, 0x000002dc, 0x360fa8c0, 0x2ca885e6},
+ {0x687bb79b, 0x00000f3d, 0x000000c2, 0x448d3be2, 0x79be2f78},
+ {0x6710f550, 0x000009e9, 0x00000603, 0xdbfd1998, 0x1d25f627},
+ {0x873171d1, 0x00000787, 0x000004d5, 0xab7f1b62, 0xa76a5656},
+ {0x373b1314, 0x00000f0f, 0x000000f0, 0x184098ab, 0xba273974},
+ {0x90fad9cd, 0x00000ead, 0x00000152, 0x23ce52ff, 0xb7bc958c},
+ {0x19676fe7, 0x0000007d, 0x0000070d, 0xf8a76f1e, 0xf882b644},
+ {0x89facd45, 0x000005f3, 0x00000473, 0x4331a006, 0xe9dc1396},
+ {0x6f173747, 0x00000fc3, 0x0000003c, 0xb012f08e, 0xc6b888ee},
+ {0x4b44a106, 0x0000075a, 0x0000008b, 0xf6f7ac38, 0x60cd2b74},
+ {0xb620ad06, 0x00000774, 0x0000017e, 0xd34558e6, 0x3a0a615b},
+ {0x976f21e9, 0x000008d7, 0x0000034a, 0xe533aa3a, 0xa99e60be},
+ {0x687628c0, 0x000006c5, 0x0000061b, 0x3a840b15, 0x9bfcaef2},
+ {0xe24ac108, 0x00000cd0, 0x0000032f, 0x51010ae8, 0x20958672},
+ {0x361c44a3, 0x00000304, 0x00000719, 0xfd7bd481, 0xd70ff2b2},
+ {0xd93ff95e, 0x00000db7, 0x0000008e, 0xcfbbc304, 0xad716acd},
+ {0xed752d12, 0x00000883, 0x00000091, 0x65a6c868, 0x95c71c7b},
+ {0xb4ff4b54, 0x000003d3, 0x000001c1, 0xf82597e7, 0x44b7f99b},
+ {0x111b520f, 0x00000708, 0x000000eb, 0xc3e109f3, 0x71bc01ee},
+ {0x62c806f2, 0x00000ba3, 0x0000045c, 0x874d3a72, 0xc539b753},
+ {0x40d97470, 0x000005e1, 0x0000058d, 0x87a9684f, 0xea6073a5},
+ {0x4312179c, 0x00000056, 0x0000070e, 0x809a00f5, 0x209aea3b},
+ {0x13d5f84c, 0x00000a2d, 0x00000104, 0xf3d27578, 0xe087a8b6},
+ {0x1f302cb2, 0x00000151, 0x00000014, 0x1e162693, 0x95e4b90e},
+ {0xe491db24, 0x00000600, 0x000006f6, 0x7ff09615, 0x77611523},
+ {0xf9a98069, 0x000002ba, 0x000002ad, 0x01af7387, 0xea925faa},
+ {0xe9c477ad, 0x0000015f, 0x00000778, 0x6facf9a0, 0x1130f736},
+ {0x353f32b2, 0x0000087c, 0x00000783, 0x6cc964ea, 0x32459994},
+ {0x78e1b24f, 0x00000650, 0x000006a8, 0xb3bb7c27, 0x5a632f78},
+ {0x61aa400e, 0x00000049, 0x00000254, 0xb8cd1681, 0xdf2652d5},
+ {0xb84b10b0, 0x00000f73, 0x0000008c, 0x406a6450, 0x3619d31b},
+ {0x9fa99c9c, 0x00000a7c, 0x000004d7, 0xfb3d21b4, 0xea31c743},
+ {0x3fc9ebe3, 0x00000cd9, 0x000000d6, 0x43803f9c, 0x1f76a809},
+ {0x529879cd, 0x000002f2, 0x00000595, 0x78b4c6a6, 0x63b9b93f},
+ {0x3a933019, 0x00000516, 0x00000266, 0xdcb45436, 0x8f99c98c},
+ {0x887b4977, 0x00000227, 0x0000038d, 0xc5f7c3d9, 0xaf5e3091},
+ {0x770745de, 0x000008c6, 0x00000739, 0xf69145e8, 0x53d0dce1},
+ {0x28be3b47, 0x00000c46, 0x0000032b, 0x764c028f, 0x106d0905},
+ {0x5013a050, 0x00000cf6, 0x00000309, 0xea8fe164, 0x62180b57},
+ {0x2ec4c9ba, 0x000006e8, 0x0000078d, 0xa35557a9, 0xf44430a4},
+ {0xa9f950c9, 0x00000d33, 0x000002cc, 0x41ea8618, 0x587b4eb3},
+ {0x5b520229, 0x000007b2, 0x00000484, 0x44569f1f, 0x92406c32},
+ {0xd8dcbbfc, 0x0000002f, 0x0000048c, 0xdb88ab8b, 0x13bfe70e},
+ {0x25529792, 0x00000d1d, 0x000002e2, 0x20cda404, 0x19d3b4e4},
+ {0x9f3f6d71, 0x00000238, 0x0000079a, 0x0720443e, 0x3c107021},
+ {0x64121215, 0x000007ff, 0x0000038f, 0x6aacff2c, 0xb82fdc3e},
+ {0xfb6cdde0, 0x00000ef8, 0x00000107, 0xbd43a0f1, 0xab0d3c1d},
+ {0x221c9d6f, 0x000007b6, 0x0000014f, 0xb67f834b, 0x1371ad05},
+ {0x030e1de4, 0x00000836, 0x000004b4, 0x0d67d26a, 0xe2e72df1},
+ {0xb56fa6cf, 0x00000c07, 0x000003f8, 0x60601ac1, 0x039de73e},
+ {0xb55c89f5, 0x0000098e, 0x000001d4, 0x2400efbe, 0xfe39a2bb},
+ {0x5e90b6d5, 0x0000070b, 0x000003ea, 0x3bb5d6ea, 0xf0f794a0},
+ {0x2a7045ae, 0x00000961, 0x00000633, 0xfca89e4b, 0xe66ce41c},
+ {0x8b374ea9, 0x000006ba, 0x00000780, 0xbce036ed, 0x4cb28ef7},
+ {0x8bd90bc9, 0x00000562, 0x00000369, 0xcb26a24b, 0x40236d1d},
+ {0x5b1b1762, 0x000000fd, 0x0000051a, 0x33cdda07, 0xc32e420a},
+ {0xa4153555, 0x0000058f, 0x000005c7, 0xbe50eeca, 0x83a67f35},
+ {0x0be1f931, 0x00000651, 0x00000672, 0x95a25753, 0x88f1aac1},
+ {0xb7e78618, 0x00000a7f, 0x000002bb, 0xe06bcc1c, 0x74274f66},
+ {0x4a9bc41b, 0x00000e51, 0x000001ae, 0x709e8d2c, 0x54eff534},
+ {0xfc359d13, 0x00000440, 0x000002f8, 0x0a58451f, 0x55e9363f},
+ {0x5aa48619, 0x000006d1, 0x00000284, 0x928ead83, 0x31041c06},
+ {0xa609afa8, 0x0000053e, 0x00000272, 0xb048c141, 0x4704efba},
+ {0x3f108afb, 0x00000949, 0x00000150, 0x9a6bb5bc, 0x4e4430c8},
+ {0x79bec2d3, 0x000008ed, 0x00000712, 0x32692d57, 0x11d52a7b},
+ {0x9429e067, 0x00000bc3, 0x0000043c, 0x5295ceff, 0x04640f4d},
+ {0xae58b96a, 0x0000082d, 0x000007d2, 0xc2a681ba, 0xf7ca4a2c},
+ {0x95df24be, 0x00000985, 0x000004c1, 0x3a287765, 0x2c4af003},
+ {0x5e94976f, 0x00000596, 0x000004ed, 0xff00c489, 0x5ae11687},
+ {0xf5e5f1de, 0x00000d31, 0x000002ce, 0x35f28e91, 0x30d47957},
+ {0xa2c219cf, 0x00000a3c, 0x00000374, 0x707d21eb, 0x2a14a255},
+ {0xf21b6ceb, 0x00000919, 0x00000135, 0x0847fb8b, 0xcb8d3b93},
+ {0xaa988728, 0x00000787, 0x00000771, 0x885aeaa4, 0x6531b509},
+ {0xaa5dfaac, 0x000003e5, 0x0000051b, 0x52c48ab7, 0xe43cc5e9},
+ {0x0a053968, 0x00000d2a, 0x000002d5, 0x7a90256d, 0x8004765c},
+ {0x1421dc20, 0x00000eef, 0x00000110, 0x97d6da24, 0x1378f6ff},
+ {0xb47c2166, 0x00000a6a, 0x00000209, 0xcfd6cc52, 0x676e14a5},
+ {0x77dd1955, 0x000000de, 0x00000266, 0xba74bcaa, 0xc71b429c},
+ {0x68a03cc2, 0x0000082f, 0x000007b0, 0x752bd5d8, 0x19ed14aa},
+ {0x0226b0a3, 0x00000a5f, 0x000005a0, 0x82de4970, 0xf654d3ed},
+ {0x637bf3b1, 0x00000d93, 0x0000026c, 0x5c7115cb, 0x3cccb57e},
+ {0x3b120edf, 0x00000c13, 0x000003ec, 0x80d7d20f, 0x92132798},
+ {0xe2456780, 0x000002eb, 0x00000641, 0xc0a5d289, 0x6160c87a},
+ {0x9b2e7125, 0x00000c0c, 0x000003f3, 0xcc15f57e, 0x6f00f637},
+ {0x153033ef, 0x00000787, 0x000006b6, 0x3cde443b, 0xb46caa6e},
+ {0x18458b3f, 0x0000066c, 0x00000561, 0x9a2bd8c6, 0xb6c29121},
+ {0x4ff9d4b9, 0x00000c8f, 0x0000033a, 0xd0ee6d6d, 0xc81cf380},
+ {0xdf84b5d9, 0x00000802, 0x0000029a, 0xdab0d74a, 0xb2464559},
+ {0x81ee15df, 0x000003ce, 0x00000725, 0x9942e2de, 0x4ccf571b},
+ {0x5c768e04, 0x00000afd, 0x00000160, 0x36110831, 0xae0b305a},
+ {0xe5e18094, 0x00000b4b, 0x000000a0, 0xffa3e4a7, 0x6c8a4f09},
+ {0xed7263b6, 0x00000d0d, 0x000002f2, 0xb0006a35, 0x7e04af8c},
+ {0x5bfde7d7, 0x000006fb, 0x00000554, 0xa4193b76, 0xb3a91d12},
+ {0x67f4a743, 0x00000b85, 0x0000047a, 0xf05c8d8f, 0xfb472fdf},
+ {0xf13bdf22, 0x00000ff7, 0x00000008, 0x816351eb, 0xf347f235},
+ {0x08ecc608, 0x00000d5d, 0x00000098, 0x90492772, 0x0b7f1521},
+ {0x296f52ba, 0x000004f9, 0x00000788, 0x5e5a4896, 0x1cc67088},
+ {0xbe4624c2, 0x00000427, 0x000004ef, 0xcd267b94, 0x550caefd},
+ {0x906f7c7c, 0x00000a05, 0x0000003f, 0x03fcfc33, 0x9ed82a02},
+ {0x8f7b323e, 0x00000458, 0x000004c7, 0xcd4969c8, 0x633c38a8},
+ {0x88d6593d, 0x00000597, 0x000005b5, 0xf199cd3b, 0x0491452f},
+ {0x978a7768, 0x00000268, 0x000001d3, 0xb28c95bd, 0x1a42fe61},
+ {0x857a621e, 0x000007a7, 0x000003a8, 0xf4bf84ab, 0xcd0694c6},
+ {0xb0e121ef, 0x000005be, 0x00000644, 0x28747c14, 0xf0510c72},
{0, 0, 0, 0, 0},
};

@@ -1114,15 +902,15 @@ static int test_crc32c(void)
while (t->length) {
uint32_t be, le;
le = ext2fs_crc32c_le(t->crc, test_buf + t->start, t->length);
- be = ext2fs_crc32c_be(t->crc, test_buf + t->start, t->length);
- if (le != t->crc_le) {
+ be = ext2fs_crc32_be(t->crc, test_buf + t->start, t->length);
+ if (le != t->crc32c_le) {
printf("Test %d LE fails, %x != %x\n",
- (t - test), le, t->crc_le);
+ (t - test), le, t->crc32c_le);
failures++;
}
- if (be != t->crc_be) {
+ if (be != t->crc32_be) {
printf("Test %d BE fails, %x != %x\n",
- (t - test), be, t->crc_be);
+ (t - test), be, t->crc32_be);
failures++;
}
t++;
diff --git a/lib/ext2fs/crc32c_defs.h b/lib/ext2fs/crc32c_defs.h
index 023f2c0..3f9a09e 100644
--- a/lib/ext2fs/crc32c_defs.h
+++ b/lib/ext2fs/crc32c_defs.h
@@ -1,10 +1,18 @@
/*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+#define CRCPOLY_BE 0x04c11db7
+
+/*
* This is the CRC32c polynomial, as outlined by Castagnoli.
* x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+x^11+x^10+x^9+
* x^8+x^6+x^0
*/
-#define CRCPOLY_LE 0x82F63B78
-#define CRCPOLY_BE 0x1EDC6F41
+#define CRC32C_POLY_LE 0x82F63B78
+#define CRC32C_POLY_BE 0x1EDC6F41

/* How many bits at a time to use. Valid values are 1, 2, 4, 8, 32 and 64. */
/* For less performance-sensitive, use 4 */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index cd6c0b2..28cb626 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -956,7 +956,7 @@ extern int ext2fs_super_and_bgd_loc(ext2_filsys fs,
extern void ext2fs_update_dynamic_rev(ext2_filsys fs);

/* crc32c.c */
-extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
+extern __u32 ext2fs_crc32_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);

/* csum.c */
diff --git a/lib/ext2fs/gen_crc32ctable.c b/lib/ext2fs/gen_crc32ctable.c
index 9996e9d..a0742ee 100644
--- a/lib/ext2fs/gen_crc32ctable.c
+++ b/lib/ext2fs/gen_crc32ctable.c
@@ -4,23 +4,27 @@

#define ENTRIES_PER_LINE 4

-#if CRC_LE_BITS <= 8
-#define LE_TABLE_SIZE (1 << CRC_LE_BITS)
+#if CRC_LE_BITS > 8
+# define LE_TABLE_ROWS (CRC_LE_BITS/8)
+# define LE_TABLE_SIZE 256
#else
-#define LE_TABLE_SIZE 256
+# define LE_TABLE_ROWS 1
+# define LE_TABLE_SIZE (1 << CRC_LE_BITS)
#endif

-#if CRC_BE_BITS <= 8
-#define BE_TABLE_SIZE (1 << CRC_BE_BITS)
+#if CRC_BE_BITS > 8
+# define BE_TABLE_ROWS (CRC_BE_BITS/8)
+# define BE_TABLE_SIZE 256
#else
-#define BE_TABLE_SIZE 256
+# define BE_TABLE_ROWS 1
+# define BE_TABLE_SIZE (1 << CRC_BE_BITS)
#endif

-static uint32_t crc32ctable_le[8][256];
-static uint32_t crc32ctable_be[8][256];
+static uint32_t crc32table_be[BE_TABLE_ROWS][256];
+static uint32_t crc32ctable_le[LE_TABLE_ROWS][256];

/**
- * crc32cinit_le() - allocate and initialize LE table data
+ * crc32init_le() - allocate and initialize LE table data
*
* crc is the crc of the byte i; other entries are filled in based on the
* fact that crctable[i^j] = crctable[i] ^ crctable[j].
@@ -34,13 +38,13 @@ static void crc32cinit_le(void)
crc32ctable_le[0][0] = 0;

for (i = LE_TABLE_SIZE >> 1; i; i >>= 1) {
- crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+ crc = (crc >> 1) ^ ((crc & 1) ? CRC32C_POLY_LE : 0);
for (j = 0; j < LE_TABLE_SIZE; j += 2 * i)
crc32ctable_le[0][i + j] = crc ^ crc32ctable_le[0][j];
}
for (i = 0; i < LE_TABLE_SIZE; i++) {
crc = crc32ctable_le[0][i];
- for (j = 1; j < 8; j++) {
+ for (j = 1; j < LE_TABLE_ROWS; j++) {
crc = crc32ctable_le[0][crc & 0xff] ^ (crc >> 8);
crc32ctable_le[j][i] = crc;
}
@@ -48,75 +52,65 @@ static void crc32cinit_le(void)
}

/**
- * crc32cinit_be() - allocate and initialize BE table data
+ * crc32init_be() - allocate and initialize BE table data
*/
-static void crc32cinit_be(void)
+static void crc32init_be(void)
{
unsigned i, j;
uint32_t crc = 0x80000000;

- crc32ctable_be[0][0] = 0;
+ crc32table_be[0][0] = 0;

for (i = 1; i < BE_TABLE_SIZE; i <<= 1) {
crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
for (j = 0; j < i; j++)
- crc32ctable_be[0][i + j] = crc ^ crc32ctable_be[0][j];
+ crc32table_be[0][i + j] = crc ^ crc32table_be[0][j];
}
for (i = 0; i < BE_TABLE_SIZE; i++) {
- crc = crc32ctable_be[0][i];
- for (j = 1; j < 8; j++) {
- crc = crc32ctable_be[0][(crc >> 24) & 0xff] ^
- (crc << 8);
- crc32ctable_be[j][i] = crc;
+ crc = crc32table_be[0][i];
+ for (j = 1; j < BE_TABLE_ROWS; j++) {
+ crc = crc32table_be[0][(crc >> 24) & 0xff] ^ (crc << 8);
+ crc32table_be[j][i] = crc;
}
}
}

-static void output_table(uint32_t table[8][256], int len, char trans)
+static void output_table(uint32_t (*table)[256], int rows, int len, char *trans)
{
int i, j;

- for (j = 0 ; j < 8; j++) {
- printf("static const uint32_t t%d_%ce[] = {", j, trans);
+ for (j = 0 ; j < rows; j++) {
+ printf("{");
for (i = 0; i < len - 1; i++) {
- if ((i % ENTRIES_PER_LINE) == 0)
+ if (i % ENTRIES_PER_LINE == 0)
printf("\n");
- printf("to%ce(0x%8.8xL),", trans, table[j][i]);
- if ((i % ENTRIES_PER_LINE) != (ENTRIES_PER_LINE - 1))
- printf(" ");
- }
- printf("to%ce(0x%8.8xL)};\n\n", trans, table[j][len - 1]);
-
- if (trans == 'l') {
- if ((j+1)*8 >= CRC_LE_BITS)
- break;
- } else {
- if ((j+1)*8 >= CRC_BE_BITS)
- break;
+ printf("%s(0x%8.8xL), ", trans, table[j][i]);
}
+ printf("%s(0x%8.8xL)},\n", trans, table[j][len - 1]);
}
}

int main(int argc, char **argv)
{
- printf("/*\n");
- printf(" * crc32ctable.h - CRC32c tables\n");
- printf(" * this file is generated - do not edit\n");
- printf(" * # gen_crc32ctable > crc32c_table.h\n");
- printf(" * with\n");
- printf(" * CRC_LE_BITS = %d\n", CRC_LE_BITS);
- printf(" * CRC_BE_BITS = %d\n", CRC_BE_BITS);
- printf(" */\n");
- printf("#include <stdint.h>\n");
+ printf("/* this file is generated - do not edit */\n\n");

+ if (CRC_BE_BITS > 1) {
+ crc32init_be();
+ printf("static const uint32_t "
+ "crc32table_be[%d][%d] = {",
+ BE_TABLE_ROWS, BE_TABLE_SIZE);
+ output_table(crc32table_be, LE_TABLE_ROWS,
+ BE_TABLE_SIZE, "tobe");
+ printf("};\n");
+ }
if (CRC_LE_BITS > 1) {
crc32cinit_le();
- output_table(crc32ctable_le, LE_TABLE_SIZE, 'l');
- }

2012-03-07 00:03:49

by djwong

[permalink] [raw]
Subject: [PATCH 51/54] libext2fs: Enable support for the metadata checksumming feature

Add metadata checksumming to the list of supported features.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext2fs.h | 6 ++++--
lib/ext2fs/kernel-jbd.h | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)


diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 49d9271..cd6c0b2 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -598,7 +598,8 @@ typedef struct ext2_icount *ext2_icount_t;
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\
EXT4_FEATURE_RO_COMPAT_GDT_CSUM|\
EXT4_FEATURE_RO_COMPAT_BIGALLOC|\
- EXT4_FEATURE_RO_COMPAT_QUOTA)
+ EXT4_FEATURE_RO_COMPAT_QUOTA|\
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
#else
#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
@@ -606,7 +607,8 @@ typedef struct ext2_icount *ext2_icount_t;
EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\
EXT4_FEATURE_RO_COMPAT_GDT_CSUM|\
- EXT4_FEATURE_RO_COMPAT_BIGALLOC)
+ EXT4_FEATURE_RO_COMPAT_BIGALLOC|\
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
#endif

/*
diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index e9d725d..a86150c 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -259,7 +259,8 @@ typedef struct journal_superblock_s
#define JFS_KNOWN_ROCOMPAT_FEATURES 0
#define JFS_KNOWN_INCOMPAT_FEATURES (JFS_FEATURE_INCOMPAT_REVOKE|\
JFS_FEATURE_INCOMPAT_ASYNC_COMMIT|\
- JFS_FEATURE_INCOMPAT_64BIT)
+ JFS_FEATURE_INCOMPAT_64BIT|\
+ JFS_FEATURE_INCOMPAT_CSUM_V2)

#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
#ifdef E2FSCK_INCLUDE_INLINE_FUNCS


2012-03-07 00:04:13

by djwong

[permalink] [raw]
Subject: [PATCH 48/54] e2fsck: Check descriptor block checksum when recovering journal

Verify the descriptor block checksum when recovering a journal.

Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/recovery.c | 39 ++++++++++++++++++++++++++++++++++++++-
1 files changed, 38 insertions(+), 1 deletions(-)


diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index 60c8ceb..f76e7d8 100644
--- a/e2fsck/recovery.c
+++ b/e2fsck/recovery.c
@@ -174,6 +174,27 @@ static int jread(struct buffer_head **bhp, journal_t *journal,
return 0;
}

+static int jbd2_descr_block_csum_verify(journal_t *j,
+ void *buf)
+{
+ struct journal_block_tail *tail;
+ __u32 provided, calculated;
+
+ if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2))
+ return 1;
+
+ tail = (struct journal_block_tail *)(buf + j->j_blocksize -
+ sizeof(struct journal_block_tail));
+ provided = tail->t_checksum;
+ tail->t_checksum = 0;
+ calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid,
+ sizeof(j->j_superblock->s_uuid));
+ calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize);
+ tail->t_checksum = provided;
+
+ provided = ext2fs_be32_to_cpu(provided);
+ return provided == calculated;
+}

/*
* Count the number of in-use tags in a journal descriptor block.
@@ -186,6 +207,9 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
int nr = 0, size = journal->j_blocksize;
int tag_bytes = journal_tag_bytes(journal);

+ if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_CSUM_V2))
+ size -= sizeof(struct journal_block_tail);
+
tagp = &bh->b_data[sizeof(journal_header_t)];

while ((tagp - bh->b_data + tag_bytes) <= size) {
@@ -364,6 +388,7 @@ static int do_one_pass(journal_t *journal,
int blocktype;
int tag_bytes = journal_tag_bytes(journal);
__u32 crc32_sum = ~0; /* Transactional Checksums */
+ int descr_csum_size = 0;

/* Precompute the maximum metadata descriptors in a descriptor block */
int MAX_BLOCKS_PER_DESC;
@@ -454,6 +479,18 @@ static int do_one_pass(journal_t *journal,

switch(blocktype) {
case JFS_DESCRIPTOR_BLOCK:
+ /* Verify checksum first */
+ if (JFS_HAS_INCOMPAT_FEATURE(journal,
+ JFS_FEATURE_INCOMPAT_CSUM_V2))
+ descr_csum_size =
+ sizeof(struct journal_block_tail);
+ if (descr_csum_size > 0 &&
+ !jbd2_descr_block_csum_verify(journal,
+ bh->b_data)) {
+ err = -EIO;
+ goto failed;
+ }
+
/* If it is a valid descriptor block, replay it
* in pass REPLAY; if journal_checksums enabled, then
* calculate checksums in PASS_SCAN, otherwise,
@@ -484,7 +521,7 @@ static int do_one_pass(journal_t *journal,

tagp = &bh->b_data[sizeof(journal_header_t)];
while ((tagp - bh->b_data + tag_bytes)
- <= journal->j_blocksize) {
+ <= journal->j_blocksize - descr_csum_size) {
unsigned long io_block;

tag = (journal_block_tag_t *) tagp;


2012-03-07 00:04:34

by djwong

[permalink] [raw]
Subject: [PATCH 54/54] mke2fs: Enable metadata_csum on ext4dev filesystems

Enable full-power metadata checksumming by default on 'ext4dev' filesystems.
This should be fairly safe for now, since only developers should be using this
new feature.

Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/mke2fs.conf | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)


diff --git a/misc/mke2fs.conf b/misc/mke2fs.conf
index 0871f77..178733f 100644
--- a/misc/mke2fs.conf
+++ b/misc/mke2fs.conf
@@ -16,7 +16,7 @@
inode_size = 256
}
ext4dev = {
- features = has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize
+ features = has_journal,extent,huge_file,flex_bg,metadata_csum,64bit,dir_nlink,extra_isize
inode_size = 256
options = test_fs=1
}


2012-03-09 22:04:47

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 02/54] libext2fs: Change ext4 on-disk layout to support metadata checksumming

On Tue, Mar 06, 2012 at 03:57:34PM -0800, Darrick J. Wong wrote:
> Define flags and extend ext4 structure definitions to support metadata
> checksumming. Ted T'so covered many of these fields in an earlier patch, but
> there are more required changes to the disk layout.
>
> Signed-off-by: Darrick J. Wong <[email protected]>

> @@ -619,7 +630,7 @@ struct ext2_super_block {
> __u64 s_mmp_block; /* Block for multi-mount protection */
> __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
> __u8 s_log_groups_per_flex; /* FLEX_BG group size */
> - __u8 s_reserved_char_pad;
> + __u8 s_checksum_type; /* metadata checksum algorithm */
> __u16 s_reserved_pad; /* Padding to next 32bits */
> __u64 s_kbytes_written; /* nr of lifetime kilobytes written */
> __u32 s_snapshot_inum; /* Inode number of active snapshot */

Note: when you change the superblock definition, you *must* also
update the test in lib/ext2fs/tst_super_size.c.

If you don't then running "make check" from the top-level of the build
tree will likely bomb out. And you will also reveal to me that you
haven't run the full set of regression tests before submitting your
patch series. :-)

- Ted

2012-03-09 22:37:39

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH v3 00/54] e2fsprogs: Add metadata checksumming

I've imported this patch set into a guilt patch series, which makes it
easier for me to work on the patch series and make minor tweaks so
that I can get interim e2fsprogs support up and going (and at least
building and passing the regression test suite) so I can test and play
with the kernel side patches.

People who want to follow along can look here:

https://github.com/tytso/e2fsprogs-cksum-patch-queue

... or at the read-only git tree which you can find here:

git://github.com/tytso/e2fsprogs-cksum-patch-queue.git

With these large patch series it's easier for me to make blanket
changes to fix minor annoyances (such as commit descriptions which are
too wide and make "git log" displays hard to follow) or to merge
patches that are too fine grained[1] together.

Open source project management theory says that it's better to ask the
submitter to do all of this work, but given certain special
circumstances in this particular case, I decided to make the cleanup
work that I do publicly visible, as a compromise. I'll also make some
comments about some of the issues I find (and fix) by replying to the
emails on this mail thread.

This patch series will be maintained so that it can be applied to the
top-most "maint" branch of e2fsprogs.

Darrick, if you have time and would like to make further changes, ping
me off-line and I would be happy to give you write access to the
github tree.

- Ted

2012-03-09 23:36:41

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 01/54] libext2fs: Read and write full size inodes

On Tue, Mar 06, 2012 at 03:57:27PM -0800, Darrick J. Wong wrote:
> Change libext2fs to read and write full-size inodes in preparation for the
> metadata checksumming patchset, which will require this. Due to ABI
> compatibility requirements, this change must be hidden from client programs.
>
> Signed-off-by: Darrick J. Wong <[email protected]>

After applying this first patch, the e2fsprogs regression test suite
blew up spectacularly caused by the malloc-managed free lists pointers
getting corrupted:

*** glibc detected *** /usr/projects/e2fsprogs/e2fsprogs/build/debugfs/debugfs: free(): invalid next size (fast): 0x000000000063f9d0 ***
======= Backtrace: =========
/lib/libc.so.6(+0x77806)[0x7ffff6e4a806]
/lib/libc.so.6(cfree+0x73)[0x7ffff6e510d3]
/usr/projects/e2fsprogs/e2fsprogs/build/lib/libext2fs.so.2(ext2fs_free_mem+0x30)[0x7ffff7bb562f]


Interestingly, valgrind was *not* useful in finding the problem;
apparently it was getting confused by the ext2fs_get_mem()
abstraction, which is unfortunate. I'll have to look into that at
some point.

Anyway, the problem was in ext2fs_write_inode_full(), and it could be
replicated by simply writing to an inode, i.e.

mke2fs -F -O resize_inode -o Linux -b 1024 /tmp/image 16384
debugfs -w -R "set_inode_field <7> mtime now" /tmp/image

is enough to trigger it. The problem is with a 128 byte ext2 file
system, ext2fs_write_inode_full() is passed a large inode and so
bufsize is 156, but EXT2_INODE_SIZE(fs->super) is 128. So at
lib/ext2fs/inode.c:698:

memcpy(w_inode, inode, bufsize);

you end up writing 156 bytes into a memory buffer that was allocated
to a size of 128 bytes. Hilarity ensues.

The fix is relatively simple:

memcpy(w_inode, inode, (bufsize > length) ? length : bufsize);

Anyway, this is *why* running the regression tests are important.
(And why projects which don't have regression test suites are just
asking for trouble.)

They catch all sorts of interesting oversights like this....

- Ted

2012-03-10 05:09:37

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 08/54] e2fsck: Verify and correct inode checksums

On Tue, Mar 06, 2012 at 03:58:12PM -0800, Darrick J. Wong wrote:
> Detect mismatches of the inode and checksum, and prompt the user to fix the
> situation.
>
> Signed-off-by: Darrick J. Wong <[email protected]>

The new problem definitions were inserted into the wrong place in
problem.c. This is something that was picked up by a unit test in the
e2fsck directory:

LD_LIBRARY_PATH=../lib DYLD_LIBRARY_PATH=../lib ./tst_problem
*** Unordered problem table:
curr code = 0x00010065: The bad @b @i looks @n.
*** prev code = 0x00010067: @i %i passes checks, but checksum does not match @i.
*** This is a programming error in e2fsck
make[1]: *** [check] Error 1

The fix was simple; just move the new lines in problem.c down two lines.

> /* Quota inode is user visible */
> { PR_1_QUOTA_INODE_NOT_HIDDEN,
> N_("@q @i is visible to the user. "),
> - PROMPT_CLEAR, PR_PREEN_OK },
> + PROMPT_FIX, PR_PREEN_OK },

PR1_QUOTA_INODE_NOT_HIDDEN isn't currently used, so there's no point
changing it now. Depending on how this bit of the first class quota
patch gets implemented, the message might change completely.

- Ted

2012-03-10 05:22:38

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 29/54] e2fsck: Check extended attribute block checksums

On Tue, Mar 06, 2012 at 04:00:32PM -0800, Darrick J. Wong wrote:
> diff --git a/e2fsck/problem.c b/e2fsck/problem.c
> index d910629..ad6887e 100644
> --- a/e2fsck/problem.c
> +++ b/e2fsck/problem.c
> @@ -961,6 +961,21 @@ static struct e2fsck_problem problem_table[] = {
> "extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"),
> PROMPT_FIX, 0 },
>
> + /* Extended attribute block checksum for inode does not match. */
> + { PR_1_EA_BLOCK_CSUM_INVALID,
> + N_("Extended attribute @a @b %b checksum for @i %i does not "
> + "match. "),
> + PROMPT_CLEAR, 0 },
> +
> + /*
> + * Extended attribute block passes checks, but checksum for inode does
> + * not match.
> + */
> + { PR_1_EA_BLOCK_CSUM_INVALID,
^^^^^^^^^^^^^^^^^^^^^^^^^^

This should be PR_1_EA_BLOCK_ONLY_CSUM_INVALID. This was discovered
again via the tst_problem unit test in the e2fsprogs regression test
suite:

LD_LIBRARY_PATH=../lib DYLD_LIBRARY_PATH=../lib ./tst_problem
*** Duplicate in problem table:
curr code = 0x0001006a: Extended attribute @a @b %b passes checks, but checksum for @i %i does not match.
*** prev code = 0x0001006a: Extended attribute @a @b %b checksum for @i %i does not match.
*** This is a fatal programming error in e2fsck

- Ted

2012-03-10 05:47:06

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 13/54] e2fsck: Verify inode bitmap checksum

On Tue, Mar 06, 2012 at 03:58:44PM -0800, Darrick J. Wong wrote:
> Rewrite the block bitmap when the checksum doesn't match. This is ok since
> e2fsck will have already computed the correct inode bitmap.
>
> Signed-off-by: Darrick J. Wong <[email protected]>

> @@ -74,6 +77,67 @@ void e2fsck_pass5(e2fsck_t ctx)
> print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
> }
>
> +static void check_inode_bitmap_checksum(e2fsck_t ctx)
> +{
> + struct problem_context pctx;
> + struct ext4_group_desc *gdp;
> + char *buf;
> + dgrp_t i;
> + int nbytes;
> + ext2_ino_t ino_itr;
> + errcode_t retval;
> + int csum_flag = 0;
> +
> + /* If bitmap is dirty from being fixed, checksum will be corrected */
> + if (ext2fs_test_ib_dirty(ctx->fs))
> + return;
> +
> + nbytes = (size_t)(EXT2_INODES_PER_GROUP(ctx->fs->super) / 8);
> + retval = ext2fs_get_memalign(ctx->fs->blocksize, ctx->fs->blocksize,
> + &buf);
> + if (retval) {
> + com_err(ctx->program_name, 0,
> + _("check_inode_bitmap_checksum: Memory allocation error"));
> + fatal_error(ctx, 0);
> + }
> +
> + if (EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
> + EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
> + csum_flag = 1;

This patch just looks wrong. RO_COMPAT_GDT_CSUM is the old feature,
and doesn't imply that there will be a checksum in the inode bitmap
block. Shouldn't this be RO_COMPAT_METADATA_CSUM?

By the way, the bugs which I'm finding in this patch series are
seriously degrading my faith about how well both they and the kernel
side patches have been tested.....

At this point I will probably work on other kernel patches, and return
to this later.

- Ted

P.S. Note that if I apply the full patch series, I am still finding
45 (out of 118) test failures from the regression test suite. While I
was bisecting to find errors, I found this one by quick inspection.

2012-03-11 02:12:00

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 13/54] e2fsck: Verify inode bitmap checksum

On Sat, Mar 10, 2012 at 12:46:57AM -0500, Ted Ts'o wrote:
>
> This patch just looks wrong. RO_COMPAT_GDT_CSUM is the old feature,
> and doesn't imply that there will be a checksum in the inode bitmap
> block. Shouldn't this be RO_COMPAT_METADATA_CSUM?

Correction; the big problem here is that check_inode_bitmap_checksum()
(and in the next e2fsck page, check_block_bitmap_checksum()) are are
doing their thing without actually checking for
RO_COMPAT_METADATA_CSUM). These code paths shouldn't be getting
activated at all for non-METADATA_CSUM file systems. But they are.

(And the check_block_bitmap_checksum() function is using
EXT2_BLOCKS_PER_GROUP where it needs to use EXT2_CLUSTERS_PER_GROUP or
it will end up corrupting memory which will cause the bigalloc-related
test f_dup_ba blow up. But again, this code path shouldn't have been
getting activated in the first place.)

- Ted

2012-03-12 21:27:28

by djwong

[permalink] [raw]
Subject: Re: [PATCH 13/54] e2fsck: Verify inode bitmap checksum

On Sat, Mar 10, 2012 at 09:11:50PM -0500, Ted Ts'o wrote:
> On Sat, Mar 10, 2012 at 12:46:57AM -0500, Ted Ts'o wrote:
> >
> > This patch just looks wrong. RO_COMPAT_GDT_CSUM is the old feature,
> > and doesn't imply that there will be a checksum in the inode bitmap
> > block. Shouldn't this be RO_COMPAT_METADATA_CSUM?
>
> Correction; the big problem here is that check_inode_bitmap_checksum()
> (and in the next e2fsck page, check_block_bitmap_checksum()) are are
> doing their thing without actually checking for
> RO_COMPAT_METADATA_CSUM). These code paths shouldn't be getting
> activated at all for non-METADATA_CSUM file systems. But they are.

You're right, the function could be optimized to exit early if metadata_csum
isn't set. The bitmap verify function won't flag errors when metadata_csum is
unset, at least, but I suppose it is unecessary looping and IO.

--D

> (And the check_block_bitmap_checksum() function is using
> EXT2_BLOCKS_PER_GROUP where it needs to use EXT2_CLUSTERS_PER_GROUP or
> it will end up corrupting memory which will cause the bigalloc-related
> test f_dup_ba blow up. But again, this code path shouldn't have been
> getting activated in the first place.)
>
> - Ted
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


2012-03-12 22:24:26

by djwong

[permalink] [raw]
Subject: Re: [PATCH 02/54] libext2fs: Change ext4 on-disk layout to support metadata checksumming

On Fri, Mar 09, 2012 at 05:04:35PM -0500, Ted Ts'o wrote:
> On Tue, Mar 06, 2012 at 03:57:34PM -0800, Darrick J. Wong wrote:
> > Define flags and extend ext4 structure definitions to support metadata
> > checksumming. Ted T'so covered many of these fields in an earlier patch, but
> > there are more required changes to the disk layout.
> >
> > Signed-off-by: Darrick J. Wong <[email protected]>
>
> > @@ -619,7 +630,7 @@ struct ext2_super_block {
> > __u64 s_mmp_block; /* Block for multi-mount protection */
> > __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
> > __u8 s_log_groups_per_flex; /* FLEX_BG group size */
> > - __u8 s_reserved_char_pad;
> > + __u8 s_checksum_type; /* metadata checksum algorithm */
> > __u16 s_reserved_pad; /* Padding to next 32bits */
> > __u64 s_kbytes_written; /* nr of lifetime kilobytes written */
> > __u32 s_snapshot_inum; /* Inode number of active snapshot */
>
> Note: when you change the superblock definition, you *must* also
> update the test in lib/ext2fs/tst_super_size.c.
>
> If you don't then running "make check" from the top-level of the build
> tree will likely bomb out. And you will also reveal to me that you
> haven't run the full set of regression tests before submitting your
> patch series. :-)

Ok, thank you for catching (and fixing) this.

--D
>
> - Ted
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


2012-03-12 22:24:24

by djwong

[permalink] [raw]
Subject: Re: [PATCH 01/54] libext2fs: Read and write full size inodes

On Fri, Mar 09, 2012 at 06:36:31PM -0500, Ted Ts'o wrote:
> On Tue, Mar 06, 2012 at 03:57:27PM -0800, Darrick J. Wong wrote:
> > Change libext2fs to read and write full-size inodes in preparation for the
> > metadata checksumming patchset, which will require this. Due to ABI
> > compatibility requirements, this change must be hidden from client programs.
> >
> > Signed-off-by: Darrick J. Wong <[email protected]>
>
> After applying this first patch, the e2fsprogs regression test suite
> blew up spectacularly caused by the malloc-managed free lists pointers
> getting corrupted:
>
> *** glibc detected *** /usr/projects/e2fsprogs/e2fsprogs/build/debugfs/debugfs: free(): invalid next size (fast): 0x000000000063f9d0 ***
> ======= Backtrace: =========
> /lib/libc.so.6(+0x77806)[0x7ffff6e4a806]
> /lib/libc.so.6(cfree+0x73)[0x7ffff6e510d3]
> /usr/projects/e2fsprogs/e2fsprogs/build/lib/libext2fs.so.2(ext2fs_free_mem+0x30)[0x7ffff7bb562f]
>
>
> Interestingly, valgrind was *not* useful in finding the problem;
> apparently it was getting confused by the ext2fs_get_mem()
> abstraction, which is unfortunate. I'll have to look into that at
> some point.
>
> Anyway, the problem was in ext2fs_write_inode_full(), and it could be
> replicated by simply writing to an inode, i.e.
>
> mke2fs -F -O resize_inode -o Linux -b 1024 /tmp/image 16384
> debugfs -w -R "set_inode_field <7> mtime now" /tmp/image
>
> is enough to trigger it. The problem is with a 128 byte ext2 file
> system, ext2fs_write_inode_full() is passed a large inode and so
> bufsize is 156, but EXT2_INODE_SIZE(fs->super) is 128. So at
> lib/ext2fs/inode.c:698:
>
> memcpy(w_inode, inode, bufsize);
>
> you end up writing 156 bytes into a memory buffer that was allocated
> to a size of 128 bytes. Hilarity ensues.
>
> The fix is relatively simple:
>
> memcpy(w_inode, inode, (bufsize > length) ? length : bufsize);
>
> Anyway, this is *why* running the regression tests are important.
> (And why projects which don't have regression test suites are just
> asking for trouble.)
>
> They catch all sorts of interesting oversights like this....

Hmm, good catch. To be honest I wasn't aware that there /was/ a make check.
I'll contribute my checksum tests when I figure out how to integrate them.

I'll send out patches to fix the other 45 failures this afternoon; I traced
most of them to a mistake I made in e2fsck/rehash.c.

--D


2012-03-12 22:30:20

by djwong

[permalink] [raw]
Subject: Re: [PATCH 13/54] e2fsck: Verify inode bitmap checksum

On Mon, Mar 12, 2012 at 02:25:37PM -0700, Darrick J. Wong wrote:
> On Sat, Mar 10, 2012 at 09:11:50PM -0500, Ted Ts'o wrote:
> > On Sat, Mar 10, 2012 at 12:46:57AM -0500, Ted Ts'o wrote:
> > >
> > > This patch just looks wrong. RO_COMPAT_GDT_CSUM is the old feature,
> > > and doesn't imply that there will be a checksum in the inode bitmap
> > > block. Shouldn't this be RO_COMPAT_METADATA_CSUM?
> >
> > Correction; the big problem here is that check_inode_bitmap_checksum()
> > (and in the next e2fsck page, check_block_bitmap_checksum()) are are
> > doing their thing without actually checking for
> > RO_COMPAT_METADATA_CSUM). These code paths shouldn't be getting
> > activated at all for non-METADATA_CSUM file systems. But they are.
>
> You're right, the function could be optimized to exit early if metadata_csum
> isn't set. The bitmap verify function won't flag errors when metadata_csum is
> unset, at least, but I suppose it is unecessary looping and IO.
>
> --D
>
> > (And the check_block_bitmap_checksum() function is using
> > EXT2_BLOCKS_PER_GROUP where it needs to use EXT2_CLUSTERS_PER_GROUP or
> > it will end up corrupting memory which will cause the bigalloc-related
> > test f_dup_ba blow up. But again, this code path shouldn't have been
> > getting activated in the first place.)

As for this bit -- I should have paid closer attention when bigalloc went in.
I'll send out a corrected patch.

--D
> >
> > - Ted
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


2012-03-12 22:44:14

by djwong

[permalink] [raw]
Subject: Re: [PATCH 29/54] e2fsck: Check extended attribute block checksums

On Sat, Mar 10, 2012 at 12:22:31AM -0500, Ted Ts'o wrote:
> On Tue, Mar 06, 2012 at 04:00:32PM -0800, Darrick J. Wong wrote:
> > diff --git a/e2fsck/problem.c b/e2fsck/problem.c
> > index d910629..ad6887e 100644
> > --- a/e2fsck/problem.c
> > +++ b/e2fsck/problem.c
> > @@ -961,6 +961,21 @@ static struct e2fsck_problem problem_table[] = {
> > "extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"),
> > PROMPT_FIX, 0 },
> >
> > + /* Extended attribute block checksum for inode does not match. */
> > + { PR_1_EA_BLOCK_CSUM_INVALID,
> > + N_("Extended attribute @a @b %b checksum for @i %i does not "
> > + "match. "),
> > + PROMPT_CLEAR, 0 },
> > +
> > + /*
> > + * Extended attribute block passes checks, but checksum for inode does
> > + * not match.
> > + */
> > + { PR_1_EA_BLOCK_CSUM_INVALID,
> ^^^^^^^^^^^^^^^^^^^^^^^^^^
>
> This should be PR_1_EA_BLOCK_ONLY_CSUM_INVALID. This was discovered
> again via the tst_problem unit test in the e2fsprogs regression test
> suite:
>
> LD_LIBRARY_PATH=../lib DYLD_LIBRARY_PATH=../lib ./tst_problem
> *** Duplicate in problem table:
> curr code = 0x0001006a: Extended attribute @a @b %b passes checks, but checksum for @i %i does not match.
> *** prev code = 0x0001006a: Extended attribute @a @b %b checksum for @i %i does not match.
> *** This is a fatal programming error in e2fsck
>
> - Ted

Geesh, thank you for finding and fixing that one.

--D


2012-03-12 22:49:20

by djwong

[permalink] [raw]
Subject: Re: [PATCH 08/54] e2fsck: Verify and correct inode checksums

On Sat, Mar 10, 2012 at 12:09:28AM -0500, Ted Ts'o wrote:
> On Tue, Mar 06, 2012 at 03:58:12PM -0800, Darrick J. Wong wrote:
> > Detect mismatches of the inode and checksum, and prompt the user to fix the
> > situation.
> >
> > Signed-off-by: Darrick J. Wong <[email protected]>
>
> The new problem definitions were inserted into the wrong place in
> problem.c. This is something that was picked up by a unit test in the
> e2fsck directory:
>
> LD_LIBRARY_PATH=../lib DYLD_LIBRARY_PATH=../lib ./tst_problem
> *** Unordered problem table:
> curr code = 0x00010065: The bad @b @i looks @n.
> *** prev code = 0x00010067: @i %i passes checks, but checksum does not match @i.
> *** This is a programming error in e2fsck
> make[1]: *** [check] Error 1
>
> The fix was simple; just move the new lines in problem.c down two lines.
>
> > /* Quota inode is user visible */
> > { PR_1_QUOTA_INODE_NOT_HIDDEN,
> > N_("@q @i is visible to the user. "),
> > - PROMPT_CLEAR, PR_PREEN_OK },
> > + PROMPT_FIX, PR_PREEN_OK },
>
> PR1_QUOTA_INODE_NOT_HIDDEN isn't currently used, so there's no point
> changing it now. Depending on how this bit of the first class quota
> patch gets implemented, the message might change completely.

Ugh, that looks like a merge error on my end. Sorry and thanks for fixing it.

--D


2012-03-12 22:53:45

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 01/54] libext2fs: Read and write full size inodes

On Mon, Mar 12, 2012 at 03:23:25PM -0700, Darrick J. Wong wrote:
>
> I'll send out patches to fix the other 45 failures this afternoon; I traced
> most of them to a mistake I made in e2fsck/rehash.c.

Do you have a github login? It will be a lot easier if you just
update the patch queue at:

https://github.com/tytso/e2fsprogs-cksum-patch-queue

- Ted

2012-03-13 00:33:22

by djwong

[permalink] [raw]
Subject: [PATCH v3.1 13/54] e2fsck: Verify inode bitmap checksum

Rewrite the block bitmap when the checksum doesn't match. This is
ok since e2fsck will have already computed the correct inode bitmap.

Signed-off-by: Darrick J. Wong <[email protected]>
---

e2fsck/pass5.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
e2fsck/problem.c | 5 ++++
e2fsck/problem.h | 3 +++
3 files changed, 70 insertions(+), 0 deletions(-)

diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index 1e836e3..bfcf384 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -27,6 +27,7 @@ static void check_block_bitmaps(e2fsck_t ctx);
static void check_inode_bitmaps(e2fsck_t ctx);
static void check_inode_end(e2fsck_t ctx);
static void check_block_end(e2fsck_t ctx);
+static void check_inode_bitmap_checksum(e2fsck_t ctx);

void e2fsck_pass5(e2fsck_t ctx)
{
@@ -64,6 +65,8 @@ void e2fsck_pass5(e2fsck_t ctx)
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;

+ check_inode_bitmap_checksum(ctx);
+
ext2fs_free_inode_bitmap(ctx->inode_used_map);
ctx->inode_used_map = 0;
ext2fs_free_inode_bitmap(ctx->inode_dir_map);
@@ -74,6 +77,65 @@ void e2fsck_pass5(e2fsck_t ctx)
print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
}

+static void check_inode_bitmap_checksum(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ struct ext4_group_desc *gdp;
+ char *buf;
+ dgrp_t i;
+ int nbytes;
+ ext2_ino_t ino_itr;
+ errcode_t retval;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return;
+
+ /* If bitmap is dirty from being fixed, checksum will be corrected */
+ if (ext2fs_test_ib_dirty(ctx->fs))
+ return;
+
+ nbytes = (size_t)(EXT2_INODES_PER_GROUP(ctx->fs->super) / 8);
+ retval = ext2fs_get_memalign(ctx->fs->blocksize, ctx->fs->blocksize,
+ &buf);
+ if (retval) {
+ com_err(ctx->program_name, 0,
+ _("check_inode_bitmap_checksum: Memory allocation error"));
+ fatal_error(ctx, 0);
+ }
+
+ clear_problem_context(&pctx);
+ for (i = 0; i < ctx->fs->group_desc_count; i++) {
+ if (ext2fs_bg_flags_test(ctx->fs, i, EXT2_BG_INODE_UNINIT))
+ continue;
+
+ ino_itr = 1 + (i * (nbytes << 3));
+ gdp = (struct ext4_group_desc *)ext2fs_group_desc(ctx->fs,
+ ctx->fs->group_desc, i);
+ retval = ext2fs_get_inode_bitmap_range2(ctx->fs->inode_map,
+ ino_itr, nbytes << 3,
+ buf);
+ if (retval)
+ break;
+
+ if (ext2fs_inode_bitmap_csum_verify(ctx->fs, i, buf, nbytes))
+ continue;
+ pctx.group = i;
+ if (!fix_problem(ctx, PR_5_INODE_BITMAP_CSUM_INVALID, &pctx))
+ continue;
+
+ /*
+ * Fixing one checksum will rewrite all of them. The bitmap
+ * will be checked against the one we made during pass1 for
+ * discrepancies, and fixed if need be.
+ */
+ ext2fs_mark_ib_dirty(ctx->fs);
+ break;
+ }
+
+ ext2fs_free_mem(&buf);
+}
+
static void e2fsck_discard_blocks(e2fsck_t ctx, io_manager manager,
blk64_t start, blk64_t count)
{
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index a2a1a44..79ad34d 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1684,6 +1684,11 @@ static struct e2fsck_problem problem_table[] = {
N_("@g %g @i(s) in use but @g is marked INODE_UNINIT\n"),
PROMPT_FIX, PR_PREEN_OK },

+ /* Group N inode bitmap does not match checksum */
+ { PR_5_INODE_BITMAP_CSUM_INVALID,
+ N_("@g %g @i bitmap does not match checksum\n"),
+ PROMPT_FIX, PR_LATCH_IBITMAP | PR_PREEN_OK },
+
/* Post-Pass 5 errors */

/* Recreate journal if E2F_FLAG_JOURNAL_INODE flag is set */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 5797fe2..0adc7ea 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1019,6 +1019,9 @@ struct problem_context {
/* Inode in use but group is marked INODE_UNINIT */
#define PR_5_INODE_UNINIT 0x050019

+/* Inode bitmap checksum does not match */
+#define PR_5_INODE_BITMAP_CSUM_INVALID 0x05001A
+
/*
* Post-Pass 5 errors
*/


2012-03-13 00:33:43

by djwong

[permalink] [raw]
Subject: [PATCH v3.1 16/54] e2fsck: Verify block bitmap checksum

Check block bitmap checksum and write a new checksum if the
verification fails. This is ok because e2fsck has already computed
the correct block bitmap.

Signed-off-by: Darrick J. Wong <[email protected]>
---

e2fsck/pass5.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
e2fsck/problem.c | 5 ++++
e2fsck/problem.h | 3 +++
3 files changed, 71 insertions(+), 0 deletions(-)

diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index bfcf384..64a751d 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -28,6 +28,7 @@ static void check_inode_bitmaps(e2fsck_t ctx);
static void check_inode_end(e2fsck_t ctx);
static void check_block_end(e2fsck_t ctx);
static void check_inode_bitmap_checksum(e2fsck_t ctx);
+static void check_block_bitmap_checksum(e2fsck_t ctx);

void e2fsck_pass5(e2fsck_t ctx)
{
@@ -66,6 +67,7 @@ void e2fsck_pass5(e2fsck_t ctx)
return;

check_inode_bitmap_checksum(ctx);
+ check_block_bitmap_checksum(ctx);

ext2fs_free_inode_bitmap(ctx->inode_used_map);
ctx->inode_used_map = 0;
@@ -136,6 +138,67 @@ static void check_inode_bitmap_checksum(e2fsck_t ctx)
ext2fs_free_mem(&buf);
}

+static void check_block_bitmap_checksum(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ struct ext4_group_desc *gdp;
+ char *buf;
+ dgrp_t i;
+ int nbytes;
+ blk64_t blk_itr;
+ errcode_t retval;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return;
+
+ /* If bitmap is dirty from being fixed, checksum will be corrected */
+ if (ext2fs_test_bb_dirty(ctx->fs))
+ return;
+
+ nbytes = (size_t)(EXT2_CLUSTERS_PER_GROUP(ctx->fs->super) / 8);
+ retval = ext2fs_get_memalign(ctx->fs->blocksize, ctx->fs->blocksize,
+ &buf);
+ if (retval) {
+ com_err(ctx->program_name, 0,
+ _("check_block_bitmap_checksum: Memory allocation error"));
+ fatal_error(ctx, 0);
+ }
+
+ clear_problem_context(&pctx);
+ for (i = 0; i < ctx->fs->group_desc_count; i++) {
+ if (ext2fs_bg_flags_test(ctx->fs, i, EXT2_BG_BLOCK_UNINIT))
+ continue;
+
+ blk_itr = EXT2FS_B2C(ctx->fs,
+ ctx->fs->super->s_first_data_block) +
+ (i * (nbytes << 3));
+ gdp = (struct ext4_group_desc *)ext2fs_group_desc(ctx->fs,
+ ctx->fs->group_desc, i);
+ retval = ext2fs_get_block_bitmap_range2(ctx->fs->block_map,
+ blk_itr, nbytes << 3,
+ buf);
+ if (retval)
+ break;
+
+ if (ext2fs_block_bitmap_csum_verify(ctx->fs, i, buf, nbytes))
+ continue;
+ pctx.group = i;
+ if (!fix_problem(ctx, PR_5_BLOCK_BITMAP_CSUM_INVALID, &pctx))
+ continue;
+
+ /*
+ * Fixing one checksum will rewrite all of them. The bitmap
+ * will be checked against the one we made during pass1 for
+ * discrepancies, and fixed if need be.
+ */
+ ext2fs_mark_bb_dirty(ctx->fs);
+ break;
+ }
+
+ ext2fs_free_mem(&buf);
+}
+
static void e2fsck_discard_blocks(e2fsck_t ctx, io_manager manager,
blk64_t start, blk64_t count)
{
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 79ad34d..8476bc2 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1689,6 +1689,11 @@ static struct e2fsck_problem problem_table[] = {
N_("@g %g @i bitmap does not match checksum\n"),
PROMPT_FIX, PR_LATCH_IBITMAP | PR_PREEN_OK },

+ /* Group N block bitmap does not match checksum */
+ { PR_5_BLOCK_BITMAP_CSUM_INVALID,
+ N_("@g %g @b bitmap does not match checksum\n"),
+ PROMPT_FIX, PR_LATCH_BBITMAP | PR_PREEN_OK },
+
/* Post-Pass 5 errors */

/* Recreate journal if E2F_FLAG_JOURNAL_INODE flag is set */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 0adc7ea..39e6dba 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1022,6 +1022,9 @@ struct problem_context {
/* Inode bitmap checksum does not match */
#define PR_5_INODE_BITMAP_CSUM_INVALID 0x05001A

+/* Block bitmap checksum does not match */
+#define PR_5_BLOCK_BITMAP_CSUM_INVALID 0x05001B
+
/*
* Post-Pass 5 errors
*/


2012-03-13 00:35:51

by djwong

[permalink] [raw]
Subject: [PATCH v3.1 24/54] e2fsck: Verify htree root/node checksums

Check htree internal node checksums. If broken, ask user to clear
the htree index and recreate it later.

v3.1: Only allow lost+found to be rebuilt if metadata_csum is set --
this is how checksum errors in directories are fixed. This fixes a
large number of regressions.

Signed-off-by: Darrick J. Wong <[email protected]>
---

e2fsck/e2fsck.h | 2 ++
e2fsck/pass2.c | 34 +++++++++++++++++++++++++++++-----
e2fsck/problem.c | 10 ++++++++++
e2fsck/problem.h | 6 ++++++
e2fsck/rehash.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
5 files changed, 90 insertions(+), 8 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index ed8b0c7..c6dab54 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -483,6 +483,8 @@ extern void region_free(region_t region);
extern int region_allocate(region_t region, region_addr_t start, int n);

/* rehash.c */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino);
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino);
errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino);
void e2fsck_rehash_directories(e2fsck_t ctx);

diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 882950d..e4a06f3 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -527,7 +527,7 @@ static void parse_int_node(ext2_filsys fs,
struct ext2_db_entry2 *db,
struct check_dir_struct *cd,
struct dx_dir_info *dx_dir,
- char *block_buf)
+ char *block_buf, int failed_csum)
{
struct ext2_dx_root_info *root;
struct ext2_dx_entry *ent;
@@ -538,6 +538,7 @@ static void parse_int_node(ext2_filsys fs,
ext2_dirhash_t min_hash = 0xffffffff;
ext2_dirhash_t max_hash = 0;
ext2_dirhash_t hash = 0, prev_hash;
+ int csum_size = 0;

if (db->blockcnt == 0) {
root = (struct ext2_dx_root_info *) (block_buf + 24);
@@ -552,9 +553,22 @@ static void parse_int_node(ext2_filsys fs,
#endif

ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+
+ if (failed_csum &&
+ (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
+ fix_problem(cd->ctx, PR_2_HTREE_ROOT_CSUM_INVALID,
+ &cd->pctx)))
+ goto clear_and_exit;
} else {
ent = (struct ext2_dx_entry *) (block_buf+8);
+
+ if (failed_csum &&
+ (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
+ fix_problem(cd->ctx, PR_2_HTREE_NODE_CSUM_INVALID,
+ &cd->pctx)))
+ goto clear_and_exit;
}
+
limit = (struct ext2_dx_countlimit *) ent;

#ifdef DX_DEBUG
@@ -565,8 +579,12 @@ static void parse_int_node(ext2_filsys fs,
#endif

count = ext2fs_le16_to_cpu(limit->count);
- expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
- sizeof(struct ext2_dx_entry);
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+ expect_limit = (fs->blocksize -
+ (csum_size + ((char *) ent - block_buf))) /
+ sizeof(struct ext2_dx_entry);
if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
@@ -632,6 +650,7 @@ static void parse_int_node(ext2_filsys fs,
clear_and_exit:
clear_htree(cd->ctx, cd->pctx.ino);
dx_dir->numblocks = 0;
+ e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino);
}
#endif /* ENABLE_HTREE */

@@ -734,6 +753,7 @@ static int check_dir_block(ext2_filsys fs,
struct problem_context pctx;
int dups_found = 0;
int ret;
+ int dx_csum_size = 0;

cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -745,6 +765,10 @@ static int check_dir_block(ext2_filsys fs,
if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
return DIRENT_ABORT;

+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ dx_csum_size = sizeof(struct ext2_dx_tail);
+
/*
* Make sure the inode is still in use (could have been
* deleted in the duplicate/bad blocks pass.
@@ -834,7 +858,7 @@ static int check_dir_block(ext2_filsys fs,
(rec_len == fs->blocksize) &&
(dirent->name_len == 0) &&
(ext2fs_le16_to_cpu(limit->limit) ==
- ((fs->blocksize-8) /
+ ((fs->blocksize - (8 + dx_csum_size)) /
sizeof(struct ext2_dx_entry))))
dx_db->type = DX_DIRBLOCK_NODE;
}
@@ -1121,7 +1145,7 @@ out_htree:
cd->pctx.dir = cd->pctx.ino;
if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
(dx_db->type == DX_DIRBLOCK_NODE))
- parse_int_node(fs, db, cd, dx_dir, buf);
+ parse_int_node(fs, db, cd, dx_dir, buf, 0);
}
#endif /* ENABLE_HTREE */
if (offset != fs->blocksize) {
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 0520010..d0f024a 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1383,6 +1383,16 @@ static struct e2fsck_problem problem_table[] = {
N_("i_file_acl_hi @F %N, @s zero.\n"),
PROMPT_CLEAR, PR_PREEN_OK },

+ /* htree root node fails checksum */
+ { PR_2_HTREE_ROOT_CSUM_INVALID,
+ N_("@p @h %d: root node fails checksum\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* htree internal node fails checksum */
+ { PR_2_HTREE_NODE_CSUM_INVALID,
+ N_("@p @h %d: internal node fails checksum\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
/* Pass 3 errors */

/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 2adb4b9..5cf80fb 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -827,6 +827,12 @@ struct problem_context {
/* i_file_acl_hi should be zero */
#define PR_2_I_FILE_ACL_HI_ZERO 0x020048

+/* htree root node fails checksum */
+#define PR_2_HTREE_ROOT_CSUM_INVALID 0x020049
+
+/* htree node fails checksum */
+#define PR_2_HTREE_NODE_CSUM_INVALID 0x02004A
+
/*
* Pass 3 errors
*/
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 15993b3..338a432 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -52,6 +52,25 @@
#include "e2fsck.h"
#include "problem.h"

+/* Schedule a dir to be rebuilt during pass 3A. */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino)
+{
+ if (!ctx->dirs_to_hash)
+ ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
+ if (ctx->dirs_to_hash)
+ ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+}
+
+/* Ask if a dir will be rebuilt during pass 3A. */
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino)
+{
+ if (ctx->options & E2F_OPT_COMPRESS_DIRS)
+ return 1;
+ if (!ctx->dirs_to_hash)
+ return 0;
+ return ext2fs_u32_list_test(ctx->dirs_to_hash, ino);
+}
+
struct fill_dir_struct {
char *buf;
struct ext2_inode *inode;
@@ -495,6 +514,7 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
struct ext2_dx_root_info *root;
struct ext2_dx_countlimit *limits;
int filetype = 0;
+ int csum_size = 0;

if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
filetype = EXT2_FT_DIR << 8;
@@ -519,8 +539,13 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
root->indirect_levels = 0;
root->unused_flags = 0;

+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+
limits = (struct ext2_dx_countlimit *) (buf+32);
- limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
+ limits->limit = (fs->blocksize - (32 + csum_size)) /
+ sizeof(struct ext2_dx_entry);
limits->count = 0;

return root;
@@ -531,14 +556,20 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
{
struct ext2_dir_entry *dir;
struct ext2_dx_countlimit *limits;
+ int csum_size = 0;

memset(buf, 0, fs->blocksize);
dir = (struct ext2_dir_entry *) buf;
dir->inode = 0;
(void) ext2fs_set_rec_len(fs, fs->blocksize, dir);

+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+
limits = (struct ext2_dx_countlimit *) (buf+8);
- limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
+ limits->limit = (fs->blocksize - (8 + csum_size)) /
+ sizeof(struct ext2_dx_entry);
limits->count = 0;

return (struct ext2_dx_entry *) limits;
@@ -865,8 +896,17 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
if (!ext2fs_u32_list_iterate(iter, &ino))
break;
}
- if (ino == ctx->lost_and_found)
+
+ /*
+ * If metadata_csum is enabled, then lost+found must not be
+ * excluded from cleanups or else checksum errors won't get
+ * fixed.
+ */
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ ino == ctx->lost_and_found)
continue;
+
pctx.dir = ino;
if (first) {
fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);


2012-03-13 00:38:14

by djwong

[permalink] [raw]
Subject: [PATCH v3.1 37/54] e2fsck: Ensure block group checksum uses metadata_csum algorithm

Use the helper function to determine if group descriptors have a
checksum. Ensure that metadata_csum and uninit_bg flags are not set
simultaneously, as part of pass 0.

v3.1: Reworked bitmap checksum functions in e2fsck; patch shrunk.

Signed-off-by: Darrick J. Wong <[email protected]>
---

e2fsck/pass5.c | 6 ++----
e2fsck/problem.c | 9 +++++++++
e2fsck/problem.h | 5 +++++
e2fsck/super.c | 16 ++++++++++++++--
e2fsck/unix.c | 2 +-
5 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index 64a751d..13c946f 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -318,8 +318,7 @@ static void check_block_bitmaps(e2fsck_t ctx)
goto errout;
}

- csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ csum_flag = ext2fs_has_group_desc_csum(fs);
redo_counts:
had_problem = 0;
save_problem = 0;
@@ -595,8 +594,7 @@ static void check_inode_bitmaps(e2fsck_t ctx)
goto errout;
}

- csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ csum_flag = ext2fs_has_group_desc_csum(fs);
redo_counts:
had_problem = 0;
save_problem = 0;
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index b1faba0..c792a25 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -423,6 +423,15 @@ static struct e2fsck_problem problem_table[] = {
N_("@S has invalid MMP magic. "),
PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},

+ /*
+ * metadata_csum implies uninit_bg; both feature bits cannot
+ * be set simultaneously.
+ */
+ { PR_0_META_AND_GDT_CSUM_SET,
+ N_("@S metadata_csum supersedes uninit_bg; both feature "
+ "bits cannot be set simultaneously."),
+ PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},
+
/* Pass 1 errors */

/* Pass 1: Checking inodes, blocks, and sizes */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index b8a7548..e47984c 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -242,6 +242,11 @@ struct problem_context {
/* Superblock has invalid MMP magic. */
#define PR_0_MMP_INVALID_MAGIC 0x000043

+/*
+ * metadata_csum supersedes uninit_bg; both feature bits cannot be set
+ * simultaneously.
+ */
+#define PR_0_META_AND_GDT_CSUM_SET 0x000044

/*
* Pass 1 errors
diff --git a/e2fsck/super.c b/e2fsck/super.c
index dbd337c..939a71e 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -577,14 +577,26 @@ void check_super_block(e2fsck_t ctx)
}
}

+ /* Are metadata_csum and uninit_bg both set? */
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+ fix_problem(ctx, PR_0_META_AND_GDT_CSUM_SET, &pctx)) {
+ fs->super->s_feature_ro_compat &=
+ ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
+ ext2fs_mark_super_dirty(fs);
+ for (i = 0; i < fs->group_desc_count; i++)
+ ext2fs_group_desc_csum_set(fs, i);
+ }
+
/*
* Verify the group descriptors....
*/
first_block = sb->s_first_data_block;
last_block = ext2fs_blocks_count(sb)-1;

- csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ csum_flag = ext2fs_has_group_desc_csum(fs);
for (i = 0; i < fs->group_desc_count; i++) {
pctx.group = i;

diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index a8e3775..678ce9b 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1653,7 +1653,7 @@ no_journal:
}

if ((run_result & E2F_FLAG_CANCEL) == 0 &&
- sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM &&
+ ext2fs_has_group_desc_csum(ctx->fs) &&
!(ctx->options & E2F_OPT_READONLY)) {
retval = ext2fs_set_gdt_csum(ctx->fs);
if (retval) {


2012-03-13 00:41:39

by djwong

[permalink] [raw]
Subject: Re: [PATCH v3 00/54] e2fsprogs: Add metadata checksumming

On Fri, Mar 09, 2012 at 05:37:28PM -0500, Ted Ts'o wrote:
> I've imported this patch set into a guilt patch series, which makes it
> easier for me to work on the patch series and make minor tweaks so
> that I can get interim e2fsprogs support up and going (and at least
> building and passing the regression test suite) so I can test and play
> with the kernel side patches.
>
> People who want to follow along can look here:
>
> https://github.com/tytso/e2fsprogs-cksum-patch-queue
>
> ... or at the read-only git tree which you can find here:
>
> git://github.com/tytso/e2fsprogs-cksum-patch-queue.git
>
> With these large patch series it's easier for me to make blanket
> changes to fix minor annoyances (such as commit descriptions which are
> too wide and make "git log" displays hard to follow) or to merge
> patches that are too fine grained[1] together.
>
> Open source project management theory says that it's better to ask the
> submitter to do all of this work, but given certain special
> circumstances in this particular case, I decided to make the cleanup
> work that I do publicly visible, as a compromise. I'll also make some
> comments about some of the issues I find (and fix) by replying to the
> emails on this mail thread.
>
> This patch series will be maintained so that it can be applied to the
> top-most "maint" branch of e2fsprogs.
>
> Darrick, if you have time and would like to make further changes, ping
> me off-line and I would be happy to give you write access to the
> github tree.

Here's a copy of my testing script....

--D

#!/bin/bash -e

# ext4 metadata checksum test script
# licensed under gplv2
# copyright (c) 2011 ibm corporation. all rights reserved.

function print_help {
echo "Usage: $0 [-d device] [-m mountpoint] [-v verbs] [-o mkfs_opts] [-e mkfs_features] [-l] [-n] [-p e2fsprogs_path] [-z mount_opts] [-q]"
echo "-l Use valgrind"
echo "-n No checksumming"
echo "-q Enable MMP"
echo "verbs is any combination of:"
echo "${VERBS}"
exit 1
}

type attr || (echo "Must install attr program."; exit 5)
type /usr/bin/time || (echo "Must install time program."; exit 5)

DEV=/dev/vda
MNT=/mnt
VALGRIND="valgrind" # --leak-check=full --show-reachable=yes"
VALGRIND=
MKFS_OPTS="64bit"
FUZZ="Fuzzy Wuzzy was a bear. Fuzzy Wuzzy had no hair. I guess he wasn't fuzzy, was he?"
VERBS="$(grep ^function $0 | grep '_test ' | awk '{print $2}')"
E2FSPROGS=/djwong/e2fsprogs-csum

while getopts "p:d:m:v:z:o:e:l" OPTION; do
case "$OPTION" in
"z")
MOUNT_OPTS="${OPTARG}"
;;
"d")
DEV="${OPTARG}"
;;
"m")
MNT="${OPTARG}"
;;
"v")
VERBS="${OPTARG}"
;;
"o")
MKFS_OPTS="${OPTARG}"
;;
"q")
MKFS_OPTS="${MKFS_OPTS},mmp"
;;
"e")
MKFS_FEATURES="${OPTARG}"
;;
"l")
VALGRIND=valgrind
;;
"n")
NO_CSUM=1
;;
"p")
E2FSPROGS="${OPTARG}"
;;
*)
print_help
exit 1
;;
esac
done
if [ ! -z "${MKFS_OPTS}" ]; then
MKFS_OPTS="-O ${MKFS_OPTS}"
fi
if [ ! -z "${MKFS_FEATURES}" ]; then
MKFS_FEATURES="-E ${MKFS_FEATURES}"
fi
if [ ! -z "${MOUNT_OPTS}" ]; then
MOUNT_OPTS="-o ${MOUNT_OPTS}"
fi

function msg {
if [ -x /usr/bin/figlet ]; then
figlet -f mono9 -w 132 $*
echo $* > /dev/ttyprintk
else
echo $* | tee /dev/ttyprintk
fi
}

if [ ! -b "${DEV}" ]; then
echo "${DEV} is not a block device?"
exit 0
fi

umount $MNT || true
rmmod ext4 || true
rmmod jbd2 || true
rmmod crc32c || true
rmmod crc32c-intel || true

dmesg -c > /dev/null
trap 'echo "test ended" | tee /dev/ttyprintk; dmesg' EXIT
msg "$0: Begin test @ $(date)"
set -x

#####################################
function simple_test {
msg "Create fs with files, no checksums"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 $MKFS_OPTS $MKFS_FEATURES

mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRd /etc/ $MNT/
mkdir -p $MNT/fragged
dd if=/dev/zero bs=4k count=1 of=$MNT/fragged/fragfile
sync
set +x
echo + frag
for i in `seq 1 8`; do
echo moo > $MNT/fragged/a$i
sync
dd if=/dev/zero bs=4k count=1 >> $MNT/fragged/fragfile
sync
done
set -x
echo moo > $MNT/ea_file
set +x
echo + set_ea
for i in `seq -w 1 100`; do
attr -s $i -V $i $MNT/ea_file
done
set -x
umount $MNT

#########################################
msg "Enable checksums"
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -o -type d -print0 | xargs -0 touch
attr -l $MNT/ea_file
umount $MNT

# Copy stuff into fs to create inodes w/ checksums
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
mkdir $MNT/moo
cp -pRd /etc/ $MNT/moo/
umount $MNT

# Create htree dir
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
mkdir -p $MNT/bigdir2
set +x
echo + htree2
for i in `seq 1 256`; do
echo moo > "$MNT/bigdir2/$(echo "$(date)_$i" | md5sum)"
done
set -x
umount $MNT

########################################
msg "Mess with extent tree"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
set +x
echo + frag
for i in `seq 9 18`; do
echo moo > $MNT/fragged/a$i
sync
dd if=/dev/zero bs=4k count=1 >> $MNT/fragged/fragfile
sync
done
set -x
umount $MNT

# Keep rewriting the extent tree just to drive it crazy
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
set +x
echo + frag
for i in `seq 19 28`; do
echo moo > $MNT/fragged/a$i
sync
dd if=/dev/zero bs=4k count=1 >> $MNT/fragged/fragfile
sync
done
set -x
umount $MNT

# Truncate the file
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
truncate -s -64k $MNT/fragged/fragfile
umount $MNT

# Keep rewriting the extent tree just to drive it crazy
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
set +x
echo + frag
for i in `seq 29 38`; do
echo moo > $MNT/fragged/a$i
sync
dd if=/dev/zero bs=4k count=1 >> $MNT/fragged/fragfile
sync
done
set -x
umount $MNT

# Re-walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -print0 | xargs -0 cat > /dev/null
umount $MNT/

# Check fs again
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

##################################################
msg "Disable checksums"
${E2FSPROGS}/misc/dumpe2fs $DEV > /root/before
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O ^metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
${E2FSPROGS}/misc/dumpe2fs $DEV > /root/after

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -o -type d -print0 | xargs -0 touch
umount $MNT

# Copy stuff into fs to create inodes w/o checksums
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
mkdir $MNT/oom
cp -pRd /etc/ $MNT/oom/
umount $MNT

# Re-walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -print0 | xargs -0 cat > /dev/null
umount $MNT/

# Check fs again
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

####################################
msg "Re-enable checksums"
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -o -type d -print0 | xargs -0 touch
umount $MNT

# Remove file
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
dd if=/dev/zero of=$MNT/bigfile bs=1024k count=16
umount $MNT
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
rm -rf $MNT/bigfile
umount $MNT

#############################
# create xattrs
msg "mess with xattrs"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo attrs > $MNT/attrfile
set +x
echo + set_xattrs
for i in `seq -w 1 100`; do
attr -s $i -V $i $MNT/attrfile
done
set -x
attr -l $MNT/attrfile
umount $MNT

# Check fs one last time
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#################################
function corrupt_inode_csum_test {
msg "Deliberately corrupt inode checksum"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 $MKFS_OPTS $MKFS_FEATURES -b 4096 -I 4096 -E lazy_itable_init=1
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
mkdir $MNT/filldir
set +x
echo + filldir
for i in `seq 1 10000`; do
echo moo > $MNT/filldir/$i
done
set -x
cp -pRd /etc/ $MNT/
cp -pRd /etc/ $MNT/moo
umount $MNT

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
set +x
echo + corrupt_inode_csum
FIRST_GROUP=1
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs $DEV | grep 'Inode table' | sed -e 's/^.*at \([0-9]*\)-\([0-9]*\).*$/\1 \2/g' | while read start end; do
if [ $FIRST_GROUP -gt 0 ]; then
FIRST_GROUP=0
continue
fi
echo -en " $start"
if [ $((end - start)) -gt 4 ]; then
end=$((start + 4))
fi
seq $start $end | while read block; do
offset=$(( (start * (4096 / 4)) + 31 ))
yes "${FUZZ}" | dd of=$DEV bs=4 seek=$offset count=1 > /dev/null 2> /dev/null
done
done
echo
set -x

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
(find $MNT -type f -o -type d -print0 | xargs -0 touch) || true
umount $MNT

# Repair fs
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -o -type d -print0 | xargs -0 touch
umount $MNT

# Check fs one last time
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#################################
function corrupt_inode_test {
msg "Deliberately corrupt inode table"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 $MKFS_OPTS $MKFS_FEATURES
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
mkdir $MNT/filldir
set +x
echo + filldir
for i in `seq 1 10000`; do
echo moo > $MNT/filldir/$i
done
set -x
cp -pRd /etc/ $MNT/
cp -pRd /etc/ $MNT/moo
umount $MNT

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

set +x
echo + corrupt_inode_table
BLOCKSIZE=$(${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Block size:' | sed -e 's/^.* \([0-9]*\)$/\1/g')
BLOCKS=$(${E2FSPROGS}/misc/dumpe2fs $DEV -h | grep 'Inode blocks per group:' | sed -e 's/^.* \([0-9]*\)$/\1/g')
FIRST_GROUP=1
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs $DEV | grep 'Inode table' | sed -e 's/^.*at \([0-9]*\)-.*$/\1/g' | while read f; do
if [ $FIRST_GROUP -gt 0 ]; then
FIRST_GROUP=0
continue
fi
echo -en " $f"
yes "${FUZZ}" | dd of=$DEV bs=$BLOCKSIZE seek=$f count=$BLOCKS > /dev/null 2> /dev/null
done
echo
set -x

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
(find $MNT -type f -o -type d -print0 | xargs -0 touch) || true
umount $MNT

# Repair fs
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -o -type d -print0 | xargs -0 touch
umount $MNT

# Check fs one last time
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#################################
function corrupt_ibitmap_test {
msg "Deliberately corrupt inode bitmap"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 $MKFS_OPTS $MKFS_FEATURES
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRd /etc/ $MNT/
cp -pRd /etc/ $MNT/moo
umount $MNT

set +x
echo + corrupt_inode_bitmap
BLOCKSIZE=$(${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Block size:' | sed -e 's/^.* \([0-9]*\)$/\1/g')
FIRST_GROUP=1
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs $DEV | grep 'Inode bitmap at' | sed -e 's/^.*Inode bitmap at //g' -e 's/ .*$//g' | while read f; do
if [ $FIRST_GROUP -gt 0 ]; then
FIRST_GROUP=0
continue
fi
echo -en " $f"
yes "${FUZZ}" | dd of=$DEV bs=$BLOCKSIZE seek=$f count=1 > /dev/null 2> /dev/null
done
echo
set -x

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
(find $MNT -type d | while read dir; do
echo "${dir}/file1.$$"
echo test > "${dir}/file1.$$"
if [ ! -e "${dir}/file1.$$" ]; then
# Beat on it a few more times just for good measure
for i in `seq 1 8`; do
echo test > "${dir}/file1.$$"
done
break
fi
done) || true
umount $MNT

# Repair fs
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
ITER=0
find $MNT -type d | while read dir; do echo "${dir}/file2.$$"; echo test > "${dir}/file2.$$"; if [ $ITER -gt 20 ]; then break; fi; ITER=$((ITER + 1)); done
umount $MNT

# Check fs one last time
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#################################
function corrupt_bbitmap_test {
msg "Deliberately corrupt block bitmap"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 $MKFS_OPTS $MKFS_FEATURES
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRd /etc/ $MNT/
cp -pRd /etc/ $MNT/moo
umount $MNT

set +x
echo + corrupt_block_bitmap
BLOCKSIZE=$(${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Block size:' | sed -e 's/^.* \([0-9]*\)$/\1/g')
FIRST_GROUP=1
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs $DEV | grep 'Block bitmap at' | sed -e 's/^.*Block bitmap at //g' -e 's/ .*$//g' | while read f; do
if [ $FIRST_GROUP -gt 0 ]; then
FIRST_GROUP=0
continue
fi
echo -en " $f"
yes "${FUZZ}" | dd of=$DEV bs=$BLOCKSIZE seek=$f count=1 > /dev/null 2> /dev/null
done
echo
set -x

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
(find $MNT -type d | while read dir; do echo "${dir}/file1.$$"; echo test > "${dir}/file1.$$"; if [ ! -e "${dir}/file1.$$" ]; then break; fi; done) || true
umount $MNT

# Repair fs
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true

# Walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
ITER=0
find $MNT -type d | while read dir; do echo "${dir}/file2.$$"; echo test > "${dir}/file2.$$"; if [ $ITER -gt 20 ]; then break; fi; ITER=$((ITER + 1)); done
umount $MNT

# Check fs one last time
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#######################
function corrupt_extent_head_test {
msg "Corrupt an extent header"
BLK_SZ=2048
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum -b $BLK_SZ
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
SZ=$(df -k $MNT | awk '{print $4}' | tail -1)
fallocate -l $((SZ * 9 / 10))k $MNT/ouch
umount $MNT

set +x
echo + corrupt_extent_tree
echo 'ex /ouch' | ${E2FSPROGS}/debugfs/debugfs $DEV | grep -v -- '-.*-' | grep "^[ 0-9]" | awk '{print $8}' | while read block; do
yes "${FUZZ}" | dd of=$DEV bs=$BLK_SZ seek=$block count=1 > /dev/null 2> /dev/null
done
set -x

# Look for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo >> $MNT/ouch || true
umount $MNT

# Repair
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true

# Look for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo oom >> $MNT/ouch
umount $MNT

# Check fs one last time
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#######################
function corrupt_extent_test {
msg "Destroy an extent entry"
BLK_SZ=2048
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum -b $BLK_SZ
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
SZ=$(df -k $MNT | awk '{print $4}' | tail -1)
fallocate -l $((SZ * 9 / 10))k $MNT/ouch
umount $MNT

echo 'stat /ouch' | ${E2FSPROGS}/debugfs/debugfs $DEV -n | cat -
set +x
echo + corrupt_extent_tree
echo 'ex /ouch' | ${E2FSPROGS}/debugfs/debugfs $DEV | grep -v -- '-.*-' | grep "^[ 0-9]" | awk '{print $8}' | while read block; do
real_block=$(((block * (BLK_SZ / 16)) + 1))
yes "${FUZZ}" | dd of=$DEV bs=16 seek=$real_block count=1 > /dev/null 2> /dev/null
done
set -x
echo 'stat /ouch' | ${E2FSPROGS}/debugfs/debugfs $DEV -n | cat -

# Look for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo >> $MNT/ouch || true
umount $MNT

# Repair
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true

echo 'stat /ouch' | ${E2FSPROGS}/debugfs/debugfs $DEV -n | cat -

# Look for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo oom >> $MNT/ouch
umount $MNT

# Check fs one last time
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#######################
function corrupt_extent_csum_test {
msg "Destroy an extent block csum"
BLK_SZ=2048
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum -b $BLK_SZ
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
SZ=$(df -k $MNT | awk '{print $4}' | tail -1)
fallocate -l $((SZ * 9 / 10))k $MNT/ouch
umount $MNT

echo 'ex /ouch' | ${E2FSPROGS}/debugfs/debugfs $DEV -n | cat -
set +x
echo + corrupt_extent_block_csum
echo 'ex /ouch' | ${E2FSPROGS}/debugfs/debugfs $DEV | grep -v -- '-.*-' | grep "^[ 0-9]" | awk '{print $8}' | while read block; do
real_block=$((((block + 1) * (BLK_SZ / 16)) - 1))
yes "${FUZZ}" | dd of=$DEV bs=16 seek=$real_block count=1 > /dev/null 2> /dev/null
done
set -x
echo 'ex /ouch' | ${E2FSPROGS}/debugfs/debugfs $DEV -n | cat -

# Look for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo >> $MNT/ouch || true
umount $MNT

# Repair
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true

echo 'stat /ouch' | ${E2FSPROGS}/debugfs/debugfs $DEV -n | cat -

# Look for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo oom >> $MNT/ouch
umount $MNT

# Check fs one last time
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#######################
function htree_test {
msg "Simple htree test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum
msg "htree1"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
set +x
echo + htree1
for i in 1; do
for j in 1 2 3 4; do
mkdir -p $MNT/$i/$j
for k in `seq 1 24`; do
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$k\" x 256);")
echo moo > "$MNT/$i/$j/$FNAME"
done
done
for k in `seq 1 32`; do
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$k\" x 256);")
echo moo > "$MNT/$i/$FNAME"
done
done
set -x
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

msg "htree2"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/1 > /dev/null
set +x
echo + htree2
for i in 2; do
for j in 1 2 3 4; do
mkdir -p $MNT/$i/$j
for k in `seq 1 24`; do
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$k\" x 256);")
echo moo > "$MNT/$i/$j/$FNAME"
done
done
for k in `seq 1 32`; do
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$k\" x 256);")
echo moo > "$MNT/$i/$FNAME"
done
done
set -x
umount $MNT

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

msg "htree_del"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
rm -rf $MNT/1 $MNT/2
umount $MNT

msg "multi level htree"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum -b 2048
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
mkdir $MNT/3
echo moo > $MNT/3/base.txt
set +x
echo + mltree
seq 1 5000 | while read f; do
if [ $((f % 71)) -eq 0 ]; then
echo -n "$f..."
fi
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$f\" x 256);")
ln $MNT/3/base.txt $MNT/3/$FNAME
done
echo
set -x
umount $MNT

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

msg "mlhtree delete"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
rm -rf $MNT/3
umount $MNT

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV
}

#########################
function corrupt_htree_test {
msg "corrupt htree"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum -b 2048
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
mkdir $MNT/3
echo moo > $MNT/3/base.txt
set +x
echo + htree
seq 1 10 | while read f; do
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$f\" x 256);")
ln $MNT/3/base.txt $MNT/3/$FNAME
done
set -x
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV || true

# now blast the root block
ROOT_BLK=$(echo 'stat /3' | ${E2FSPROGS}/debugfs/debugfs $DEV | grep EXTENTS -A1 | tail -1 | sed -e 's/^.*://g' -e 's/-.*$//g')
yes "${FUZZ}" | dd of=$DEV bs=8 seek=$(( ( (ROOT_BLK + 1) * 256) - 1 )) count=1 > /dev/null 2> /dev/null
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV || true

# walk looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/3 > /dev/null || true
umount $MNT
# WARNING: The htree flag is now off!

# fix
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

# rescan
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/3 > /dev/null || true
umount $MNT

# check once more
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

# inflate directory some more
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
mkdir -p $MNT/3
echo moo > $MNT/3/base.txt
set +x
echo + mltree_fuzz
seq 11 2000 | while read f; do
if [ $((f % 71)) -eq 0 ]; then
echo -n "$f..."
fi
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$f\" x 256);")
ln $MNT/3/base.txt $MNT/3/$FNAME
done
set -x
umount $MNT

# now corrupt the secondary blocks
echo 'htree /3' | ${E2FSPROGS}/debugfs/debugfs $DEV | grep 'Number of entries' -B1 | grep block | sed -e 's/^.*block //g' | while read blocknr; do
PBLK=$(echo "bmap /3 ${blocknr}" | ${E2FSPROGS}/debugfs/debugfs $DEV -f - | grep '^[0-9]')
PBLK=$(( ($PBLK * 256) + 2))
yes "${FUZZ}" | dd of=$DEV bs=8 seek=${PBLK} count=1 > /dev/null 2> /dev/null
done
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV || true

# walk looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/3 > /dev/null || true
umount $MNT

# fix
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV || true
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

# rescan
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/3 > /dev/null || true
umount $MNT

# check once more
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV
}

#########################
function flat_dir_test {
msg "flat dir test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum,^dir_index -b 2048
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/a
cp -pRl $MNT/a $MNT/b
cp -pRl $MNT/a $MNT/c
cp -pRl $MNT/a $MNT/d
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

msg "cat files"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/b -type f -print0 | xargs -0 cat > /dev/null
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

msg "enlarge direntry"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
set +x
echo + enbiggen dirents
# You need depth-first traversal here
find $MNT/b -depth | while read f; do
mv $f $f.longer
done
set -x
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

msg "rm file"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/b -type f -print0 | xargs -0 rm -rf
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

msg "rm dir"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
rm -rf $MNT/b
umount $MNT

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV
}

#########################
function corrupt_flat_dir_test {
msg "corrupt flat dir test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum,^dir_index -b 2048
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
mkdir $MNT/a/
cp -pRdu /etc/init* $MNT/a/
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

echo 'ex /a' | ${E2FSPROGS}/debugfs/debugfs $DEV | awk '{if (($8 * 1) == $8) {print $8}}' | while read blk; do
yes "${FUZZ}" | dd of=$DEV bs=4 count=1 seek=$(( (blk * 512) + 511 ))
done
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV || true

msg "cat files (brokeN)"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/a -type f -print0 | xargs -0 cat > /dev/null
echo moo | tee $MNT/a/test0 || true
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true

msg "cat files again"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/a -type f -print0 | xargs -0 cat > /dev/null
echo moo | tee $MNT/a/test0
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

msg "blast flat dir test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum,^dir_index -b 2048
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/a
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

echo 'ex /a' | ${E2FSPROGS}/debugfs/debugfs $DEV | awk '{if (($8 * 1) == $8) {print $8}}' | while read blk; do
yes "${FUZZ}" | dd of=$DEV bs=2048 count=1 seek=$blk
done
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV || true

msg "cat files (brokeN)"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/a -type f -print0 | xargs -0 cat > /dev/null
echo moo | tee $MNT/a/test0 || true
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

msg "cat files again"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT/a -type f -print0 | xargs -0 cat > /dev/null
echo moo | tee $MNT/a/test1
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV
}

####################
function ignore_fsck_d_test {
msg "ignore_fsck_d_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/
umount $MNT

$VALGRIND ${E2FSPROGS}/misc/tune2fs $DEV -O metadata_csum
echo "Find all possible damage."
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

echo "Actually repair damage. 0"
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true
echo "Test run 0."
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -print0 | xargs -0 cat > /dev/null
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

# do it again, but wiht -D
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/
umount $MNT

$VALGRIND ${E2FSPROGS}/misc/tune2fs $DEV -O metadata_csum
echo "Actually repair damage. 1"
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

echo "Test run. 1"
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV

mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -print0 | xargs -0 cat > /dev/null
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fn $DEV
}

############################
function xattr_test {
#############################
# create xattrs
msg "xattr_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 $MKFS_OPTS $MKFS_FEATURES
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

# create a bunch of xattrs
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo attrs | tee $MNT/attr1 $MNT/attr2 $MNT/noattr
set +x
echo + set_xattrs
for i in `seq -w 1 100`; do
attr -s $i -V $i $MNT/attr1 > /dev/null
done
attr -s XXX -V XXX $MNT/attr2 > /dev/null
set -x
umount $MNT

# reload and reread
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
attr -l $MNT/attr1
attr -l $MNT/attr2
attr -l $MNT/noattr
umount $MNT

# Check fs one last time
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#############################
function corrupt_xattr_test {
msg "corrupt_xattr_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum -b 4096

mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo attrs | tee $MNT/attr1 $MNT/attr2 $MNT/noattr
set +x
echo + set_xattrs
for i in `seq -w 1 100`; do
attr -s $i -V $i $MNT/attr1 > /dev/null
done
attr -s XXX -V XXX $MNT/attr2 > /dev/null
set -x
umount $MNT

# gibberise the xattr block
XATTR_BLOCK=$(echo 'stat /attr1' | ${E2FSPROGS}/debugfs/debugfs $DEV | grep 'File ACL' | awk '{print $3}')
if [ -z "${XATTR_BLOCK}" -o "0${XATTR_BLOCK}" -lt 1 ]; then
echo "Uh... no ACL block?"
exit 50
fi
yes "${FUZZ}" | dd of=$DEV bs=4096 seek=$XATTR_BLOCK count=1 > /dev/null 2> /dev/null

# reload and reread
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV || true
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
attr -l $MNT/attr1 || true
attr -l $MNT/attr1 || true
attr -l $MNT/attr2
attr -l $MNT/noattr
umount $MNT

# fix
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true

# reread, but this time fixed
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
attr -l $MNT/attr1
attr -l $MNT/attr2
attr -l $MNT/noattr
umount $MNT

# once more
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

####################################
function simple_sb_test {
msg "simple_sb_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 $MKFS_OPTS $MKFS_FEATURES
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep Checksum
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# write stuff
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo > $MNT/moo
umount $MNT
${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep Checksum
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# change label
$VALGRIND ${E2FSPROGS}/misc/tune2fs -L moocow $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep Checksum
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# quick check kernel
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo > $MNT/cow
umount $MNT
${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep Checksum
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# change label again
$VALGRIND ${E2FSPROGS}/misc/tune2fs -L cowmoo $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep Checksum
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# quick check kernel
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo > $MNT/cow
umount $MNT
${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep Checksum
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# change uuid
$VALGRIND ${E2FSPROGS}/misc/tune2fs -U random $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo > $MNT/post-uuid
umount $MNT

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

##########################
function prep_speed_test {
VER=3.2.8
if [ ! -r /tmp/linux-${VER}.tar.bz2 ]; then
cp /home/djwong/linux-${VER}.tar.bz2 /tmp/
#scp elm3c44:/home/djwong/kern*/linux-${VER}.tar.bz2 /tmp/
fi
if [ ! -r /tmp/tarfiles ]; then
tar tvf /tmp/linux-${VER}.tar.bz2 > /tmp/tarfiles
fi
if [ ! -r /tmp/dirs -o ! -r /tmp/files -o ! -r /tmp/topdirs ]; then
rm -rf /tmp/dirs /tmp/files /tmp/topdirs
for i in a1 a2 a3 a4 a5 a6 a7 a8 a9 aA aB aC aD aE aF; do
cat /tmp/tarfiles | grep ^d | awk "{printf(\"$i/%s\n\", \$6);}" >> /tmp/dirs
cat /tmp/tarfiles | grep -v ^d | awk "{printf(\"%s $i/%s\n\", \$3, \$6);}" >> /tmp/files
echo "$i" >> /tmp/topdirs
done
fi
if [ ! -x /tmp/fab ]; then
cat > /tmp/fab.c << ENDL
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
FILE *fp;
int fd;
size_t size;
char *space;
char buf[1024];

if (argc < 2) {
printf("Usage: %s file_containing_sz_name_pairs\n", argv[0]);
return 4;
}

fp = fopen(argv[1], "r");
if (!fp) {
perror(argv[1]);
return 5;
}

while (fgets(buf, 1024, fp)) {
buf[strlen(buf) - 1] = 0;
space = strchr(buf, ' ');
if (!space) {
fprintf(stderr, "space not found at line \"%s\"!\n", buf);
break;
}
*space = 0;
space++;
fd = open(space, O_WRONLY | O_CREAT, 0644);
if (fd < 0) {
perror(space);
break;
}
size = strtoul(buf, NULL, 0);
posix_fallocate(fd, 0, size);
close(fd);
}

fclose(fp);
return 0;
}
ENDL
gcc -o /tmp/fab /tmp/fab.c
fi
}

function tune2fs_speed_test {
msg "tune2fs_speed_test"
prep_speed_test
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cd $MNT
set +x
echo + mkdirs
mkdir -p $(cat /tmp/dirs)
set -x
/tmp/fab /tmp/files
cd -
umount $MNT

/usr/bin/time $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
/usr/bin/time $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y -D $DEV || true
}

function all_speed_test {
msg "all_speed_test"
prep_speed_test

msg "All checksums including journal and data=journal"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,metadata_csum,mmp
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum,data=journal,nodelalloc
cd $MNT
/usr/bin/time bash -c "mkdir -p \$(cat /tmp/dirs); /tmp/fab /tmp/files; sync"
/usr/bin/time bash -c "rm -rf \$(cat /tmp/topdirs); sync"
cd -
umount $MNT
}

function most_speed_test {
msg "most_speed_test"
prep_speed_test

msg "All checksums including journal and data=ordered"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,metadata_csum,mmp
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cd $MNT
/usr/bin/time bash -c "mkdir -p \$(cat /tmp/dirs); /tmp/fab /tmp/files; sync"
/usr/bin/time bash -c "rm -rf \$(cat /tmp/topdirs); sync"
cd -
umount $MNT
}

function none_speed_test {
msg "none_speed_test"
prep_speed_test

msg "No checksums at all"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
cd $MNT
/usr/bin/time bash -c "mkdir -p \$(cat /tmp/dirs); /tmp/fab /tmp/files; sync"
/usr/bin/time bash -c "rm -rf \$(cat /tmp/topdirs); sync"
cd -
umount $MNT
}

######################
function ext_speed_test {
msg "ext_speed_test"
prep_speed_test
msg "All ext4 checksums, no journal checksum"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
cd $MNT
/usr/bin/time bash -c "mkdir -p \$(cat /tmp/dirs); /tmp/fab /tmp/files; sync"
/usr/bin/time bash -c "rm -rf \$(cat /tmp/topdirs); sync"
cd -
umount $MNT

msg "All ext4 checksums except use old bg method, no journal checksum"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,metadata_csum
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
cd $MNT
/usr/bin/time bash -c "mkdir -p \$(cat /tmp/dirs); /tmp/fab /tmp/files; sync"
/usr/bin/time bash -c "rm -rf \$(cat /tmp/topdirs); sync"
cd -
umount $MNT
}

########################
function dir_rewrite_test {
msg "dir_rewrite_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/a
set +x
echo + htree1
for i in 1; do
for j in 1 2 3 4; do
mkdir -p $MNT/$i/$j
for k in `seq 1 24`; do
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$k\" x 256);")
echo moo > "$MNT/$i/$j/$FNAME"
done
done
for k in `seq 1 32`; do
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$k\" x 256);")
echo moo > "$MNT/$i/$FNAME"
done
done
mkdir -p $MNT/full
for k in `seq 1 32`; do
FNAME=$(perl -e "printf(\"%.248s\\n\", \"_$k\" x 256);")
echo moo > "$MNT/full/$FNAME"
done
set -x
umount $MNT

# enable checksums
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# run it by the kernel
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -print0 | xargs -0 cat > /dev/null
umount $MNT

# one last check
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

##################
function uuid_change_test {
msg "uuid_change_test"
UUID1=deadbeef-cafe-babe-dead-beefcafebabe
UUID2=d15ea5ed-dead-beef-face-feeddefec8ed

# Try to change UUID w/o checksums
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -U $UUID1
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/a
umount $MNT
UUID=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Filesystem UUID' | sed -e 's/^.*:[ ]*//g')
if [ "${UUID}" != "${UUID1}" ]; then
echo "Bad UUID ${UUID}"
exit 1
fi
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

msg "offline change nocsum"
$VALGRIND ${E2FSPROGS}/misc/tune2fs -U $UUID2 $DEV
UUID=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Filesystem UUID' | sed -e 's/^.*:[ ]*//g')
if [ "${UUID}" != "${UUID2}" -o "${UUID}" = "${UUID1}" ]; then
echo "UUID change fail?"
exit 2
fi
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -print0 | xargs -0 cat > /dev/null
umount $MNT

msg "online change nocsum"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo > $MNT/badfile
$VALGRIND ${E2FSPROGS}/misc/tune2fs -U $UUID1 $DEV
sync
find $MNT -type f -print0 | xargs -0 cat > /dev/null
umount $MNT
UUID=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Filesystem UUID' | sed -e 's/^.*:[ ]*//g')
if [ "${UUID}" != "${UUID1}" -o "${UUID}" = "${UUID2}" ]; then
echo "UUID should have changed"
exit 3
fi
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# Try to change UUID with chekcsums
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O mmp,metadata_csum -U $UUID1
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/a
umount $MNT
UUID=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Filesystem UUID' | sed -e 's/^.*:[ ]*//g')
if [ "${UUID}" != "${UUID1}" ]; then
echo "Bad UUID ${UUID}"
exit 1
fi
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

msg "offline change"
$VALGRIND ${E2FSPROGS}/misc/tune2fs -U $UUID2 $DEV
UUID=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Filesystem UUID' | sed -e 's/^.*:[ ]*//g')
if [ "${UUID}" != "${UUID2}" -o "${UUID}" = "${UUID1}" ]; then
echo "UUID change fail?"
exit 2
fi
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -print0 | xargs -0 cat > /dev/null
umount $MNT

msg "online nochange"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo > $MNT/badfile
$VALGRIND ${E2FSPROGS}/misc/tune2fs -U $UUID1 $DEV || true
sync
find $MNT -type f -print0 | xargs -0 cat > /dev/null
umount $MNT
UUID=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Filesystem UUID' | sed -e 's/^.*:[ ]*//g')
if [ "${UUID}" != "${UUID2}" -o "${UUID}" = "${UUID1}" ]; then
echo "UUID should not have changed"
exit 3
fi
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

}

################################
function bg_meta_use_csum_test {
msg "bg_meta_use_csum_test"
# write junk into last error field
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,^uninit_bg,^metadata_csum -b 4096
mount $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/etc
umount $MNT

for bg_checksum in 0 1; do
for metadata_csum in 0 1; do
opts=""
if [ $metadata_csum -eq 1 ]; then
opts="metadata_csum,${opts}"
else
opts="^metadata_csum,${opts}"
fi
if [ $bg_checksum -eq 1 ]; then
opts="uninit_bg,${opts}"
else
opts="^uninit_bg,${opts}"
fi
$VALGRIND ${E2FSPROGS}/misc/tune2fs -O $opts $DEV
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'Filesystem features:'
mount $DEV $MNT -t ext4
find $MNT -type f -print0 | xargs -0 cat > /dev/null
date > "${MNT}/${bg_checksum}_${metadata_csum}"
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
done
done
}

############################
function mmp_test {
msg "mmp_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,metadata_csum,mmp -E mmp_update_interval=5
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mount $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/a
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# Can't fsck while mounted
mount $DEV $MNT -t ext4 -o journal_checksum
yes | script -f /tmp/mmp.$$ -c "$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV; echo \"RETURN: \$?\""
RET=$(grep 'RETURN:' /tmp/mmp.$$ | awk '{print $2}')
if [ $RET -eq 0 ]; then
echo "Should not be able to fsck while mounted"
exit 50
fi
set -e
umount $MNT
}

#####################
function corrupt_mmp_test {
msg "corrupt_mmp_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,metadata_csum,mmp -E mmp_update_interval=5 -b 4096
mount $DEV $MNT -t ext4 -o journal_checksum
echo moo > $MNT/moofile
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

MMP_BLOCK=$(${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep 'MMP block number:' | awk '{print $4}')
MMP_SECTOR=$(((MMP_BLOCK * 8) + 1))
yes "${FUZZ}" | dd of=$DEV bs=512 seek=${MMP_SECTOR} count=1 > /dev/null 2> /dev/null
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV || true
set +e
mount $DEV $MNT -t ext4 -o journal_checksum
if [ $? -eq 0 ]; then
echo "Should not be able to mount with corrupt MMP"
exit 50
fi
set -e

$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true
mount $DEV $MNT -t ext4 -o journal_checksum
echo $MNT/moofile > /dev/null
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

##############################
function journal_sb_test {
msg "journal_sb_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,metadata_csum
mount $DEV $MNT -t ext4 -o journal_checksum,data=journal,nodelalloc
cp -pRdu /etc $MNT/etc
sync
rm -rf $MNT/etc/init*
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep ^Journal

# Make the kernel run through again
mount $DEV $MNT -t ext4 -o journal_checksum,data=journal,nodelalloc
cp -pRdu /etc $MNT/blob
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep ^Journal

# What happens if we don't specify any options?
mount $DEV $MNT -t ext4
#set +x
#mkdir $MNT/fragged/
#for i in `seq 1 1000`; do
# echo moo > $MNT/fragged/a$i
# sync
# dd if=/dev/zero bs=4k count=1 >> $MNT/fragged/fragfile 2> /dev/null > /dev/null
# sync
#done
#set -x
#truncate -s -2000k $MNT/fragged/fragfile
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep ^Journal
}

##############################
function corrupt_journal_sb_test {
msg "corrupt_journal_sb_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,metadata_csum -b 4096
mount $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/etc
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep ^Journal

# Fuzz part of the superblock
JSB_BLOCK=$(echo 'ex <8>' | ${E2FSPROGS}/debugfs/debugfs -n $DEV | grep '0 - 3276' | awk '{print $8}')
JSB_SECTOR=$(((JSB_BLOCK * 8) + 1))
yes "${FUZZ}" | dd of=$DEV bs=512 seek=${JSB_SECTOR} count=1 > /dev/null 2> /dev/null
dd if=$DEV bs=4096 skip=${JSB_BLOCK} count=1 | od -tx1 -Ad -c
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep ^Journal

# Make the kernel run through again
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV || true
set +e
mount $DEV $MNT -t ext4 -o journal_checksum
if [ $? -eq 0 ]; then
echo "Should not be able to mount with corrupt journal."
exit 50
fi
set -e
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y $DEV || true
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep ^Journal

# Mount, copy more files
mount $DEV $MNT -t ext4 -o journal_checksum
cp -pRdu /etc $MNT/next
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep ^Journal
}

# Leave this at the end
################################
function corrupt_sb_test {
msg "corrupt_sb_test"
# write junk into last error field
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum -b 4096 -i 524288
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep UUID
yes "${FUZZ}" | dd of=$DEV bs=32 seek=47 count=1 > /dev/null 2> /dev/null
# fsck -n will modify the fs due to backup sb being used ! DO NOT RUN THIS: $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum && echo should not get here && exit 50
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y -D $DEV || true
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo > $MNT/file
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

# totally destroy sb
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O 64bit,mmp,metadata_csum -b 4096 -i 524288
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep UUID
yes "${FUZZ}" | dd of=$DEV bs=4096 seek=0 count=1 > /dev/null 2> /dev/null
# fsck -n will modify the fs due to backup sb being used ! DO NOT RUN THIS: $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum && echo should not get here && exit 50
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y -D $DEV || true
echo "beat on it a second time"
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -y -D $DEV || true
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
echo moo > $MNT/file
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
}

#####################################
function mkfs_flag_collision_test_helper {
if [ ! -z "$1" ]; then
FEATURES="-O $1,^has_journal"
else
FEATURES="-O ^has_journal"
fi
FEATURE_STRING="$2"
if [ ! -z "$3" ]; then
ERROR_MSG="$3"
else
ERROR_MSG="$1"
fi

$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 $FEATURES
set +e
MCSUM=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep -c metadata_csum)
GCSUM=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep -c uninit_bg)
set -e
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
if [ "${MCSUM},${GCSUM}" != "${FEATURE_STRING}" ]; then
msg "FAIL ${ERROR_MSG}"
return 1
fi
msg "PASS ${ERROR_MSG}"
return 0
}

function mkfs_flag_collision_test {
msg "mkfs_flag_collision_test"
mkfs_flag_collision_test_helper "" "0,1" "no flags"
mkfs_flag_collision_test_helper "metadata_csum" "1,0"
# The following are actually tested in the tune2fs flag collision test
#mkfs_flag_collision_test_helper "metadata_csum,^uninit_bg" "1,0"
#mkfs_flag_collision_test_helper "^metadata_csum,uninit_bg" "0,1"
#mkfs_flag_collision_test_helper "metadata_csum,uninit_bg" "1,0"
#mkfs_flag_collision_test_helper "^metadata_csum,^uninit_bg" "0,0"
}

#####################################
function tune2fs_flag_collision_test_helper {
if [ ! -z "$1" ]; then
FEATURES="-O $1"
fi
FEATURE_STRING="$2"
ERROR_MSG="$1"

$VALGRIND ${E2FSPROGS}/misc/tune2fs $DEV $FEATURES
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true
if [ -z "${FEATURE_STRING}" ]; then
return 0
fi
set +e
MCSUM=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep -c metadata_csum)
GCSUM=$($VALGRIND ${E2FSPROGS}/misc/dumpe2fs -h $DEV | grep -c uninit_bg)
set -e
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
if [ "${MCSUM},${GCSUM}" != "${FEATURE_STRING}" ]; then
msg "FAIL ${ERROR_MSG}"
return 1
fi
msg "PASS ${ERROR_MSG}"
return 0
}

function tune2fs_flag_collision_test {
msg "tune2fs_flag_collision_test"
declare -A COMBOS
COMBOS[0]="^metadata_csum,^uninit_bg"
COMBOS[1]="metadata_csum,^uninit_bg"
COMBOS[2]="^metadata_csum,uninit_bg"
COMBOS[3]="metadata_csum,uninit_bg"

declare -A RESULTS
RESULTS[0]="0,0"
RESULTS[1]="1,0"
RESULTS[2]="0,1"
RESULTS[3]="1,0"

for i in ${!COMBOS[@]}; do
for j in ${!COMBOS[@]}; do
mkfs_flag_collision_test_helper "${COMBOS[$i]}" "${RESULTS[$i]}"
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRd /etc/ $MNT/etc_a
umount $MNT
tune2fs_flag_collision_test_helper "${COMBOS[$j]}" "${RESULTS[$j]}"
done
done
}

#####################################
function remove_checksum_test {
msg "remove_checksum_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O ^has_journal,metadata_csum

# Dump in some files
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRd /etc/ $MNT/etc_a
umount $MNT

tune2fs_flag_collision_test_helper "^metadata_csum,uninit_bg" "0,1" "remove only metadata_csum"
tune2fs_flag_collision_test_helper "^uninit_bg" "0,0" "remove uninit_bg"

msg "fast disable"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O ^has_journal,metadata_csum

# Dump in some files
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRd /etc/ $MNT/etc_a
umount $MNT

tune2fs_flag_collision_test_helper "^metadata_csum,^uninit_bg" "0,0" "remove metadata_csum and uninit_bg"
}

#####################################
function shrink_uninit_test {
msg "shrink_uninit_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O ^has_journal,metadata_csum,64bit -b 4096 -N 192 393216
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
mkdir -p $MNT/dirX/
j=0
touch $MNT/bigfile
while true; do
touch $MNT/dirX/file_${i}_${j} || break
j=$((j + 1))
done
dd if=/dev/zero of=$MNT/bigfile conv=notrunc oflag=append || true
umount $MNT
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
rm -rf $MNT/dirX $MNT/bigfile
umount $MNT

for i in `seq 1 16`; do
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true
$VALGRIND ${E2FSPROGS}/resize/resize2fs $DEV $((393216 - ($i * 16384)))
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs $DEV
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
dd if=/dev/zero of=$MNT/bigfile conv=notrunc oflag=append || true
sync
rm -rf $MNT/bigfile
umount $MNT
done
}

#####################################
function resize_uninit_test {
msg "resize_test: uninit_bg"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O ^has_journal,metadata_csum,64bit -b 4096 -N 64 131072

for i in `seq 1 16`; do
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true
$VALGRIND ${E2FSPROGS}/resize/resize2fs $DEV $((131072 + ($i * 16384)))
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs $DEV
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
mkdir -p $MNT/dirX/
j=0
touch $MNT/bigfile
while true; do
touch $MNT/dirX/file_${i}_${j} || break
j=$((j + 1))
done
dd if=/dev/zero of=$MNT/bigfile conv=notrunc oflag=append || true
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
done
}

######################################
function resize_init_test {
msg "resize_init_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O ^has_journal,^uninit_bg,64bit -b 4096 -N 64 131072
$VALGRIND ${E2FSPROGS}/misc/tune2fs $DEV -O metadata_csum
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

for i in `seq 1 16`; do
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true
$VALGRIND ${E2FSPROGS}/resize/resize2fs $DEV $((131072 + ($i * 16384)))
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs $DEV
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
mkdir -p $MNT/dirY/
j=0
touch $MNT/bigfile
while true; do
touch $MNT/dirY/file_${i}_${j} || break
j=$((j+1))
done
dd if=/dev/zero of=$MNT/bigfile conv=notrunc oflag=append || true
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
done
}

#####################################
function online_resize_uninit_test {
msg "online_resize_test: uninit_bg"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O ^has_journal,metadata_csum,64bit -b 4096 -N 64 131072

for i in `seq 1 16`; do
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs $DEV
$VALGRIND ${E2FSPROGS}/resize/resize2fs $DEV $((131072 + ($i * 16384)))
$VALGRIND ${E2FSPROGS}/misc/dumpe2fs $DEV
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
mkdir -p $MNT/dirX/
j=0
touch $MNT/bigfile
while true; do
touch $MNT/dirX/file_${i}_${j} || break
j=$((j + 1))
done
dd if=/dev/zero of=$MNT/bigfile conv=notrunc oflag=append || true
umount $MNT
# XXX: this is broken, even before metadata_csum came about
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
done
}

######################################
function online_resize_init_test {
msg "online_resize_init_test"
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 -O ^has_journal,^uninit_bg,64bit -b 4096 -N 64 131072
$VALGRIND ${E2FSPROGS}/misc/tune2fs $DEV -O metadata_csum
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

for i in `seq 1 16`; do
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fy $DEV || true
mount ${MOUNT_OPTS} $DEV $MNT -t ext4
$VALGRIND ${E2FSPROGS}/resize/resize2fs $DEV $((131072 + ($i * 16384)))
mkdir -p $MNT/dirY/
j=0
touch $MNT/bigfile
while true; do
touch $MNT/dirY/file_${i}_${j} || break
j=$((j+1))
done
dd if=/dev/zero of=$MNT/bigfile conv=notrunc oflag=append || true
umount $MNT
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV
done
}

#####################################
function simple_prep_fs {
msg "Create fs with files, dirs, EAs, htree dirs, etc."
$VALGRIND ${E2FSPROGS}/misc/mke2fs -F $DEV -T ext4 $MKFS_OPTS $MKFS_FEATURES
test -z "$NO_CSUM" && $VALGRIND ${E2FSPROGS}/misc/tune2fs -O metadata_csum $DEV
${E2FSPROGS}/misc/dumpe2fs -h $DEV 2> /dev/null | egrep -q "^Filesystem state:[ ]*clean$" || $VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -fDy $DEV || true

# Dump in some files
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
cp -pRd /etc/ $MNT/etc_a
mkdir -p $MNT/fragged
dd if=/dev/zero bs=4k count=1 of=$MNT/fragged/fragfile
sync
set +x
echo + frag
for i in `seq 1 8`; do
echo moo > $MNT/fragged/a$i
sync
dd if=/dev/zero bs=4k count=1 >> $MNT/fragged/fragfile
sync
done
set -x
echo moo > $MNT/ea_file
set +x
echo + set_ea
for i in `seq -w 1 100`; do
attr -s $i -V $i $MNT/ea_file
done

# add some random files
cp -pRd /etc/ $MNT/etc_b

# create htree
mkdir -p $MNT/bigdir2
set +x
echo + htree2
for i in `seq 1 256`; do
echo moo > "$MNT/bigdir2/$(echo "$(date)_$i" | md5sum)"
done
set -x

# Fragment a file
set +x
echo + frag
for i in `seq 9 18`; do
echo moo > $MNT/fragged/a$i
sync
dd if=/dev/zero bs=4k count=1 >> $MNT/fragged/fragfile
sync
done
set -x

# Rewrite extent tree
set +x
echo + frag
for i in `seq 19 28`; do
echo moo > $MNT/fragged/a$i
sync
dd if=/dev/zero bs=4k count=1 >> $MNT/fragged/fragfile
sync
done
set -x

# Truncate
truncate -s -64k $MNT/fragged/fragfile

# More rewrites
set +x
echo + frag
for i in `seq 29 38`; do
echo moo > $MNT/fragged/a$i
sync
dd if=/dev/zero bs=4k count=1 >> $MNT/fragged/fragfile
sync
done
set -x

# multi-level htree
mkdir $MNT/3
echo moo > $MNT/3/base.txt
set +x
echo + mltree
seq 1 5000 | while read f; do
if [ $((f % 71)) -eq 0 ]; then
echo -n "$f..."
fi
FNAME=$(perl -e "printf(\"%.250s\\n\", \"_$f\" x 256);")
ln $MNT/3/base.txt $MNT/3/$FNAME
done
echo
set -x
umount $MNT

# Re-walk fs looking for errors
mount ${MOUNT_OPTS} $DEV $MNT -t ext4 -o journal_checksum
find $MNT -type f -print0 | xargs -0 cat > /dev/null
attr -l $MNT/ea_file > /dev/null
umount $MNT/

# Check fs again
$VALGRIND ${E2FSPROGS}/e2fsck/e2fsck -f -n $DEV

echo 'Filesystem created; please mount with -o journal_checksum'
}

#####################
for verb in $(echo "${VERBS}" | tr ':,' ' '); do
$verb
done

####################################
msg "Finished successfully, ENJOY."


2012-03-13 04:03:55

by Andreas Dilger

[permalink] [raw]
Subject: Re: [PATCH v3 00/54] e2fsprogs: Add metadata checksumming

On 2012-03-12, at 6:41 PM, Darrick J. Wong wrote:
> Here's a copy of my testing script....

Darrick,
it would be great if you could translate this test script into test cases for the
e2fsprogs regression tests in the tests/ subdirectory so that they will continue
to be run in the future.

Cheers, Andreas