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.
This patchset has been tested on 3.2.0-rc5 on x64, i386, ppc64, and ppc32. The
patches seems to work fine on all four platforms. The patchset is based atop
the 1.42 release.
--D
[1] https://ext4.wiki.kernel.org/index.php/Ext4_Metadata_Checksums
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, 83 insertions(+), 61 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 00e46d0..62e49c6 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -680,7 +680,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 82e1ba0..cf60458 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..7b0db60 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,10 @@ 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 = inode;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -547,13 +570,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 +598,13 @@ 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;
+ if (bufsize < length) {
+ retval = ext2fs_get_mem(length, &iptr);
+ if (retval)
+ return retval;
+ }
- ptr = (char *) inode;
+ ptr = (char *) iptr;
while (length) {
clen = length;
if ((offset + length) > fs->blocksize)
@@ -604,18 +626,24 @@ 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;
+ memcpy(fs->icache->cache[fs->icache->cache_last].inode, iptr, length);
+
+ if (iptr != inode) {
+ memcpy(inode, iptr, bufsize);
+ ext2fs_free_mem(&iptr);
+ }
return 0;
}
@@ -633,9 +661,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 +675,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 +726,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;
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 | 36 ++++++++++++++++++++++++++++++++++--
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/ext3_extents.h | 11 +++++++++++
5 files changed, 50 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 0f8cde8..ce2fd66 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -234,6 +234,13 @@ struct ext2_dx_countlimit {
__u16 count;
};
+/*
+ * This goes at the end of each htree block.
+ */
+struct ext2_dx_tail {
+ __u32 reserved;
+ __u32 checksum; /* crc32c(uuid+inum+dxblock) */
+};
/*
* Macro-instructions used to manage group descriptors
@@ -462,6 +469,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__)
@@ -533,6 +541,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
*/
@@ -618,7 +629,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 */
@@ -719,6 +730,7 @@ struct ext2_super_block {
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400
#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000
+#define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM 0x2000
#define EXT2_FEATURE_COMPAT_SUPP 0
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
@@ -778,6 +790,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 reserved_zero1; /* Pretend to be unused */
+ __u16 rec_len; /* 12 */
+ __u16 reserved_name_len; /* 0xDE00, fake namelen/filetype */
+ __u32 checksum; /* crc32c(uuid+inode+dirent) */
+};
+
+/*
* Ext2 directory file types. Only the low 3 bits are used. The
* other bits are reserved for now.
*/
@@ -793,6 +816,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
@@ -833,7 +864,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 227ee58..16c4567 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
*/
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 70c8326..310dae2 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 b7ff00d..419c76c 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -2300,9 +2300,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)
@@ -2329,6 +2329,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;
This patch adds the ability for the libext2fs functions to read and write the
inode checksum. It also fixes a few fields that were omitted from the byte
swapping routines.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 83 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2_fs.h | 4 ++
lib/ext2fs/ext2fs.h | 4 ++
lib/ext2fs/inode.c | 49 ++++++++++++++++++++++-----
5 files changed, 134 insertions(+), 9 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 596923e..1f7c641 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,89 @@
#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 ncrc;
+ struct ext2_inode_large *desc = inode;
+ size_t size = fs->super->s_inode_size;
+ __u16 old_lo;
+ __u16 old_hi = 0;
+ errcode_t retval = 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);
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)desc, size);
+ *crc = ncrc;
+
+ inode->i_checksum_lo = old_lo;
+ if (has_hi)
+ inode->i_checksum_hi = old_hi;
+ return retval;
+}
+
+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_LOCATION);
+
+ 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_LOCATION);
+
+ 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 ce2fd66..4bcf1de 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -459,6 +459,10 @@ struct ext2_inode_large {
__u32 i_version_hi; /* high 32 bits for 64-bit version */
};
+#define EXT4_INODE_CSUM_HI_EXTRA_LOCATION \
+ (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 16c4567..822cf78 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -936,6 +936,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 7b0db60..f465e27 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);
}
@@ -547,11 +559,11 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
blk64_t block_nr;
unsigned long group, block, offset;
char *ptr;
- errcode_t retval;
+ errcode_t retval = 0;
int clen, i, inodes_per_block;
io_channel io;
int length = EXT2_INODE_SIZE(fs->super);
- struct ext2_inode *iptr = inode;
+ struct ext2_inode_large *iptr = (struct ext2_inode_large *)inode;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -628,24 +640,34 @@ 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))
+ retval = EXT2_ET_INODE_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
(struct ext2_inode_large *) iptr,
0, length);
#endif
+ /* Don't fill the cache with corrupt inodes */
+ if (retval == EXT2_ET_INODE_CSUM_INVALID)
+ goto err;
+
/* 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;
memcpy(fs->icache->cache[fs->icache->cache_last].inode, iptr, length);
- if (iptr != inode) {
+err:
+ if (iptr != (struct ext2_inode_large *)inode) {
memcpy(inode, iptr, bufsize);
ext2fs_free_mem(&iptr);
}
- return 0;
+ return retval;
}
errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino,
@@ -682,12 +704,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) {
@@ -714,6 +741,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);
Write out checksummed inodes even 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 0ef2531..98af8b0 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -305,6 +305,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;
@@ -350,6 +371,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)
@@ -869,7 +896,7 @@ static __u32 ok_features[3] = {
#ifdef CONFIG_QUOTA
EXT4_FEATURE_RO_COMPAT_QUOTA|
#endif
- 0
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
};
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 | 9 ++++++++-
e2fsck/problem.c | 7 ++++++-
e2fsck/problem.h | 3 +++
3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 62e49c6..15320c6 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -729,7 +729,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;
@@ -739,6 +740,12 @@ 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 &&
+ fix_problem(ctx, PR_1_INODE_CSUM_INVALID, &pctx))
+ goto clear_inode;
+
if (inode->i_links_count) {
pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
ino, inode->i_links_count);
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index f042b89..89dc72b 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -934,7 +934,12 @@ 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_CLEAR, PR_PREEN_OK },
+ 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_FIX, PR_PREEN_OK },
/* Invalid bad inode */
{ PR_1_INVALID_BAD_INODE,
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 9db29d8..f9f8cd7 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -550,6 +550,9 @@ 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
+
/*
* Pass 1b errors
*/
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 | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 104 insertions(+), 2 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 74a0489..260b040 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,59 @@ 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;
+ 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 +602,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 +2208,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) {
/*
@@ -2159,7 +2256,12 @@ retry_open:
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 "
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 419c76c..1d2b2f3 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);
Display the inode bitmap checksum for each block group.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/dumpe2fs.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 5b114e9..98bee2b 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -226,6 +226,11 @@ 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_desc_size >= EXT2_MIN_DESC_SIZE_64BIT &&
+ 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) +
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 a60e84a..fbdde5f 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 89dc72b..3712c43 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 f9f8cd7..30c6707 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1016,6 +1016,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
*/
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 260b040..b53e5da 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -426,6 +426,9 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
{
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
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;
}
Provide a field in the block group descriptor to store block 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/csum.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_fs.h | 3 +++
lib/ext2fs/ext2fs.h | 6 ++++++
lib/ext2fs/rw_bitmaps.c | 16 ++++++++++++++++
5 files changed, 84 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index 190242c..5770238 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)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 47c1111..1bad35c 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -74,6 +74,50 @@ 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(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ calculated = ext2fs_crc32c_le(calculated, (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(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ crc = ext2fs_crc32c_le(crc, (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 70b9fa2..734e22d 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -194,6 +194,9 @@ struct ext4_group_desc
#define EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION \
(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 3a07f4a..05f3e4c 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -798,6 +798,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);
@@ -825,6 +826,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);
@@ -938,6 +940,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;
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 | 44 ++++++++++++++++++++++++++++++++++++++++++++
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, 105 insertions(+), 12 deletions(-)
diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index 33da7d6..190242c 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_LOCATION)
+ 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 1f7c641..47c1111 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,50 @@
#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(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ calculated = ext2fs_crc32c_le(calculated, (unsigned char *)bitmap,
+ size);
+ if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION)
+ 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(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ crc = ext2fs_crc32c_le(crc, (unsigned char *)bitmap, size);
+ gdp->bg_inode_bitmap_csum_lo = crc & 0xFFFF;
+ if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION)
+ 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 4bcf1de..70b9fa2 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -191,6 +191,10 @@ struct ext4_group_desc
__u32 bg_reserved;
};
+#define EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION \
+ (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 822cf78..3a07f4a 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -797,6 +797,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);
@@ -827,6 +828,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);
@@ -936,6 +938,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;
Display the block bitmap checksum when displaying block groups.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/dumpe2fs.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 98bee2b..33c0933 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -222,6 +222,11 @@ 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_desc_size >= EXT2_MIN_DESC_SIZE_64BIT &&
+ 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,
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 fbdde5f..0e86efa 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 3712c43..e74ad79 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 30c6707..1d63598 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1019,6 +1019,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
*/
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 f00734e..037f270 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -246,7 +246,9 @@ void e2fsck_read_bitmaps(e2fsck_t ctx)
}
old_op = ehandler_operation(_("reading inode and block bitmaps"));
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_read_bitmaps(fs);
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
ehandler_operation(old_op);
if (retval) {
com_err(ctx->program_name, retval,
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 b53e5da..12a22ed 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -428,6 +428,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;
}
Verify and calculate extent tree block checksums when processing filesystems.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 75 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2fs.h | 6 ++++
lib/ext2fs/extent.c | 21 +++++++++++++
4 files changed, 105 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 1bad35c..28d7ef1 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,81 @@
#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 ncrc;
+ errcode_t retval = 0;
+
+ size = EXT3_EXTENT_TAIL_OFFSET(eh) + offsetof(struct ext3_extent_tail,
+ et_checksum);
+
+ inum = ext2fs_cpu_to_le32(inum);
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)eh, size);
+ *crc = ncrc;
+
+ return retval;
+}
+
+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 05f3e4c..1394d02 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -940,6 +940,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);
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 | 11 +++++++++--
e2fsck/problem.c | 6 ++++++
e2fsck/problem.h | 3 +++
3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 15320c6..7cfc739 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1761,7 +1761,9 @@ 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) {
is_leaf = extent.e_flags & EXT2_EXTENT_FLAGS_LEAF;
is_dir = LINUX_S_ISDIR(pctx->inode->i_mode);
@@ -1776,6 +1778,8 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
(extent.e_pblk + extent.e_len) >
ext2fs_blocks_count(ctx->fs->super))
problem = PR_1_EXTENT_ENDS_BEYOND;
+ else if (pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID)
+ problem = PR_1_EXTENT_CSUM_INVALID;
if (problem) {
report_problem:
@@ -1809,7 +1813,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 e74ad79..96b0de5 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -946,6 +946,12 @@ static struct e2fsck_problem problem_table[] = {
N_("The bad @b @i looks @n. "),
PROMPT_CLEAR, 0 },
+ /* Extent block 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 },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 1d63598..a1e7ffb 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -553,6 +553,9 @@ struct problem_context {
/* inode checksum does not match inode */
#define PR_1_INODE_CSUM_INVALID 0x010066
+/* extent block checksum does not match extent block */
+#define PR_1_EXTENT_CSUM_INVALID 0x010067
+
/*
* Pass 1b errors
*/
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..f79fa05 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->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,
Verify and calculate checksums of htree internal node blocks.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 145 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +
lib/ext2fs/ext2fs.h | 4 +
3 files changed, 152 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 28d7ef1..31dc4dc 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,151 @@
#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 = 0;
+ char *buf = (char *)dirent;
+ int size;
+ __u32 ncrc, old_csum;
+
+ size = count_offset + (count * sizeof(struct ext2_dx_entry));
+ old_csum = t->checksum;
+ t->checksum = 0;
+
+ inum = ext2fs_cpu_to_le32(inum);
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, size);
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)t,
+ sizeof(struct ext2_dx_tail));
+ t->checksum = old_csum;
+
+ *crc = ncrc;
+
+ return retval;
+}
+
+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->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->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 1394d02..da598d0 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -940,6 +940,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);
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/pass2.c | 31 ++++++++++++++++++++++++++-----
e2fsck/problem.c | 10 ++++++++++
e2fsck/problem.h | 6 ++++++
e2fsck/rehash.c | 16 ++++++++++++++--
4 files changed, 56 insertions(+), 7 deletions(-)
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 103b155..e11ddbf 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -522,7 +522,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;
@@ -533,6 +533,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);
@@ -547,9 +548,20 @@ static void parse_int_node(ext2_filsys fs,
#endif
ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+
+ if (failed_csum &&
+ 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 &&
+ 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
@@ -560,8 +572,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))
@@ -729,6 +745,7 @@ static int check_dir_block(ext2_filsys fs,
struct problem_context pctx;
int dups_found = 0;
int ret;
+ int csum_size = 0;
cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -740,6 +757,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))
+ 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.
@@ -829,7 +850,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 + csum_size)) /
sizeof(struct ext2_dx_entry))))
dx_db->type = DX_DIRBLOCK_NODE;
}
@@ -1116,7 +1137,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 96b0de5..2e9ab7f 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1374,6 +1374,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: 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 a1e7ffb..08c06e1 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -821,6 +821,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..a472aad 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -495,6 +495,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 +520,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 +537,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;
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 | 193 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 193 insertions(+), 0 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index c7b9fd3..236a324 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,176 @@ 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->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 +649,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);
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 12a22ed..c7b9fd3 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);
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 | 64 +++++++++++++++++++++++++++++++++++++++++++++
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, 142 insertions(+), 27 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index b2b6596..17cc445 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1450,7 +1450,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;
@@ -1461,8 +1462,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);
@@ -1559,7 +1561,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 f7ce8e4..6cab32c 100644
--- a/e2fsck/pass1b.c
+++ b/e2fsck/pass1b.c
@@ -651,9 +651,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 77a0927..56a2242 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -1282,9 +1282,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 afec4b4..8aa7652 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 5a370d6..26f453b 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,70 @@
#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 = 0;
+ char *buf = (char *)hdr;
+ __u32 ncrc, old_crc = hdr->h_checksum;
+
+ hdr->h_checksum = 0;
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ if (ext2fs_le32_to_cpu(hdr->h_refcount) != 1) {
+ block = ext2fs_cpu_to_le64(block);
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&block,
+ sizeof(block));
+ } else {
+ inum = ext2fs_cpu_to_le32(inum);
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum,
+ sizeof(inum));
+ }
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, fs->blocksize);
+ hdr->h_checksum = old_crc;
+ *crc = ncrc;
+
+ return retval;
+}
+
+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 6de2da7..9d95c17 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -940,6 +940,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)))
@@ -1095,16 +1101,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]);
}
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/e2fsck.h | 1 +
e2fsck/pass2.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++------
e2fsck/pass3.c | 4 +++
e2fsck/problem.c | 10 +++++++
e2fsck/problem.h | 6 ++++
e2fsck/rehash.c | 15 +++++++++++
6 files changed, 103 insertions(+), 8 deletions(-)
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index d225d89..96a0cc9 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -483,6 +483,7 @@ extern void region_free(region_t region);
extern int region_allocate(region_t region, region_addr_t start, int n);
/* rehash.c */
+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 cf2079a..77a0927 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -745,8 +745,9 @@ static int check_dir_block(ext2_filsys fs,
struct problem_context pctx;
int dups_found = 0;
int ret;
- int 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;
@@ -759,8 +760,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))
- csum_size = sizeof(struct ext2_dx_tail);
+ 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
@@ -806,11 +809,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);
@@ -855,13 +862,48 @@ 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 + csum_size)) /
+ ((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;
+ 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);
+ 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++;
+ }
+ }
+ /* 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 {
@@ -1128,7 +1170,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
@@ -1145,16 +1187,33 @@ 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)) {
+ 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);
+ }
+
+ 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))
diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 86a509c..7ebbadc 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -658,8 +658,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 2e9ab7f..c78186d 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1384,6 +1384,16 @@ static struct e2fsck_problem problem_table[] = {
N_("@p @h %d: 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, 0 },
+
+ /* leaf node fails checksum */
+ { PR_2_LEAF_NODE_MISSING_CSUM,
+ N_("@d @i %i, %B, offset %N: @d has no 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 08c06e1..25d73ee 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -827,6 +827,12 @@ 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
+
/*
* Pass 3 errors
*/
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index e80f728..ee99f70 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -52,6 +52,15 @@
#include "e2fsck.h"
#include "problem.h"
+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;
@@ -900,8 +909,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);
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 | 3 +++
e2fsck/problem.c | 5 +++++
e2fsck/problem.h | 3 +++
3 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 17cc445..cbf017a 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1562,6 +1562,9 @@ 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 &&
+ fix_problem(ctx, PR_1_EA_BLOCK_CSUM_INVALID, pctx))
+ goto clear_extattr;
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/problem.c b/e2fsck/problem.c
index c78186d..35e078c 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -952,6 +952,11 @@ static struct e2fsck_problem problem_table[] = {
"%c, @n physical @b %b, len %N)\n"),
PROMPT_CLEAR, 0 },
+ /* Checksum failure reading extended attribute block */
+ { PR_1_EA_BLOCK_CSUM_INVALID,
+ N_("Extended attribute @a @b %b for @i %i does not match. "),
+ PROMPT_CLEAR, 0 },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 25d73ee..0045872 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -556,6 +556,9 @@ struct problem_context {
/* extent block checksum does not match extent block */
#define PR_1_EXTENT_CSUM_INVALID 0x010067
+/* ea block checksum invalid */
+#define PR_1_EA_BLOCK_CSUM_INVALID 0x010068
+
/*
* Pass 1b errors
*/
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 | 143 +++++++++++++++++++++++++++++++++++++++++++++
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, 356 insertions(+), 82 deletions(-)
diff --git a/debugfs/htree.c b/debugfs/htree.c
index f79fa05..ddd1539 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 7cfc739..b2b6596 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 e11ddbf..cf2079a 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -746,6 +746,7 @@ static int check_dir_block(ext2_filsys fs,
int dups_found = 0;
int ret;
int csum_size = 0;
+ int failed_csum = 0;
cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -796,10 +797,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;
@@ -1137,7 +1142,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) {
@@ -1148,7 +1153,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))
@@ -1468,7 +1474,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 7164aa9..86a509c 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -197,7 +197,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);
@@ -443,7 +444,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;
@@ -684,6 +685,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,
@@ -724,7 +726,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) {
@@ -777,6 +780,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 a472aad..e80f728 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -62,6 +62,7 @@ struct fill_dir_struct {
int dir_size;
int compress;
ino_t parent;
+ ext2_ino_t dir;
};
struct hash_entry {
@@ -106,7 +107,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;
}
@@ -397,7 +401,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;
@@ -408,6 +413,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",
@@ -418,6 +425,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);
@@ -432,9 +443,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++) {
@@ -449,12 +460,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)
@@ -483,6 +499,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;
}
@@ -640,6 +660,7 @@ struct write_dir_struct {
errcode_t err;
e2fsck_t ctx;
int cleared;
+ ext2_ino_t dir;
};
/*
@@ -671,7 +692,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;
@@ -693,6 +714,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);
@@ -745,6 +767,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;
@@ -804,7 +827,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 31dc4dc..5a370d6 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -93,6 +93,117 @@ 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->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->reserved_zero1 ||
+ translate(t->rec_len) != sizeof(struct ext2_dir_entry_tail) ||
+ translate(t->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 = 0;
+ char *buf = (char *)dirent;
+ __u32 ncrc;
+
+ inum = ext2fs_cpu_to_le32(inum);
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, size);
+ *crc = ncrc;
+
+ return retval;
+}
+
+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->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->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,
@@ -175,6 +286,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 da598d0..6de2da7 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -940,6 +940,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,
@@ -1023,12 +1035,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,
@@ -1417,6 +1433,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 b12bf2d..0d1b7b8 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -95,7 +95,7 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
/*
* Write out the inode and inode data block
*/
- retval = ext2fs_write_dir_block(fs, blk, block);
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
if (retval)
goto cleanup;
retval = ext2fs_write_new_inode(fs, ino, &inode);
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
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 236a324..4b91192 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -609,9 +609,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;
@@ -628,6 +630,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) {
@@ -658,9 +666,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);
}
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 26f453b..a171f21 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 9d95c17..972bcd6 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -940,6 +940,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 40a52c5..3abdaf0 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);
Whenever we are calculating a checksum for a piece of metadata that is
associated with an inode, incorporate i_generation into that calculation so
that old metadata blocks cannot be re-associated after a delete/create cycle.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 45 +++++++++++++++++++++++++++++++++++++++------
lib/ext2fs/mkdir.c | 8 +++++---
2 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index a171f21..c064c0d 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -72,7 +72,8 @@ static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum,
{
errcode_t retval = 0;
char *buf = (char *)hdr;
- __u32 ncrc, old_crc = hdr->h_checksum;
+ __u32 gen, ncrc, old_crc = hdr->h_checksum;
+ struct ext2_inode inode;
hdr->h_checksum = 0;
ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
@@ -82,14 +83,21 @@ static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum,
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&block,
sizeof(block));
} else {
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ goto out;
inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum,
sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen,
+ sizeof(gen));
}
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, fs->blocksize);
hdr->h_checksum = old_crc;
*crc = ncrc;
+out:
return retval;
}
@@ -249,15 +257,23 @@ static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum,
{
errcode_t retval = 0;
char *buf = (char *)dirent;
- __u32 ncrc;
+ __u32 ncrc, gen;
+ struct ext2_inode inode;
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ goto out;
inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen, sizeof(gen));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, size);
*crc = ncrc;
+out:
return retval;
}
@@ -311,16 +327,23 @@ static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
errcode_t retval = 0;
char *buf = (char *)dirent;
int size;
- __u32 ncrc, old_csum;
+ __u32 ncrc, old_csum, gen;
+ struct ext2_inode inode;
size = count_offset + (count * sizeof(struct ext2_dx_entry));
old_csum = t->checksum;
t->checksum = 0;
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ goto out;
+
inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen, sizeof(gen));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, size);
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)t,
sizeof(struct ext2_dx_tail));
@@ -328,6 +351,7 @@ static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
*crc = ncrc;
+out:
return retval;
}
@@ -431,19 +455,26 @@ static errcode_t ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum,
__u32 *crc)
{
int size;
- __u32 ncrc;
- errcode_t retval = 0;
+ __u32 ncrc, 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)
+ goto out;
inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen, sizeof(gen));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)eh, size);
*crc = ncrc;
+out:
return retval;
}
@@ -584,7 +615,7 @@ static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_inode_large *inode,
__u32 *crc, int has_hi)
{
- __u32 ncrc;
+ __u32 ncrc, gen;
struct ext2_inode_large *desc = inode;
size_t size = fs->super->s_inode_size;
__u16 old_lo;
@@ -599,9 +630,11 @@ static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
}
inum = ext2fs_cpu_to_le32(inum);
+ gen = inode->i_generation;
ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen, sizeof(gen));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)desc, size);
*crc = ncrc;
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index 0d1b7b8..cb1e5a7 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 it.
*/
- retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
if (retval)
goto cleanup;
- retval = ext2fs_write_new_inode(fs, ino, &inode);
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
if (retval)
goto cleanup;
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 d2a8c80..0881002 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1186,6 +1186,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) {
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 4b91192..7299ad6 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -699,6 +699,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);
}
/*
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 c064c0d..00a1d6a 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 972bcd6..bcb8526 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -940,6 +940,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 3abdaf0..257ec78 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
Change the block group algorithm to use the same algorithm as the rest of the
metadata_csum, if a certain incompat feature flag is set.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/e2p/feature.c | 2 ++
lib/ext2fs/csum.c | 62 ++++++++++++++++++++++++++++++++++++-----------------
2 files changed, 44 insertions(+), 20 deletions(-)
diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
index 63486f3..dc52ba7 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -85,6 +85,8 @@ static struct feature feature_list[] = {
"mmp" },
{ E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG,
"flex_bg"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM,
+ "bg_use_meta_csum"},
{ 0, 0, 0 },
};
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 00a1d6a..f938c83 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -721,32 +721,54 @@ 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) &&
+ EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM)) {
+ /* new metadata csum code */
+ __u16 old_crc;
+ __u32 crc32;
+
+ old_crc = desc->bg_checksum;
+ desc->bg_checksum = 0;
+ crc32 = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ crc32 = ext2fs_crc32c_le(crc32, (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;
}
There are some features (gdt_csum, 64bit, bg_use_meta_csum) that help
metadata_csum to work optimally. 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 | 35 ++++++++++++++++++++++++++++++++++-
1 files changed, 34 insertions(+), 1 deletions(-)
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index ba7625d..c07c468 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -884,7 +884,8 @@ static __u32 ok_features[3] = {
EXT2_FEATURE_INCOMPAT_META_BG|
EXT4_FEATURE_INCOMPAT_FLEX_BG|
EXT4_FEATURE_INCOMPAT_MMP |
- EXT4_FEATURE_INCOMPAT_64BIT,
+ EXT4_FEATURE_INCOMPAT_64BIT |
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM,
/* R/O compat */
EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
@@ -2298,6 +2299,38 @@ 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_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ printf(_("Group descriptor checksums "
+ "are not enabled. This reduces the "
+ "coverage of metadata checksumming. "
+ "Pass -O uninit_bg to rectify.\n"));
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+ !EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM))
+ printf(_("Group descriptor checksums will not use "
+ "the faster metadata_checksum algorithm. "
+ "Pass -O bg_use_meta_csum to rectify.\n"));
+ 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);
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 98af8b0..ba7625d 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -2418,6 +2418,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);
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 35e078c..c36c527 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -428,6 +428,11 @@ static struct e2fsck_problem problem_table[] = {
N_("@S has invalid MMP magic. "),
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 0045872..503cb39 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -242,6 +242,9 @@ struct problem_context {
/* Superblock has invalid MMP magic. */
#define PR_0_MMP_INVALID_MAGIC 0x000043
+/* Superblock has invalid MMP checksum. */
+#define PR_0_MMP_CSUM_INVALID 0x000044
+
/*
* Pass 1 errors
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 0881002..3f7ca62 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1086,6 +1086,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;
}
When toggling the bg_use_meta_csum feature, we should rewrite the block groups
with the desired checksum.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 7299ad6..8301927 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -92,6 +92,7 @@ static unsigned long new_inode_size;
static char *ext_mount_opts;
static int usrquota, grpquota;
static int rewrite_checksums;
+static int rewrite_bgs_for_checksum;
int journal_size, journal_flags;
char *journal_device;
@@ -137,7 +138,8 @@ static __u32 ok_features[3] = {
EXT2_FEATURE_INCOMPAT_FILETYPE |
EXT3_FEATURE_INCOMPAT_EXTENTS |
EXT4_FEATURE_INCOMPAT_FLEX_BG |
- EXT4_FEATURE_INCOMPAT_MMP,
+ EXT4_FEATURE_INCOMPAT_MMP |
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM,
/* R/O compat */
EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
@@ -157,7 +159,8 @@ static __u32 clear_ok_features[3] = {
/* Incompat */
EXT2_FEATURE_INCOMPAT_FILETYPE |
EXT4_FEATURE_INCOMPAT_FLEX_BG |
- EXT4_FEATURE_INCOMPAT_MMP,
+ EXT4_FEATURE_INCOMPAT_MMP |
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM,
/* R/O compat */
EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
@@ -692,7 +695,11 @@ static void rewrite_inodes(ext2_filsys fs)
static void rewrite_metadata_checksums(ext2_filsys fs)
{
+ int i;
+
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ 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);
@@ -708,6 +715,28 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
}
/*
+ * Rewrite just the block group checksums. Only call this function if
+ * you're _not_ calling rewrite_metadata_checksums; this function exists
+ * to handle the case that you're changing bg_use_meta_csum and NOT changing
+ * either gdt_csum or metadata_csum.
+ */
+static void rewrite_bg_checksums(ext2_filsys fs)
+{
+ int i;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM) ||
+ !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return;
+
+ 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);
+}
+
+/*
* Update the feature set as provided by the user.
*/
static int update_feature_set(ext2_filsys fs, char *features)
@@ -879,6 +908,20 @@ mmp_error:
}
}
+ if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM)) {
+ if (check_fsck_needed(fs))
+ exit(1);
+ rewrite_bgs_for_checksum = 1;
+ }
+
+ if (FEATURE_OFF(E2P_FEATURE_INCOMPAT,
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM)) {
+ if (check_fsck_needed(fs))
+ exit(1);
+ rewrite_bgs_for_checksum = 1;
+ }
+
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
if (check_fsck_needed(fs))
@@ -2540,6 +2583,8 @@ retry_open:
}
if (rewrite_checksums)
rewrite_metadata_checksums(fs);
+ else if (rewrite_bgs_for_checksum)
+ rewrite_bg_checksums(fs);
if (I_flag) {
if (mount_flags & EXT2_MF_MOUNTED) {
fputs(_("The inode size may only be "
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 8301927..2b06a86 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -704,6 +704,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,
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 dc52ba7..f85ed14 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 33c0933..a9cec88 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -313,6 +313,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;
@@ -379,6 +389,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)
@@ -404,6 +423,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"
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
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);
}
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;
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 | 39 +++++++++++++++++++++++++++++++++++++++
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, 65 insertions(+), 2 deletions(-)
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 1d2b2f3..d2e2ff2 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -2248,6 +2248,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 f938c83..3e4162b 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,45 @@
#define STATIC static
#endif
+static __u32 ext2fs_mmp_csum(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ int offset = offsetof(struct mmp_struct, mmp_checksum);
+ __u32 crc;
+
+ crc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ crc = ext2fs_crc32c_le(crc, (unsigned char *)mmp, offset);
+
+ return crc;
+}
+
+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 bcb8526..e107e61 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -940,6 +940,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);
@@ -1428,6 +1430,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)
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;
Add metadata checksumming to the list of supported features.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext2fs.h | 12 ++++++++----
lib/ext2fs/kernel-jbd.h | 3 ++-
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index e107e61..a09597e 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -567,7 +567,8 @@ typedef struct ext2_icount *ext2_icount_t;
EXT3_FEATURE_INCOMPAT_EXTENTS|\
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
EXT4_FEATURE_INCOMPAT_MMP|\
- EXT4_FEATURE_INCOMPAT_64BIT)
+ EXT4_FEATURE_INCOMPAT_64BIT|\
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM)
#else
#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
@@ -576,7 +577,8 @@ typedef struct ext2_icount *ext2_icount_t;
EXT3_FEATURE_INCOMPAT_EXTENTS|\
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
EXT4_FEATURE_INCOMPAT_MMP|\
- EXT4_FEATURE_INCOMPAT_64BIT)
+ EXT4_FEATURE_INCOMPAT_64BIT|\
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM)
#endif
#ifdef CONFIG_QUOTA
#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
@@ -586,7 +588,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|\
@@ -594,7 +597,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
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 a09597e..7344289 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -940,7 +940,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');
- }
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 96a0cc9..d0249cb 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);
}
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;
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>
On 2011-12-13, at 19:14, "Darrick J. Wong" <[email protected]> wrote:
> Display the inode bitmap checksum for each block group.
>
> Signed-off-by: Darrick J. Wong <[email protected]>
> ---
> misc/dumpe2fs.c | 5 +++++
> 1 files changed, 5 insertions(+), 0 deletions(-)
>
>
> diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
> index 5b114e9..98bee2b 100644
> --- a/misc/dumpe2fs.c
> +++ b/misc/dumpe2fs.c
> @@ -226,6 +226,11 @@ 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_desc_size >= EXT2_MIN_DESC_SIZE_64BIT &&
> + fs->super->s_feature_ro_compat &
> + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
> + printf(_(", csum 0x%08x"),
This shouldn't only be printed for 64 byte descriptors. There is a checksum even for 32 byte descriptors.
Cheers, Andreas
On 2011-12-14, at 2:15, "Darrick J. Wong" <[email protected]> wrote:
> Check htree internal node checksums. If broken, ask user to clear the htree
> index and recreate it later.
>
> @@ -560,8 +572,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);
How does this handle the case where the METADATA_CSUM feature is newly enabled but there is a dirent usinf the end of the leaf block? It definitely shouldn't cause the last entry to be considered invalid.
> @@ -829,7 +850,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 + csum_size)) /
> sizeof(struct ext2_dx_entry))))
> dx_db->type = DX_DIRBLOCK_NODE;
Same here.
> diff --git a/e2fsck/problem.c b/e2fsck/problem.c
> index 96b0de5..2e9ab7f 100644
> --- a/e2fsck/problem.c
> +++ b/e2fsck/problem.c
> @@ -1374,6 +1374,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: node fails checksum\n"),
This error message should include "internal" in the printed message.
Cheers, Andreas
On 2011-12-14, at 2:15, "Darrick J. Wong" <[email protected]> wrote:
> 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.
>
> diff --git a/e2fsck/problem.c b/e2fsck/problem.c
> index e74ad79..96b0de5 100644
> --- a/e2fsck/problem.c
> +++ b/e2fsck/problem.c
> @@ -946,6 +946,12 @@ static struct e2fsck_problem problem_table[] = {
> N_("The bad @b @i looks @n. "),
> PROMPT_CLEAR, 0 },
>
> + /* Extent block 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 },
Since the comment above the problem definition is the only place that the full string can be found, it should match the printed string exactly. In this case it is missing "inode" at the start and "checksum" in the middle of the comment.
Cheers, Andreas
On Mon, Dec 19, 2011 at 11:28:44AM +0100, Andreas Dilger wrote:
> On 2011-12-14, at 2:14 AM, Darrick J. Wong wrote:
> > Write out checksummed inodes even when writing out a zeroed table.
>
> Looking at the code, this is only writing out checksummed inodes
> for the reserved inodes, not all of them, right? Otherwise, going
> back to writing out the whole inode table would be really slowing
> down mke2fs compared to lazy_itable_init.
Assuming that the inodes numbered less than EXT2_FIRST_INO are reserved, then
yes it should be only writing reserved inodes (unless the user forces mkfs to
write all inodes).
--D
>
> > diff --git a/misc/mke2fs.c b/misc/mke2fs.c
> > index 0ef2531..98af8b0 100644
> > --- a/misc/mke2fs.c
> > +++ b/misc/mke2fs.c
> > @@ -305,6 +305,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;
> > @@ -350,6 +371,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);
>
> > }
>
>
> Cheers, Andreas
>
>
>
>
>
On Mon, Dec 19, 2011 at 10:36:14AM +0100, Andreas Dilger wrote:
> <html><body class="ApplePlainTextBody" style="word-wrap: break-word;
<html mail snip>
> </span>}<br></blockquote><br>Instead of having an icache that is allocating
> temporary buffers<br>to handle the inode size mismatch, and then copying the
> inode data<br>into the icache each time, it makes more sense to use the
> allocated<br>buffers for the icache directly.<br><br>Cheers,
> Andreas<br><br><br><br><br><br></body></html>
Ok, I guess we can go for the larger reorganization of the code. I was trying
not to read stuff into the cache before it was verified, but as long as we take
care of the cache bookkeeping it's fine.
--D
On Mon, Dec 19, 2011 at 07:57:21AM +0100, Andreas Dilger wrote:
> On 2011-12-14, at 2:15, "Darrick J. Wong" <[email protected]> wrote:
>
> > Check htree internal node checksums. If broken, ask user to clear the htree
> > index and recreate it later.
> >
> > @@ -560,8 +572,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);
>
> How does this handle the case where the METADATA_CSUM feature is newly
> enabled but there is a dirent usinf the end of the leaf block? It definitely
> shouldn't cause the last entry to be considered invalid.
For htree nodes, tune2fs decrements limit if count < limit. If count == limit,
then fsck is run to rebuild the htree index. For its part, e2fsck will notice
that count == limit, zero the htree node, and add the directory inode to the
pass3 rebuild list.
> > @@ -829,7 +850,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 + csum_size)) /
> > sizeof(struct ext2_dx_entry))))
> > dx_db->type = DX_DIRBLOCK_NODE;
>
> Same here.
>
> > diff --git a/e2fsck/problem.c b/e2fsck/problem.c
> > index 96b0de5..2e9ab7f 100644
> > --- a/e2fsck/problem.c
> > +++ b/e2fsck/problem.c
> > @@ -1374,6 +1374,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: node fails checksum\n"),
>
> This error message should include "internal" in the printed message.
How about "htree node"? I wondered why the other PR_*_HTREE messages don't say
"htree" -- is there a particular reason for this?
--D
On Mon, Dec 19, 2011 at 07:50:07AM +0100, Andreas Dilger wrote:
> On 2011-12-14, at 2:15, "Darrick J. Wong" <[email protected]> wrote:
>
> > 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.
> >
> > diff --git a/e2fsck/problem.c b/e2fsck/problem.c
> > index e74ad79..96b0de5 100644
> > --- a/e2fsck/problem.c
> > +++ b/e2fsck/problem.c
> > @@ -946,6 +946,12 @@ static struct e2fsck_problem problem_table[] = {
> > N_("The bad @b @i looks @n. "),
> > PROMPT_CLEAR, 0 },
> >
> > + /* Extent block 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 },
>
> Since the comment above the problem definition is the only place that the
> full string can be found, it should match the printed string exactly. In this
> case it is missing "inode" at the start and "checksum" in the middle of the
> comment.
Ok.
--D
>
> Cheers, Andreas--
> 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
>
On Mon, Dec 19, 2011 at 11:50:06AM +0100, Andreas Dilger wrote:
> On 2011-12-14, at 2:14 AM, Darrick J. Wong wrote:
> > Display the block bitmap checksum when displaying block groups.
> >
> > diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
> > index 98bee2b..33c0933 100644
> > --- a/misc/dumpe2fs.c
> > +++ b/misc/dumpe2fs.c
> > @@ -222,6 +222,11 @@ 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_desc_size >= EXT2_MIN_DESC_SIZE_64BIT &&
> > + fs->super->s_feature_ro_compat &
> > + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
> > + printf(_(", csum 0x%08x"),
> > + ext2fs_block_bitmap_checksum(fs, i));
>
> Printing the checksum should only depend on METADATA_CSUM, and has nothing
> to do with DESC_SIZE_64BIT. That is a holdover from when there was only
> a 32-bit checksum for the inode/block bitmaps and no 16-bit checksum in
> the small group descriptor.
Oops, I will fix both of these. Thank you for catching them!
--D
>
> Cheers, Andreas
>
>
>
>
>
On Sun, Dec 18, 2011 at 07:18:20AM -0700, Andreas Dilger wrote:
> On 2011-12-13, at 18:14, "Darrick J. Wong" <[email protected]> wrote:
> > Provide a field in the block group descriptor to store inode bitmap checksum,
> > and some helper functions to calculate and verify it.
> > +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(~0, fs->super->s_uuid,
> > + sizeof(fs->super->s_uuid));
>
> It makes sense to precompute the uuid checksum and store it in the
> ext2_filsys struct so that it is accessible everywhere, like is done in the
> kernel.
I wonder how much that'll help compared to the IO overhead, but I suppose every
bit helps.
> > + calculated = ext2fs_crc32c_le(calculated, (unsigned char *)bitmap,
> > + size);
> > + if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION)
> > + provided |= (__u32)gdp->bg_inode_bitmap_csum_hi << 16;
> > + else
> > + calculated &= 0xFFFF;
> > +
> > + return provided == calculated;
> > +}
> > +
> >
> > +++ b/lib/ext2fs/ext2_fs.h
> > @@ -191,6 +191,10 @@ struct ext4_group_desc
> > __u32 bg_reserved;
> > };
> >
> > +#define EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION \
> > + (offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
> > + sizeof(__u16))
>
> It is a bit misleading to call this constant the "location" since it is
> actually the end of the csum_hi field. Either the sizeof(__u16) should be
> moved to the caller, or this should be renamed to
> EXT4_BG_INODE_BITMAP_CSUM_HI_END or similar.
Ok.
--D
On Mon, Dec 19, 2011 at 03:12:03PM +0100, Andreas Dilger wrote:
> On 2011-12-13, at 7:13 PM, Darrick J. Wong wrote:
> > diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
> > index 596923e..1f7c641 100644
> > --- a/lib/ext2fs/csum.c
> > +++ b/lib/ext2fs/csum.c
> > @@ -30,6 +30,89 @@
> > #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 ncrc;
>
> Why use "ncrc" in this function instead of just using "*crc" directly?
>
> > + struct ext2_inode_large *desc = inode;
> > + size_t size = fs->super->s_inode_size;
> > + __u16 old_lo;
> > + __u16 old_hi = 0;
> > + errcode_t retval = 0;
>
> Also, it seems "retval" isn't useful, since it is always 0, which can
> be returned directly at the end.
In the old days it wasn't automatic that retval is always 0, therefore I
allocated a temporary ncrc in case the function failed. Since that no longer
happens and ext2fs_inode_csum is a static function now, I will undo all
that.
> > +
> > + 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);
> > + ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
> > + sizeof(fs->super->s_uuid));
> > + ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
> > + ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)desc, size);
> > + *crc = ncrc;
> > +
> > + inode->i_checksum_lo = old_lo;
> > + if (has_hi)
> > + inode->i_checksum_hi = old_hi;
> > + return retval;
> > +}
> > +
> > +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_LOCATION);
> > +
> > + 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;
>
> It seems a bit odd to get the low bytes of "provided" before calculating
> the checksum, but the high bytes of after calculating the checksum. It
> isn't incorrect since the checksum is preserved over the call to
> ext2fs_inode_csum(), but seems a bit strange.
I think this is a merge error.
> > diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
> > index ce2fd66..4bcf1de 100644
> > --- a/lib/ext2fs/ext2_fs.h
> > +++ b/lib/ext2fs/ext2_fs.h
> > @@ -459,6 +459,10 @@ struct ext2_inode_large {
> > __u32 i_version_hi; /* high 32 bits for 64-bit version */
> > };
> >
> > +#define EXT4_INODE_CSUM_HI_EXTRA_LOCATION \
> > + (offsetof(struct ext2_inode_large, i_checksum_hi) + sizeof(__u16) - \
> > + EXT2_GOOD_OLD_INODE_SIZE)
>
> It also makes sense to call this EXT4_INODE_CSUM_HI_END or similar, since
> _LOCATION incorrectly implies (to me at least) that this is the offset of
> the i_checksum_hi field, when it is actually the byte beyond the end.
_END it is.
--D
On Mon, Dec 19, 2011 at 11:28:24AM +0100, Andreas Dilger wrote:
> On 2011-12-14, at 2:14 AM, Darrick J. Wong wrote:
> > Detect mismatches of the inode and checksum, and prompt the user to fix the
> > situation.
> >
> > @@ -739,6 +740,12 @@ 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 &&
> > + fix_problem(ctx, PR_1_INODE_CSUM_INVALID, &pctx))
> > + goto clear_inode;
>
> If the user enters "n" here to clear the inode, does it make sense to
> also ask if they want the checksum be corrected? If this is also "n"
> (as is the case with "e2fsck -n") then nothing is done, but in some
> cases it makes sense to allow the user to keep the inode rather than
> only having the option to erase it.
I was thinking something like this might work:
if (csum_invalid)
ask and then jump to clear_inode;
...all other inode sanity checks go here...
if (csum_was_invalid)
ask and correct inode checksum;
That way, if the inode manages to survive all the other sanity checks, the user
has the opportunity to save the inode. If the inode contains junk, then the
user will probably clear the inode on account of the garbage.
Come to think of it, this is probably a good idea for everything else. I
think?
> > diff --git a/e2fsck/problem.c b/e2fsck/problem.c
> > index f042b89..89dc72b 100644
> > --- a/e2fsck/problem.c
> > +++ b/e2fsck/problem.c
> > @@ -934,7 +934,12 @@ static struct e2fsck_problem problem_table[] = {
> > + /* inode checksum does not match inode */
> > + { PR_1_INODE_CSUM_INVALID,
> > + N_("@i %i checksum does not match @i. "),
> > + PROMPT_FIX, PR_PREEN_OK },
>
> Also, "PROMPT_FIX" is misleading if the "fix" is to erase the inode.
> It should instead be "PROMPT_CLEAR_INODE".
Ok.
--D
>
> Cheers, Andreas
>
>
>
>
>
On Mon, Dec 19, 2011 at 05:54:23AM +0100, Andreas Dilger wrote:
> On 2011-12-14, at 2:13 AM, 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.
> >
> > diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
> > index 0f8cde8..ce2fd66 100644
> > --- a/lib/ext2fs/ext2_fs.h
> > +++ b/lib/ext2fs/ext2_fs.h
> > @@ -234,6 +234,13 @@ struct ext2_dx_countlimit {
> > +/*
> > + * This goes at the end of each htree block.
> > + */
> > +struct ext2_dx_tail {
> > + __u32 reserved;
> > + __u32 checksum; /* crc32c(uuid+inum+dxblock) */
> > +};
>
> These should have a structure prefix, like "dxt_reserved" and "dxt_checksum"
> to make them easier to find with tags.
>
> > /*
> > + * This is a bogus directory entry at the end of each leaf block that
> > + * records checksums.
> > + */
> > +struct ext2_dir_entry_tail {
> > + __u32 reserved_zero1; /* Pretend to be unused */
> > + __u16 rec_len; /* 12 */
> > + __u16 reserved_name_len; /* 0xDE00, fake namelen/filetype */
> > + __u32 checksum; /* crc32c(uuid+inode+dirent) */
> > +};
>
> Similarly, this should get a structure prefix for all of the fields, like
> "det_checksum" or similar.
Ok.
--D