Hi all,
This patchset adds support in e2fsprogs for attaching crc32c checksums to most
of the ext4 metadata objects. A full design document is on the ext4 wiki[1].
Please see the cover letter for the kernel patches for a more thorough summary
of this work.
Please have a look at the design document and patches, and please feel free to
suggest any changes.
v2: Checksum the MMP block, store the checksum type in the superblock, include
the inode generation in file checksums, and finally solve the problem of limited
space in block groups by splitting the checksum into two halves.
v2.1: Checksum the reserved parts of the htree tail structure.
v2.2: Reincorporate the FS UUID in the bitmap checksum calcuations. Move all
disk layout changes to the front and the feature flag enablement to the end of
the patch set. Fail journal recovery if revoke block fails checksum.
v2.3: Change fsck handling of checksum errors: First ask to clear the item; if
the user declines, run the regular checks; if no corrective actions result, ask
to reset the checksum. Also, precompute the UUID checksum seed, refactor the
inode generation patch into respective patches, and clean up structure member
declarations.
This patchset has been tested on 3.2.0-rc7 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/articles/e/x/t/Ext4_Disk_Layout_aecb.html
Change libext2fs to read and write full-size inodes in preparation for the
metadata checksumming patchset, which will require this. Due to ABI
compatibility requirements, this change must be hidden from client programs.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 3 +
lib/ext2fs/ext2fsP.h | 2 -
lib/ext2fs/inode.c | 139 +++++++++++++++++++++++++++-----------------------
3 files changed, 79 insertions(+), 65 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 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..a4f6670 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -74,7 +74,9 @@ errcode_t ext2fs_flush_icache(ext2_filsys fs)
static errcode_t create_icache(ext2_filsys fs)
{
+ int i;
errcode_t retval;
+ void *p;
if (fs->icache)
return 0;
@@ -93,13 +95,20 @@ static errcode_t create_icache(ext2_filsys fs)
fs->icache->cache_size = 4;
fs->icache->refcount = 1;
retval = ext2fs_get_array(fs->icache->cache_size,
- sizeof(struct ext2_inode_cache_ent),
+ sizeof(struct ext2_inode_cache_ent) +
+ EXT2_INODE_SIZE(fs->super),
&fs->icache->cache);
if (retval) {
ext2fs_free_mem(&fs->icache->buffer);
ext2fs_free_mem(&fs->icache);
return retval;
}
+
+ for (i = 0, p = (void *)(fs->icache->cache + fs->icache->cache_size);
+ i < fs->icache->cache_size;
+ i++, p += EXT2_INODE_SIZE(fs->super))
+ fs->icache->cache[i].inode = p;
+
ext2fs_flush_icache(fs);
return 0;
}
@@ -407,6 +416,8 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
{
errcode_t retval;
int extra_bytes = 0;
+ const int length = EXT2_INODE_SIZE(scan->fs->super);
+ struct ext2_inode *iptr = inode;
EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
@@ -469,6 +480,12 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
#endif
}
+ if (bufsize < length) {
+ retval = ext2fs_get_mem(length, &iptr);
+ if (retval)
+ return retval;
+ }
+
retval = 0;
if (extra_bytes) {
memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
@@ -477,26 +494,26 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->bytes_left -= scan->inode_size - extra_bytes;
#ifdef WORDS_BIGENDIAN
- memset(inode, 0, bufsize);
+ memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
- (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) iptr,
(struct ext2_inode_large *) scan->temp_buffer,
- 0, bufsize);
+ 0, length);
#else
- *inode = *((struct ext2_inode *) scan->temp_buffer);
+ memcpy(iptr, scan->temp_buffer, length);
#endif
if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
} else {
#ifdef WORDS_BIGENDIAN
- memset(inode, 0, bufsize);
+ memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
- (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) iptr,
(struct ext2_inode_large *) scan->ptr,
- 0, bufsize);
+ 0, length);
#else
- memcpy(inode, scan->ptr, bufsize);
+ memcpy(iptr, scan->ptr, length);
#endif
scan->ptr += scan->inode_size;
scan->bytes_left -= scan->inode_size;
@@ -507,6 +524,10 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->inodes_left--;
scan->current_inode++;
*ino = scan->current_inode;
+ if (inode != iptr) {
+ memcpy(inode, iptr, bufsize);
+ ext2fs_free_mem(&iptr);
+ }
return retval;
}
@@ -527,8 +548,11 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
unsigned long group, block, offset;
char *ptr;
errcode_t retval;
- int clen, i, inodes_per_block, length;
+ int clen, i, inodes_per_block;
io_channel io;
+ int length = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode *iptr;
+ int cache_slot;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -547,13 +571,10 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
return retval;
}
/* Check to see if it's in the inode cache */
- if (bufsize == sizeof(struct ext2_inode)) {
- /* only old good inode can be retrieved from the cache */
- for (i=0; i < fs->icache->cache_size; i++) {
- if (fs->icache->cache[i].ino == ino) {
- *inode = fs->icache->cache[i].inode;
- return 0;
- }
+ for (i = 0; i < fs->icache->cache_size; i++) {
+ if (fs->icache->cache[i].ino == ino) {
+ memcpy(inode, fs->icache->cache[i].inode, bufsize);
+ return 0;
}
}
if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
@@ -578,11 +599,10 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
}
offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
- length = EXT2_INODE_SIZE(fs->super);
- if (bufsize < length)
- length = bufsize;
+ cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size;
+ iptr = fs->icache->cache[cache_slot].inode;
- ptr = (char *) inode;
+ ptr = (char *) iptr;
while (length) {
clen = length;
if ((offset + length) > fs->blocksize)
@@ -604,18 +624,18 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
ptr += clen;
block_nr++;
}
+ length = EXT2_INODE_SIZE(fs->super);
#ifdef WORDS_BIGENDIAN
- ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode,
- (struct ext2_inode_large *) inode,
- 0, bufsize);
+ ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
+ (struct ext2_inode_large *) iptr,
+ 0, length);
#endif
- /* Update the inode cache */
- fs->icache->cache_last = (fs->icache->cache_last + 1) %
- fs->icache->cache_size;
- fs->icache->cache[fs->icache->cache_last].ino = ino;
- fs->icache->cache[fs->icache->cache_last].inode = *inode;
+ /* Update the inode cache bookkeeping */
+ fs->icache->cache_last = cache_slot;
+ fs->icache->cache[cache_slot].ino = ino;
+ memcpy(inode, iptr, bufsize);
return 0;
}
@@ -633,9 +653,10 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
blk64_t block_nr;
unsigned long group, block, offset;
errcode_t retval = 0;
- struct ext2_inode_large temp_inode, *w_inode;
+ struct ext2_inode_large *w_inode;
char *ptr;
- int clen, i, length;
+ int clen, i;
+ int length = EXT2_INODE_SIZE(fs->super);
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -646,46 +667,43 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
return retval;
}
+ if ((ino == 0) || (ino > fs->super->s_inodes_count))
+ return EXT2_ET_BAD_INODE_NUM;
+
+ /* Prepare our shadow buffer for read/modify/byteswap/write */
+ retval = ext2fs_get_mem(length, &w_inode);
+ if (retval)
+ return retval;
+ if (bufsize < length)
+ retval = ext2fs_read_inode_full(fs, ino,
+ (struct ext2_inode *)w_inode,
+ length);
+ if (retval)
+ goto errout;
+
/* Check to see if the inode cache needs to be updated */
if (fs->icache) {
for (i=0; i < fs->icache->cache_size; i++) {
if (fs->icache->cache[i].ino == ino) {
- fs->icache->cache[i].inode = *inode;
+ memcpy(fs->icache->cache[i].inode, inode,
+ bufsize);
break;
}
}
} else {
retval = create_icache(fs);
if (retval)
- return retval;
+ goto errout;
}
+ memcpy(w_inode, inode, bufsize);
- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
-
- if ((ino == 0) || (ino > fs->super->s_inodes_count))
- return EXT2_ET_BAD_INODE_NUM;
-
- length = bufsize;
- if (length < EXT2_INODE_SIZE(fs->super))
- length = EXT2_INODE_SIZE(fs->super);
-
- if (length > (int) sizeof(struct ext2_inode_large)) {
- w_inode = malloc(length);
- if (!w_inode) {
- retval = ENOMEM;
- goto errout;
- }
- } else
- w_inode = &temp_inode;
- memset(w_inode, 0, length);
+ if (!(fs->flags & EXT2_FLAG_RW)) {
+ retval = EXT2_ET_RO_FILSYS;
+ goto errout;
+ }
#ifdef WORDS_BIGENDIAN
- ext2fs_swap_inode_full(fs, w_inode,
- (struct ext2_inode_large *) inode,
- 1, bufsize);
-#else
- memcpy(w_inode, inode, bufsize);
+ ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
#endif
group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
@@ -700,10 +718,6 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
- length = EXT2_INODE_SIZE(fs->super);
- if (length > bufsize)
- length = bufsize;
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;
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..4fb9485 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 dt_reserved;
+ __u32 dt_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 det_reserved_zero1; /* Pretend to be unused */
+ __u16 det_rec_len; /* 12 */
+ __u16 det_reserved_name_len; /* 0xDE00, fake namelen/filetype */
+ __u32 det_checksum; /* crc32c(uuid+inode+dirent) */
+};
+
+/*
* Ext2 directory file types. Only the low 3 bits are used. The
* other bits are reserved for now.
*/
@@ -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
*/
This patch adds the ability for the libext2fs functions to read and write the
inode checksum.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2_fs.h | 4 ++
lib/ext2fs/ext2fs.h | 4 ++
lib/ext2fs/inode.c | 40 ++++++++++++++++++----
lib/ext2fs/mkdir.c | 8 +++-
6 files changed, 131 insertions(+), 10 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 596923e..5091a00 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,88 @@
#define STATIC static
#endif
+static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode,
+ __u32 *crc, int has_hi)
+{
+ __u32 gen;
+ struct ext2_inode_large *desc = inode;
+ size_t size = fs->super->s_inode_size;
+ __u16 old_lo;
+ __u16 old_hi = 0;
+
+ old_lo = inode->i_checksum_lo;
+ inode->i_checksum_lo = 0;
+ if (has_hi) {
+ old_hi = inode->i_checksum_hi;
+ inode->i_checksum_hi = 0;
+ }
+
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = inode->i_generation;
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)desc, size);
+
+ inode->i_checksum_lo = old_lo;
+ if (has_hi)
+ inode->i_checksum_hi = old_hi;
+ return 0;
+}
+
+int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode)
+{
+ errcode_t retval;
+ __u32 provided, calculated;
+ int has_hi;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX ||
+ !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
+
+ provided = ext2fs_le16_to_cpu(inode->i_checksum_lo);
+ retval = ext2fs_inode_csum(fs, inum, inode, &calculated, has_hi);
+ if (retval)
+ return 0;
+ if (has_hi) {
+ __u32 hi = ext2fs_le16_to_cpu(inode->i_checksum_hi);
+ provided |= hi << 16;
+ } else
+ calculated &= 0xFFFF;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode)
+{
+ errcode_t retval;
+ __u32 crc;
+ int has_hi;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX ||
+ !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
+
+ retval = ext2fs_inode_csum(fs, inum, inode, &crc, has_hi);
+ if (retval)
+ return retval;
+ inode->i_checksum_lo = ext2fs_cpu_to_le16(crc & 0xFFFF);
+ if (has_hi)
+ inode->i_checksum_hi = ext2fs_cpu_to_le16(crc >> 16);
+ return 0;
+}
+
STATIC __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group)
{
__u16 crc = 0;
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index ccf1894..d4a5b10 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -443,4 +443,7 @@ ec EXT2_ET_MMP_CHANGE_ABORT,
ec EXT2_ET_MMP_OPEN_DIRECT,
"MMP: open with O_DIRECT failed"
+ec EXT2_ET_INODE_CSUM_INVALID,
+ "Inode checksum does not match inode"
+
end
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 4fb9485..ed648a9 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_END \
+ (offsetof(struct ext2_inode_large, i_checksum_hi) + sizeof(__u16) - \
+ EXT2_GOOD_OLD_INODE_SIZE)
+
#define i_dir_acl i_size_high
#if defined(__KERNEL__) || defined(__linux__)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index c00032e..858a338 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -939,6 +939,10 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);
/* csum.c */
+extern errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode);
+extern int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode);
extern void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group);
extern int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group);
extern errcode_t ext2fs_set_gdt_csum(ext2_filsys fs);
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index a4f6670..74703c5 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -417,7 +417,7 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
errcode_t retval;
int extra_bytes = 0;
const int length = EXT2_INODE_SIZE(scan->fs->super);
- struct ext2_inode *iptr = inode;
+ struct ext2_inode_large *iptr = (struct ext2_inode_large *)inode;
EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
@@ -493,6 +493,12 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->ptr += scan->inode_size - extra_bytes;
scan->bytes_left -= scan->inode_size - extra_bytes;
+ /* Verify the inode checksum. */
+ if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
+ (struct ext2_inode_large *)scan->temp_buffer))
+ retval = EXT2_ET_INODE_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
@@ -506,6 +512,12 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
} else {
+ /* Verify the inode checksum. */
+ if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
+ (struct ext2_inode_large *)scan->ptr))
+ retval = EXT2_ET_INODE_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
@@ -524,7 +536,7 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->inodes_left--;
scan->current_inode++;
*ino = scan->current_inode;
- if (inode != iptr) {
+ if (iptr != (struct ext2_inode_large *)inode) {
memcpy(inode, iptr, bufsize);
ext2fs_free_mem(&iptr);
}
@@ -551,7 +563,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
int clen, i, inodes_per_block;
io_channel io;
int length = EXT2_INODE_SIZE(fs->super);
- struct ext2_inode *iptr;
+ struct ext2_inode_large *iptr;
int cache_slot;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -600,7 +612,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size;
- iptr = fs->icache->cache[cache_slot].inode;
+ iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode;
ptr = (char *) iptr;
while (length) {
@@ -626,6 +638,11 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
}
length = EXT2_INODE_SIZE(fs->super);
+ /* Verify the inode checksum. */
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(fs, ino, iptr))
+ return EXT2_ET_INODE_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
(struct ext2_inode_large *) iptr,
@@ -674,12 +691,17 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
retval = ext2fs_get_mem(length, &w_inode);
if (retval)
return retval;
- if (bufsize < length)
+
+ if (bufsize < length) {
+ int old_flags = fs->flags;
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_read_inode_full(fs, ino,
(struct ext2_inode *)w_inode,
length);
- if (retval)
- goto errout;
+ fs->flags = old_flags;
+ if (retval)
+ goto errout;
+ }
/* Check to see if the inode cache needs to be updated */
if (fs->icache) {
@@ -706,6 +728,10 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
#endif
+ retval = ext2fs_inode_csum_set(fs, ino, w_inode);
+ if (retval)
+ goto errout;
+
group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
EXT2_INODE_SIZE(fs->super);
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index b12bf2d..861ddab 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -93,12 +93,14 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
inode.i_size = fs->blocksize;
/*
- * Write out the inode and inode data block
+ * Write out the inode and inode data block. The inode generation
+ * number is assigned by write_new_inode, which means that the call
+ * to write_dir_block must come after that.
*/
- retval = ext2fs_write_dir_block(fs, blk, block);
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
if (retval)
goto cleanup;
- retval = ext2fs_write_new_inode(fs, ino, &inode);
+ retval = ext2fs_write_dir_block(fs, blk, block);
if (retval)
goto cleanup;
Detect mismatches of the inode and checksum, and prompt the user to fix the
situation.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
e2fsck/problem.c | 10 +++++++++
e2fsck/problem.h | 6 +++++
3 files changed, 75 insertions(+), 1 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 62e49c6..c382a7b 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -540,6 +540,40 @@ extern void e2fsck_setup_tdb_icount(e2fsck_t ctx, int flags,
*ret = 0;
}
+static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino,
+ e2fsck_t ctx,
+ struct problem_context *pctx)
+{
+ errcode_t retval;
+ struct ext2_inode_large inode;
+
+ /*
+ * Reread inode. If we don't see checksum error, then this inode
+ * has been fixed elsewhere.
+ */
+ retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
+ sizeof(inode));
+ if (retval && retval != EXT2_ET_INODE_CSUM_INVALID)
+ return retval;
+ if (!retval)
+ return 0;
+
+ /*
+ * Checksum still doesn't match. That implies that the inode passes
+ * all the sanity checks, so maybe the checksum is simply corrupt.
+ * See if the user will go for fixing that.
+ */
+ if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx))
+ return 0;
+
+ retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
+ sizeof(inode));
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
void e2fsck_pass1(e2fsck_t ctx)
{
int i;
@@ -560,6 +594,7 @@ void e2fsck_pass1(e2fsck_t ctx)
int imagic_fs, extent_fs;
int busted_fs_time = 0;
int inode_size;
+ int failed_csum = 0;
init_resource_track(&rtrack, ctx->fs->io);
clear_problem_context(&pctx);
@@ -729,7 +764,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 +775,14 @@ void e2fsck_pass1(e2fsck_t ctx)
pctx.ino = ino;
pctx.inode = inode;
ctx->stashed_ino = ino;
+
+ /* Clear corrupt inode? */
+ if (pctx.errcode == EXT2_ET_INODE_CSUM_INVALID) {
+ if (fix_problem(ctx, PR_1_INODE_CSUM_INVALID, &pctx))
+ goto clear_inode;
+ failed_csum = 1;
+ }
+
if (inode->i_links_count) {
pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
ino, inode->i_links_count);
@@ -1135,6 +1179,20 @@ void e2fsck_pass1(e2fsck_t ctx)
} else
check_blocks(ctx, &pctx, block_buf);
+ /*
+ * If the inode failed the checksum and the user didn't
+ * clear the inode, test the checksum again -- if it still
+ * fails, ask the user if the checksum should be corrected.
+ */
+ if (failed_csum) {
+ pctx.errcode = recheck_bad_inode_checksum(fs, ino, ctx,
+ &pctx);
+ if (pctx.errcode) {
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
+
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index f042b89..39e34b4 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -934,8 +934,18 @@ static struct e2fsck_problem problem_table[] = {
/* Quota inode is user visible */
{ PR_1_QUOTA_INODE_NOT_HIDDEN,
N_("@q @i is visible to the user. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* inode checksum does not match inode */
+ { PR_1_INODE_CSUM_INVALID,
+ N_("@i %i checksum does not match @i. "),
PROMPT_CLEAR, PR_PREEN_OK },
+ /* inode passes checks, but checksum does not match inode */
+ { PR_1_INODE_ONLY_CSUM_INVALID,
+ N_("@i %i passes checks, but checksum does not match @i. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
/* Invalid bad inode */
{ PR_1_INVALID_BAD_INODE,
N_("The bad @b @i looks @n. "),
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 9db29d8..4ce9f41 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -550,6 +550,12 @@ struct problem_context {
/* Invalid bad inode */
#define PR_1_INVALID_BAD_INODE 0x010065
+/* inode checksum does not match inode */
+#define PR_1_INODE_CSUM_INVALID 0x010066
+
+/* inode passes checks, but checksum does not match inode */
+#define PR_1_INODE_ONLY_CSUM_INVALID 0x010067
+
/*
* Pass 1b errors
*/
Write out reserved inodes with full checksums when writing out a zeroed table.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/mke2fs.c | 29 ++++++++++++++++++++++++++++-
1 files changed, 28 insertions(+), 1 deletions(-)
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index f2d7c81..7888a2f 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
};
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);
Precompute the FS UUID checksum seed that is used for all metadata checksumming
operations and store it in ext2_filsys.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/super.c | 1 +
lib/ext2fs/ext2fs.h | 14 ++++++++++++++
lib/ext2fs/openfs.c | 2 ++
misc/mke2fs.c | 1 +
4 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/e2fsck/super.c b/e2fsck/super.c
index afec4b4..99c0b84 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -719,6 +719,7 @@ void check_super_block(e2fsck_t ctx)
if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
uuid_generate(sb->s_uuid);
+ ext2fs_init_csum_seed(fs);
fs->flags |= EXT2_FLAG_DIRTY;
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
}
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 16c4567..c00032e 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -279,6 +279,9 @@ struct struct_ext2_filsys {
* Time at which e2fsck last updated the MMP block.
*/
long mmp_last_written;
+
+ /* Precomputed FS UUID checksum for seeding other checksums */
+ __u32 csum_seed;
};
#if EXT2_FLAT_INCLUDES
@@ -1424,6 +1427,7 @@ extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
/* inline functions */
+extern void ext2fs_init_csum_seed(ext2_filsys fs);
extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
extern errcode_t ext2fs_get_memalign(unsigned long size,
unsigned long align, void *ptr);
@@ -1476,6 +1480,16 @@ extern int ext2fs_fstat(int fd, ext2fs_struct_stat *buf);
#ifndef EXT2_CUSTOM_MEMORY_ROUTINES
#include <string.h>
+_INLINE_ void ext2fs_init_csum_seed(ext2_filsys fs)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return;
+
+ fs->csum_seed = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+}
+
/*
* Allocate memory
*/
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 40a52c5..6cb629e 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -298,6 +298,8 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
retval = EXT2_ET_CORRUPT_SUPERBLOCK;
goto cleanup;
}
+ /* Precompute the FS UUID to seed other checksums */
+ ext2fs_init_csum_seed(fs);
/*
* Read group descriptors
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 0ef2531..f2d7c81 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -2315,6 +2315,7 @@ int main (int argc, char *argv[])
}
} else
uuid_generate(fs->super->s_uuid);
+ ext2fs_init_csum_seed(fs);
/*
* Initialize the directory index variables
When toggling metadata_csum, mark the inode bitmap dirty so that they are
written out with new checksums.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index cb1aff3..478b651 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -427,6 +427,9 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
ext2fs_init_csum_seed(fs);
rewrite_inodes(fs);
+ ext2fs_read_bitmaps(fs);
+ ext2fs_mark_ib_dirty(fs);
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
}
Display the inode bitmap checksum for each block group.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/dumpe2fs.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 5b114e9..ec4f0e9 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -226,6 +226,10 @@ static void list_desc (ext2_filsys fs)
print_number(ext2fs_inode_bitmap_loc(fs, i));
print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0,
first_block, last_block);
+ if (fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ printf(_(", csum 0x%08x"),
+ ext2fs_inode_bitmap_checksum(fs, i));
fputs(_("\n Inode table at "), stdout);
print_range(ext2fs_inode_table_loc(fs, i),
ext2fs_inode_table_loc(fs, i) +
This patch adds to tune2fs the ability to toggle the metadata checksum rocompat
feature flag, which will rewrite the inode table with checksums. Disallow
changing the UUID while the fs is mounted, because rewriting the metadata
objects is racy.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 106 insertions(+), 2 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 74a0489..cb1aff3 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -91,6 +91,7 @@ static char *extended_cmd;
static unsigned long new_inode_size;
static char *ext_mount_opts;
static int usrquota, grpquota;
+static int rewrite_checksums;
int journal_size, journal_flags;
char *journal_device;
@@ -142,7 +143,8 @@ static __u32 ok_features[3] = {
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
- EXT4_FEATURE_RO_COMPAT_QUOTA
+ EXT4_FEATURE_RO_COMPAT_QUOTA |
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
};
static __u32 clear_ok_features[3] = {
@@ -160,7 +162,8 @@ static __u32 clear_ok_features[3] = {
EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
- EXT4_FEATURE_RO_COMPAT_QUOTA
+ EXT4_FEATURE_RO_COMPAT_QUOTA |
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
};
/*
@@ -351,6 +354,16 @@ static int update_mntopts(ext2_filsys fs, char *mntopts)
return 0;
}
+static int check_fsck_needed(ext2_filsys fs)
+{
+ if (fs->super->s_state & EXT2_VALID_FS)
+ return 0;
+ printf("\n%s\n", _(please_fsck));
+ if (mount_flags & EXT2_MF_READONLY)
+ printf(_("(and reboot afterwards!)\n"));
+ return 1;
+}
+
static void request_fsck_afterwards(ext2_filsys fs)
{
static int requested = 0;
@@ -364,6 +377,60 @@ static void request_fsck_afterwards(ext2_filsys fs)
}
/*
+ * Forcibly set checksums in all inodes.
+ */
+static void rewrite_inodes(ext2_filsys fs)
+{
+ int length = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode *inode;
+ ext2_inode_scan scan;
+ errcode_t retval;
+ ext2_ino_t ino;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX)
+ return;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval) {
+ com_err("set_csum", retval, "while opening inode scan");
+ exit(1);
+ }
+
+ retval = ext2fs_get_mem(length, &inode);
+ if (retval) {
+ com_err("set_csum", retval, "while allocating memory");
+ exit(1);
+ }
+
+ do {
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
+ if (retval) {
+ com_err("set_csum", retval, "while getting next inode");
+ exit(1);
+ }
+ if (!ino)
+ break;
+
+ retval = ext2fs_write_inode_full(fs, ino, inode, length);
+ if (retval) {
+ com_err("set_csum", retval, "while writing inode");
+ exit(1);
+ }
+ } while (ino);
+
+ ext2fs_free_mem(&inode);
+ ext2fs_close_inode_scan(scan);
+}
+
+static void rewrite_metadata_checksums(ext2_filsys fs)
+{
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ ext2fs_init_csum_seed(fs);
+ rewrite_inodes(fs);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+}
+
+/*
* Update the feature set as provided by the user.
*/
static int update_feature_set(ext2_filsys fs, char *features)
@@ -536,6 +603,20 @@ mmp_error:
}
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (check_fsck_needed(fs))
+ exit(1);
+ rewrite_checksums = 1;
+ }
+
+ if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (check_fsck_needed(fs))
+ exit(1);
+ rewrite_checksums = 1;
+ }
+
+ if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
for (i = 0; i < fs->group_desc_count; i++) {
gd = ext2fs_group_desc(fs, fs->group_desc, i);
@@ -2128,6 +2209,23 @@ retry_open:
int set_csum = 0;
dgrp_t i;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ /*
+ * Changing the UUID requires rewriting all metadata,
+ * which can race with a mounted fs. Don't allow that.
+ */
+ if (mount_flags & EXT2_MF_MOUNTED) {
+ fputs(_("The UUID may only be "
+ "changed when the filesystem is "
+ "unmounted.\n"), stderr);
+ exit(1);
+ }
+
+ if (check_fsck_needed(fs))
+ exit(1);
+ }
+
if (sb->s_feature_ro_compat &
EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
/*
@@ -2153,13 +2251,19 @@ retry_open:
rc = 1;
goto closefs;
}
+ ext2fs_init_csum_seed(fs);
if (set_csum) {
for (i = 0; i < fs->group_desc_count; i++)
ext2fs_group_desc_csum_set(fs, i);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
}
ext2fs_mark_super_dirty(fs);
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ rewrite_checksums = 1;
}
+ if (rewrite_checksums)
+ rewrite_metadata_checksums(fs);
if (I_flag) {
if (mount_flags & EXT2_MF_MOUNTED) {
fputs(_("The inode size may only be "
Provide a field in the block group descriptor to store inode bitmap checksum,
and some helper functions to calculate and verify it.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/blknum.c | 15 +++++++++++++++
lib/ext2fs/closefs.c | 29 +++++++++++++++++------------
lib/ext2fs/csum.c | 40 ++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2_fs.h | 4 ++++
lib/ext2fs/ext2fs.h | 6 ++++++
lib/ext2fs/rw_bitmaps.c | 16 ++++++++++++++++
7 files changed, 101 insertions(+), 12 deletions(-)
diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index 33da7d6..212ad48 100644
--- a/lib/ext2fs/blknum.c
+++ b/lib/ext2fs/blknum.c
@@ -230,6 +230,21 @@ void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk)
}
/*
+ * Return the inode bitmap checksum of a group
+ */
+__u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group)
+{
+ struct ext4_group_desc *gdp;
+ __u32 csum;
+
+ gdp = ext4fs_group_desc(fs, fs->group_desc, group);
+ csum = gdp->bg_inode_bitmap_csum_lo;
+ if (fs->super->s_desc_size < EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ csum |= ((__u32)gdp->bg_inode_bitmap_csum_hi << 16);
+ return csum;
+}
+
+/*
* Return the inode bitmap block of a group
*/
blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group)
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 973c2a2..1867be3 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -288,6 +288,23 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
fs->super->s_wtime = fs->now ? fs->now : time(NULL);
fs->super->s_block_group_nr = 0;
+
+ /*
+ * If the write_bitmaps() function is present, call it to
+ * flush the bitmaps. This is done this way so that a simple
+ * program that doesn't mess with the bitmaps doesn't need to
+ * drag in the bitmaps.c code.
+ *
+ * Bitmap checksums live in the group descriptor, so the
+ * bitmaps need to be written before the descriptors.
+ */
+ if (fs->write_bitmaps) {
+ retval = fs->write_bitmaps(fs);
+ if (retval)
+ goto errout;
+ }
+
+ /* Prepare the group descriptors for writing */
#ifdef WORDS_BIGENDIAN
retval = EXT2_ET_NO_MEMORY;
retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
@@ -378,18 +395,6 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
ext2fs_numeric_progress_close(fs, &progress, NULL);
- /*
- * If the write_bitmaps() function is present, call it to
- * flush the bitmaps. This is done this way so that a simple
- * program that doesn't mess with the bitmaps doesn't need to
- * drag in the bitmaps.c code.
- */
- if (fs->write_bitmaps) {
- retval = fs->write_bitmaps(fs);
- if (retval)
- goto errout;
- }
-
write_primary_superblock_only:
/*
* Write out master superblock. This has to be done
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 5091a00..90ccd18 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,46 @@
#define STATIC static
#endif
+int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+ __u32 provided, calculated;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+ provided = gdp->bg_inode_bitmap_csum_lo;
+ calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
+ size);
+ if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ provided |= (__u32)gdp->bg_inode_bitmap_csum_hi << 16;
+ else
+ calculated &= 0xFFFF;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ __u32 crc;
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
+ gdp->bg_inode_bitmap_csum_lo = crc & 0xFFFF;
+ if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ gdp->bg_inode_bitmap_csum_hi = crc >> 16;
+
+ return 0;
+}
+
static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_inode_large *inode,
__u32 *crc, int has_hi)
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index d4a5b10..3f5d08f 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -446,4 +446,7 @@ ec EXT2_ET_MMP_OPEN_DIRECT,
ec EXT2_ET_INODE_CSUM_INVALID,
"Inode checksum does not match inode"
+ec EXT2_ET_INODE_BITMAP_CSUM_INVALID,
+ "Inode bitmap checksum does not match bitmap"
+
end
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index ed648a9..63f323f 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_END \
+ (offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
+ sizeof(__u16))
+
#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 858a338..c74e960 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -800,6 +800,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);
@@ -830,6 +831,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);
@@ -939,6 +941,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;
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 39e34b4..54894d5 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1689,6 +1689,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 4ce9f41..dbc256e 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1019,6 +1019,9 @@ struct problem_context {
/* Inode in use but group is marked INODE_UNINIT */
#define PR_5_INODE_UNINIT 0x050019
+/* Inode bitmap checksum does not match */
+#define PR_5_INODE_BITMAP_CSUM_INVALID 0x05001A
+
/*
* Post-Pass 5 errors
*/
Calculate and verify the block bitmap checksum.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/blknum.c | 15 +++++++++++++++
lib/ext2fs/csum.c | 40 ++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_fs.h | 3 +++
lib/ext2fs/ext2fs.h | 6 ++++++
lib/ext2fs/rw_bitmaps.c | 16 ++++++++++++++++
5 files changed, 80 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index 212ad48..0386c1f 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 90ccd18..5c5b7fb 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -70,6 +70,46 @@ errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
return 0;
}
+int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+ __u32 provided, calculated;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+ provided = gdp->bg_block_bitmap_csum_lo;
+ calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
+ size);
+ if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+ provided |= (__u32)gdp->bg_block_bitmap_csum_hi << 16;
+ else
+ calculated &= 0xFFFF;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ __u32 crc;
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
+ gdp->bg_block_bitmap_csum_lo = crc & 0xFFFF;
+ if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+ gdp->bg_block_bitmap_csum_hi = crc >> 16;
+
+ return 0;
+}
+
static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_inode_large *inode,
__u32 *crc, int has_hi)
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 63f323f..66f2b42 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_END \
(offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
sizeof(__u16))
+#define EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION \
+ (offsetof(struct ext4_group_desc, bg_block_bitmap_csum_hi) + \
+ sizeof(__u16))
#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index c74e960..921ecb2 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -801,6 +801,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);
@@ -828,6 +829,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);
@@ -941,6 +943,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;
Display the block bitmap checksum when displaying block groups.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/dumpe2fs.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index ec4f0e9..23dec30 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -222,6 +222,10 @@ static void list_desc (ext2_filsys fs)
print_number(ext2fs_block_bitmap_loc(fs, i));
print_bg_rel_offset(fs, ext2fs_block_bitmap_loc(fs, i), 0,
first_block, last_block);
+ if (fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ printf(_(", csum 0x%08x"),
+ ext2fs_block_bitmap_checksum(fs, i));
fputs(_(", Inode bitmap at "), stdout);
print_number(ext2fs_inode_bitmap_loc(fs, i));
print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0,
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 54894d5..95813ce 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1694,6 +1694,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 dbc256e..51a911b 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1022,6 +1022,9 @@ struct problem_context {
/* Inode bitmap checksum does not match */
#define PR_5_INODE_BITMAP_CSUM_INVALID 0x05001A
+/* Block bitmap checksum does not match */
+#define PR_5_BLOCK_BITMAP_CSUM_INVALID 0x05001B
+
/*
* Post-Pass 5 errors
*/
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 478b651..a5ff5ce 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -429,6 +429,7 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
rewrite_inodes(fs);
ext2fs_read_bitmaps(fs);
ext2fs_mark_ib_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
}
When we encounter an extent tree block that passes the header check but fails
the checksum, offer to clear just that extent block instead of failing the
whole tree, which results in the entire inode being wiped out.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 34 ++++++++++++++++++++++++++++++++--
e2fsck/problem.c | 15 +++++++++++++++
e2fsck/problem.h | 6 ++++++
3 files changed, 53 insertions(+), 2 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index c382a7b..1ae117d 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1805,6 +1805,7 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
int is_dir, is_leaf;
errcode_t problem;
struct ext2_extent_info info;
+ int failed_csum;
pctx->errcode = ext2fs_extent_get_info(ehandle, &info);
if (pctx->errcode)
@@ -1812,11 +1813,25 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_FIRST_SIB,
&extent);
- while (!pctx->errcode && info.num_entries-- > 0) {
+ while ((pctx->errcode == 0 ||
+ pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) &&
+ info.num_entries-- > 0) {
+ failed_csum = 0;
is_leaf = extent.e_flags & EXT2_EXTENT_FLAGS_LEAF;
is_dir = LINUX_S_ISDIR(pctx->inode->i_mode);
problem = 0;
+ /* Ask to clear a corrupt extent block */
+ if (pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) {
+ pctx->blk = extent.e_pblk;
+ pctx->blk2 = extent.e_lblk;
+ pctx->num = extent.e_len;
+ problem = PR_1_EXTENT_CSUM_INVALID;
+ if (fix_problem(ctx, problem, pctx))
+ goto fix_problem_now;
+ failed_csum = 1;
+ }
+
if (extent.e_pblk == 0 ||
extent.e_pblk < ctx->fs->super->s_first_data_block ||
extent.e_pblk >= ext2fs_blocks_count(ctx->fs->super))
@@ -1828,12 +1843,24 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
ext2fs_blocks_count(ctx->fs->super))
problem = PR_1_EXTENT_ENDS_BEYOND;
+ /* Corrupt but passes checks? Ask to fix checksum. */
+ if (failed_csum) {
+ pctx->blk = extent.e_pblk;
+ pctx->blk2 = extent.e_lblk;
+ pctx->num = extent.e_len;
+ problem = 0;
+ if (fix_problem(ctx, PR_1_EXTENT_ONLY_CSUM_INVALID,
+ pctx))
+ ext2fs_extent_replace(ehandle, 0, &extent);
+ }
+
if (problem) {
report_problem:
pctx->blk = extent.e_pblk;
pctx->blk2 = extent.e_lblk;
pctx->num = extent.e_len;
if (fix_problem(ctx, problem, pctx)) {
+fix_problem_now:
e2fsck_read_bitmaps(ctx);
pctx->errcode =
ext2fs_extent_delete(ehandle, 0);
@@ -1860,7 +1887,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 95813ce..cd404f8 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -951,6 +951,21 @@ static struct e2fsck_problem problem_table[] = {
N_("The bad @b @i looks @n. "),
PROMPT_CLEAR, 0 },
+ /* Inode extent block checksum does not match extent */
+ { PR_1_EXTENT_CSUM_INVALID,
+ N_("@i %i extent block checksum does not match extent\n\t(logical @b "
+ "%c, @n physical @b %b, len %N)\n"),
+ PROMPT_CLEAR, 0 },
+
+ /*
+ * Inode extent block passes checks, but checksum does not match
+ * extent
+ */
+ { PR_1_EXTENT_ONLY_CSUM_INVALID,
+ N_("@i %i extent block passes checks, but checksum does not match "
+ "extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"),
+ PROMPT_FIX, 0 },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 51a911b..cd9e4ed 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -556,6 +556,12 @@ struct problem_context {
/* inode passes checks, but checksum does not match inode */
#define PR_1_INODE_ONLY_CSUM_INVALID 0x010067
+/* extent block checksum does not match extent block */
+#define PR_1_EXTENT_CSUM_INVALID 0x010068
+
+/* extent block passes checks, but checksum does not match extent block */
+#define PR_1_EXTENT_ONLY_CSUM_INVALID 0x010069
+
/*
* Pass 1b errors
*/
Verify and calculate checksums of htree internal node blocks.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 149 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +
lib/ext2fs/ext2fs.h | 4 +
3 files changed, 156 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 8a0543c..8907459 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,155 @@
#define STATIC static
#endif
+static __u16 do_nothing16(__u16 x)
+{
+ return x;
+}
+
+static __u16 disk_to_host16(__u16 x)
+{
+ return ext2fs_le16_to_cpu(x);
+}
+
+static errcode_t __get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dx_countlimit **cc,
+ int *offset,
+ int need_swab)
+{
+ struct ext2_dir_entry *dp;
+ struct ext2_dx_root_info *root;
+ struct ext2_dx_countlimit *c;
+ int count_offset, max_sane_entries;
+ unsigned int rec_len;
+ __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+ rec_len = translate(dirent->rec_len);
+
+ if (rec_len == fs->blocksize && translate(dirent->name_len) == 0)
+ count_offset = 8;
+ else if (rec_len == 12) {
+ dp = (struct ext2_dir_entry *)(((void *)dirent) + rec_len);
+ rec_len = translate(dp->rec_len);
+ if (rec_len != fs->blocksize - 12)
+ return EXT2_ET_DB_NOT_FOUND;
+ root = (struct ext2_dx_root_info *)(((void *)dp + 12));
+ if (root->reserved_zero ||
+ root->info_length != sizeof(struct ext2_dx_root_info))
+ return EXT2_ET_DB_NOT_FOUND;
+ count_offset = 32;
+ } else
+ return EXT2_ET_DB_NOT_FOUND;
+
+ c = (struct ext2_dx_countlimit *)(((void *)dirent) + count_offset);
+ max_sane_entries = (fs->blocksize - count_offset) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(c->limit) > max_sane_entries ||
+ ext2fs_le16_to_cpu(c->count) > max_sane_entries)
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+ if (offset)
+ *offset = count_offset;
+ if (cc)
+ *cc = c;
+
+ return 0;
+}
+
+errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dx_countlimit **cc,
+ int *offset)
+{
+ return __get_dx_countlimit(fs, dirent, cc, offset, 0);
+}
+
+static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent,
+ __u32 *crc, int count_offset, int count,
+ struct ext2_dx_tail *t)
+{
+ errcode_t retval;
+ char *buf = (char *)dirent;
+ int size;
+ __u32 old_csum, gen;
+ struct ext2_inode inode;
+
+ size = count_offset + (count * sizeof(struct ext2_dx_entry));
+ old_csum = t->dt_checksum;
+ t->dt_checksum = 0;
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size);
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)t,
+ sizeof(struct ext2_dx_tail));
+ t->dt_checksum = old_csum;
+
+ return 0;
+}
+
+static int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ __u32 calculated;
+ errcode_t retval;
+ struct ext2_dx_countlimit *c;
+ struct ext2_dx_tail *t;
+ int count_offset, limit, count;
+
+ retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1);
+ if (retval)
+ return 1;
+ limit = ext2fs_le16_to_cpu(c->limit);
+ count = ext2fs_le16_to_cpu(c->count);
+ if (count_offset + (limit * sizeof(struct ext2_dx_entry)) >
+ fs->blocksize - sizeof(struct ext2_dx_tail))
+ return 0;
+ /* htree structs are accessed in LE order */
+ t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit);
+ retval = ext2fs_dx_csum(fs, inum, dirent, &calculated, count_offset,
+ count, t);
+ if (retval)
+ return 0;
+
+ return ext2fs_le32_to_cpu(t->dt_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ __u32 crc;
+ errcode_t retval = 0;
+ struct ext2_dx_countlimit *c;
+ struct ext2_dx_tail *t;
+ int count_offset, limit, count;
+
+ retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1);
+ if (retval)
+ return retval;
+ limit = ext2fs_le16_to_cpu(c->limit);
+ count = ext2fs_le16_to_cpu(c->count);
+ if (count_offset + (limit * sizeof(struct ext2_dx_entry)) >
+ fs->blocksize - sizeof(struct ext2_dx_tail))
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+ t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit);
+
+ /* htree structs are accessed in LE order */
+ retval = ext2fs_dx_csum(fs, inum, dirent, &crc, count_offset, count, t);
+ if (retval)
+ return retval;
+ t->dt_checksum = ext2fs_cpu_to_le32(crc);
+ return retval;
+}
+
#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
(sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 9019026..1e51384 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -452,4 +452,7 @@ ec EXT2_ET_INODE_BITMAP_CSUM_INVALID,
ec EXT2_ET_EXTENT_CSUM_INVALID,
"Extent block checksum does not match extent block"
+ec EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
+ "Directory block does not have space for checksum"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 5fa809a..5943ecc 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -943,6 +943,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/e2fsck.h | 2 ++
e2fsck/pass2.c | 34 +++++++++++++++++++++++++++++-----
e2fsck/problem.c | 10 ++++++++++
e2fsck/problem.h | 6 ++++++
e2fsck/rehash.c | 41 +++++++++++++++++++++++++++++++++++++++--
5 files changed, 86 insertions(+), 7 deletions(-)
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index d225d89..39ef6e4 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -483,6 +483,8 @@ extern void region_free(region_t region);
extern int region_allocate(region_t region, region_addr_t start, int n);
/* rehash.c */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino);
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino);
errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino);
void e2fsck_rehash_directories(e2fsck_t ctx);
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 103b155..23a847e 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,22 @@ static void parse_int_node(ext2_filsys fs,
#endif
ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+
+ if (failed_csum &&
+ (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
+ fix_problem(cd->ctx, PR_2_HTREE_ROOT_CSUM_INVALID,
+ &cd->pctx)))
+ goto clear_and_exit;
} else {
ent = (struct ext2_dx_entry *) (block_buf+8);
+
+ if (failed_csum &&
+ (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
+ fix_problem(cd->ctx, PR_2_HTREE_NODE_CSUM_INVALID,
+ &cd->pctx)))
+ goto clear_and_exit;
}
+
limit = (struct ext2_dx_countlimit *) ent;
#ifdef DX_DEBUG
@@ -560,8 +574,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))
@@ -627,6 +645,7 @@ static void parse_int_node(ext2_filsys fs,
clear_and_exit:
clear_htree(cd->ctx, cd->pctx.ino);
dx_dir->numblocks = 0;
+ e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino);
}
#endif /* ENABLE_HTREE */
@@ -729,6 +748,7 @@ static int check_dir_block(ext2_filsys fs,
struct problem_context pctx;
int dups_found = 0;
int ret;
+ int dx_csum_size = 0;
cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -740,6 +760,10 @@ static int check_dir_block(ext2_filsys fs,
if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
return DIRENT_ABORT;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ dx_csum_size = sizeof(struct ext2_dx_tail);
+
/*
* Make sure the inode is still in use (could have been
* deleted in the duplicate/bad blocks pass.
@@ -829,7 +853,7 @@ static int check_dir_block(ext2_filsys fs,
(rec_len == fs->blocksize) &&
(dirent->name_len == 0) &&
(ext2fs_le16_to_cpu(limit->limit) ==
- ((fs->blocksize-8) /
+ ((fs->blocksize - (8 + dx_csum_size)) /
sizeof(struct ext2_dx_entry))))
dx_db->type = DX_DIRBLOCK_NODE;
}
@@ -1116,7 +1140,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 cd404f8..894b2f8 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1388,6 +1388,16 @@ static struct e2fsck_problem problem_table[] = {
N_("i_file_acl_hi @F %N, @s zero.\n"),
PROMPT_CLEAR, PR_PREEN_OK },
+ /* htree root node fails checksum */
+ { PR_2_HTREE_ROOT_CSUM_INVALID,
+ N_("@p @h %d: root node fails checksum\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* htree internal node fails checksum */
+ { PR_2_HTREE_NODE_CSUM_INVALID,
+ N_("@p @h %d: internal node fails checksum\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
/* Pass 3 errors */
/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index cd9e4ed..3185da9 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -827,6 +827,12 @@ struct problem_context {
/* i_file_acl_hi should be zero */
#define PR_2_I_FILE_ACL_HI_ZERO 0x020048
+/* htree root node fails checksum */
+#define PR_2_HTREE_ROOT_CSUM_INVALID 0x020049
+
+/* htree node fails checksum */
+#define PR_2_HTREE_NODE_CSUM_INVALID 0x02004A
+
/*
* Pass 3 errors
*/
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 15993b3..610b3bf 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -52,6 +52,25 @@
#include "e2fsck.h"
#include "problem.h"
+/* Schedule a dir to be rebuilt during pass 3A. */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino)
+{
+ if (!ctx->dirs_to_hash)
+ ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
+ if (ctx->dirs_to_hash)
+ ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+}
+
+/* Ask if a dir will be rebuilt during pass 3A. */
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino)
+{
+ if (ctx->options & E2F_OPT_COMPRESS_DIRS)
+ return 1;
+ if (!ctx->dirs_to_hash)
+ return 0;
+ return ext2fs_u32_list_test(ctx->dirs_to_hash, ino);
+}
+
struct fill_dir_struct {
char *buf;
struct ext2_inode *inode;
@@ -495,6 +514,7 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
struct ext2_dx_root_info *root;
struct ext2_dx_countlimit *limits;
int filetype = 0;
+ int csum_size = 0;
if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
filetype = EXT2_FT_DIR << 8;
@@ -519,8 +539,13 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
root->indirect_levels = 0;
root->unused_flags = 0;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+
limits = (struct ext2_dx_countlimit *) (buf+32);
- limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
+ limits->limit = (fs->blocksize - (32 + csum_size)) /
+ sizeof(struct ext2_dx_entry);
limits->count = 0;
return root;
@@ -531,14 +556,20 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
{
struct ext2_dir_entry *dir;
struct ext2_dx_countlimit *limits;
+ int csum_size = 0;
memset(buf, 0, fs->blocksize);
dir = (struct ext2_dir_entry *) buf;
dir->inode = 0;
(void) ext2fs_set_rec_len(fs, fs->blocksize, dir);
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+
limits = (struct ext2_dx_countlimit *) (buf+8);
- limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
+ limits->limit = (fs->blocksize - (8 + csum_size)) /
+ sizeof(struct ext2_dx_entry);
limits->count = 0;
return (struct ext2_dx_entry *) limits;
@@ -865,8 +896,14 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
if (!ext2fs_u32_list_iterate(iter, &ino))
break;
}
+#if 0
+ /*
+ * lost+found must not be excluded from cleanups or else
+ * checksum errors won't get fixed.
+ */
if (ino == ctx->lost_and_found)
continue;
+#endif
pctx.dir = ino;
if (first) {
fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
Since all the metadata checksums depend on the fs UUID, tune2fs must be able to
rewrite the checksums of _all_ metadata. It's not that hard to add in the bits
to resize the directory block structures at the same time.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 194 insertions(+), 0 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 784df9a..801e6ea 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -106,6 +106,8 @@ struct blk_move {
static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
+static const char *please_dir_fsck =
+ N_("Please run e2fsck -D on the filesystem.\n");
#ifdef CONFIG_BUILD_FINDFS
void do_findfs(int argc, char **argv);
@@ -364,6 +366,18 @@ static int check_fsck_needed(ext2_filsys fs)
return 1;
}
+static void request_dir_fsck_afterwards(ext2_filsys fs)
+{
+ static int requested;
+
+ if (requested++)
+ return;
+ fs->super->s_state &= ~EXT2_VALID_FS;
+ printf("\n%s\n", _(please_dir_fsck));
+ if (mount_flags & EXT2_MF_READONLY)
+ printf(_("(and reboot afterwards!)\n"));
+}
+
static void request_fsck_afterwards(ext2_filsys fs)
{
static int requested = 0;
@@ -419,6 +433,177 @@ static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino,
}
/*
+ * Rewrite directory blocks with checksums
+ */
+struct rewrite_dir_context {
+ char *buf;
+ errcode_t errcode;
+ ext2_ino_t dir;
+ int is_htree;
+};
+
+static int rewrite_dir_block(ext2_filsys fs,
+ blk64_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk64_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct ext2_dx_countlimit *dcl = NULL;
+ struct rewrite_dir_context *ctx = priv_data;
+ int dcl_offset, changed = 0;
+
+ ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
+ ctx->dir);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+
+ /* if htree node... */
+ if (ctx->is_htree)
+ ext2fs_get_dx_countlimit(fs, (struct ext2_dir_entry *)ctx->buf,
+ &dcl, &dcl_offset);
+ if (dcl) {
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ /* Ensure limit is the max size */
+ int max_entries = (fs->blocksize - dcl_offset) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(dcl->limit) != max_entries) {
+ changed = 1;
+ dcl->limit = ext2fs_cpu_to_le16(max_entries);
+ }
+ } else {
+ /* If htree block is full then rebuild the dir */
+ if (ext2fs_le16_to_cpu(dcl->count) ==
+ ext2fs_le16_to_cpu(dcl->limit)) {
+ request_dir_fsck_afterwards(fs);
+ return 0;
+ }
+ /*
+ * Ensure dcl->limit is small enough to leave room for
+ * the checksum tail.
+ */
+ int max_entries = (fs->blocksize - (dcl_offset +
+ sizeof(struct ext2_dx_tail))) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(dcl->limit) != max_entries)
+ dcl->limit = ext2fs_cpu_to_le16(max_entries);
+ /* Always rewrite checksum */
+ changed = 1;
+ }
+ } else {
+ unsigned int rec_len, name_size;
+ char *top = ctx->buf + fs->blocksize;
+ struct ext2_dir_entry *de = (struct ext2_dir_entry *)ctx->buf;
+ struct ext2_dir_entry *last_de = NULL, *penultimate_de = NULL;
+
+ /* Find last and penultimate dirent */
+ while ((char *)de < top) {
+ penultimate_de = last_de;
+ last_de = de;
+ ctx->errcode = ext2fs_get_rec_len(fs, de, &rec_len);
+ if (!ctx->errcode && !rec_len)
+ ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ de = (struct ext2_dir_entry *)(((void *)de) + rec_len);
+ }
+ ctx->errcode = ext2fs_get_rec_len(fs, last_de, &rec_len);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ name_size = last_de->name_len & 0xFF;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (!penultimate_de)
+ return 0;
+ if (last_de->inode ||
+ name_size ||
+ rec_len != sizeof(struct ext2_dir_entry_tail))
+ return 0;
+ /*
+ * The last dirent is unused and the right length to
+ * have stored a checksum. Erase it.
+ */
+ ctx->errcode = ext2fs_get_rec_len(fs, penultimate_de,
+ &rec_len);
+ if (!rec_len)
+ ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ ext2fs_set_rec_len(fs, rec_len +
+ sizeof(struct ext2_dir_entry_tail),
+ penultimate_de);
+ changed = 1;
+ } else {
+ int csum_size = sizeof(struct ext2_dir_entry_tail);
+ struct ext2_dir_entry_tail *t;
+
+ /*
+ * If the last dirent looks like the tail, just update
+ * the checksum.
+ */
+ if (!last_de->inode &&
+ rec_len == csum_size) {
+ t = (struct ext2_dir_entry_tail *)last_de;
+ t->det_reserved_name_len =
+ EXT2_DIR_NAME_LEN_CSUM;
+ changed = 1;
+ goto out;
+ }
+ if (name_size & 3)
+ name_size = (name_size & ~3) + 4;
+ /* If there's not enough space for the tail, e2fsck */
+ if (rec_len <= (8 + name_size + csum_size)) {
+ request_dir_fsck_afterwards(fs);
+ return 0;
+ }
+ /* Shorten that last de and insert the tail */
+ ext2fs_set_rec_len(fs, rec_len - csum_size, last_de);
+ t = EXT2_DIRENT_TAIL(ctx->buf, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+
+ /* Always update checksum */
+ changed = 1;
+ }
+ }
+
+out:
+ if (!changed)
+ return 0;
+
+ ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
+ 0, ctx->dir);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+
+ return 0;
+}
+
+errcode_t rewrite_directory(ext2_filsys fs, ext2_ino_t dir,
+ struct ext2_inode *inode)
+{
+ errcode_t retval;
+ struct rewrite_dir_context ctx;
+
+ retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
+ if (retval)
+ return retval;
+
+ ctx.is_htree = (inode->i_flags & EXT2_INDEX_FL);
+ ctx.dir = dir;
+ retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY |
+ BLOCK_FLAG_DATA_ONLY,
+ 0, rewrite_dir_block, &ctx);
+
+ ext2fs_free_mem(&ctx.buf);
+ if (retval)
+ return retval;
+
+ return ctx.errcode;
+}
+
+/*
* Forcibly set checksums in all inodes.
*/
static void rewrite_inodes(ext2_filsys fs)
@@ -465,6 +650,15 @@ static void rewrite_inodes(ext2_filsys fs)
"while rewriting extents");
exit(1);
}
+
+ if (LINUX_S_ISDIR(inode->i_mode)) {
+ retval = rewrite_directory(fs, ino, inode);
+ if (retval) {
+ com_err("rewrite_directory", retval,
+ "while rewriting directories");
+ exit(1);
+ }
+ }
} while (ino);
ext2fs_free_mem(&inode);
Verify and calculate extent tree block checksums when processing filesystems.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 79 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2fs.h | 6 +++
lib/ext2fs/extent.c | 21 ++++++++++++
4 files changed, 109 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 5c5b7fb..8a0543c 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,85 @@
#define STATIC static
#endif
+#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
+ (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
+
+static struct ext3_extent_tail *get_extent_tail(struct ext3_extent_header *h)
+{
+ return (struct ext3_extent_tail *)(((void *)h) +
+ EXT3_EXTENT_TAIL_OFFSET(h));
+}
+
+static errcode_t ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh,
+ __u32 *crc)
+{
+ int size;
+ __u32 gen;
+ errcode_t retval;
+ struct ext2_inode inode;
+
+ size = EXT3_EXTENT_TAIL_OFFSET(eh) + offsetof(struct ext3_extent_tail,
+ et_checksum);
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)eh, size);
+
+ return 0;
+}
+
+int ext2fs_extent_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh)
+{
+ errcode_t retval;
+ __u32 provided, calculated;
+ struct ext3_extent_tail *t = get_extent_tail(eh);
+
+ /*
+ * The extent tree structures are accessed in LE order, so we must
+ * swap the checksum bytes here.
+ */
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ provided = ext2fs_le32_to_cpu(t->et_checksum);
+ retval = ext2fs_extent_block_csum(fs, inum, eh, &calculated);
+ if (retval)
+ return 0;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh)
+{
+ errcode_t retval;
+ __u32 crc;
+ struct ext3_extent_tail *t = get_extent_tail(eh);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ /*
+ * The extent tree structures are accessed in LE order, so we must
+ * swap the checksum bytes here.
+ */
+ retval = ext2fs_extent_block_csum(fs, inum, eh, &crc);
+ if (retval)
+ return retval;
+ t->et_checksum = ext2fs_cpu_to_le32(crc);
+ return retval;
+}
+
int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
char *bitmap, int size)
{
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 3f5d08f..9019026 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -449,4 +449,7 @@ ec EXT2_ET_INODE_CSUM_INVALID,
ec EXT2_ET_INODE_BITMAP_CSUM_INVALID,
"Inode bitmap checksum does not match bitmap"
+ec EXT2_ET_EXTENT_CSUM_INVALID,
+ "Extent block checksum does not match extent block"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 921ecb2..5fa809a 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -943,6 +943,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);
Add to tune2fs the ability to recalculate extent tree checksums when altering
the metadata checksum feature flag.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 49 insertions(+), 0 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index a5ff5ce..784df9a 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -376,6 +376,48 @@ static void request_fsck_afterwards(ext2_filsys fs)
printf(_("(and reboot afterwards!)\n"));
}
+/* Rewrite extents */
+static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ ext2_extent_handle_t handle;
+ struct ext2fs_extent extent;
+ int op = EXT2_EXTENT_ROOT;
+ errcode_t errcode;
+
+ if (!(inode->i_flags & EXT4_EXTENTS_FL))
+ return 0;
+
+ errcode = ext2fs_extent_open(fs, ino, &handle);
+ if (errcode)
+ return errcode;
+
+ while (1) {
+ errcode = ext2fs_extent_get(handle, op, &extent);
+ if (errcode)
+ break;
+
+ /* Root node is in the separately checksummed inode */
+ if (op == EXT2_EXTENT_ROOT) {
+ op = EXT2_EXTENT_NEXT;
+ continue;
+ }
+ op = EXT2_EXTENT_NEXT;
+
+ /* Only visit the first extent in each extent block */
+ if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
+ continue;
+ errcode = ext2fs_extent_replace(handle, 0, &extent);
+ if (errcode)
+ break;
+ }
+
+ /* Ok if we run off the end */
+ if (errcode == EXT2_ET_EXTENT_NO_NEXT)
+ errcode = 0;
+ return errcode;
+}
+
/*
* Forcibly set checksums in all inodes.
*/
@@ -416,6 +458,13 @@ static void rewrite_inodes(ext2_filsys fs)
com_err("set_csum", retval, "while writing inode");
exit(1);
}
+
+ retval = rewrite_extents(fs, ino, inode);
+ if (retval) {
+ com_err("rewrite_extents", retval,
+ "while rewriting extents");
+ exit(1);
+ }
} while (ino);
ext2fs_free_mem(&inode);
Calculate and verify the checksum for separate (i.e. not in the inode) extended
attribute blocks; the checksum lives in the header.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 10 ++++---
e2fsck/pass1b.c | 4 +--
e2fsck/pass2.c | 6 ++--
e2fsck/super.c | 6 ++--
lib/ext2fs/csum.c | 68 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2fs.h | 14 +++++++++
lib/ext2fs/ext_attr.c | 59 ++++++++++++++++++++++++++++++---------
lib/ext2fs/swapfs.c | 3 +-
9 files changed, 146 insertions(+), 27 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 942c82f..2fb5560 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1501,7 +1501,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;
@@ -1512,8 +1513,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);
@@ -1610,7 +1612,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 108bdb5..da1a2db 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -1285,9 +1285,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 99c0b84..0cf2bee 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -198,9 +198,9 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
ext2fs_iblk_sub_blocks(fs, inode, pb.truncated_blocks);
if (ext2fs_file_acl_block(fs, inode)) {
- retval = ext2fs_adjust_ea_refcount2(fs,
- ext2fs_file_acl_block(fs, inode),
- block_buf, -1, &count);
+ retval = ext2fs_adjust_ea_refcount3(fs,
+ ext2fs_file_acl_block(fs, inode),
+ block_buf, -1, &count, ino);
if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
retval = 0;
count = 1;
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 1c06f5e..46f0cb5 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,74 @@
#define STATIC static
#endif
+static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr,
+ __u32 *crc)
+{
+ errcode_t retval;
+ char *buf = (char *)hdr;
+ __u32 gen, old_crc = hdr->h_checksum;
+ struct ext2_inode inode;
+
+ hdr->h_checksum = 0;
+ if (ext2fs_le32_to_cpu(hdr->h_refcount) != 1) {
+ block = ext2fs_cpu_to_le64(block);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&block,
+ sizeof(block));
+ } else {
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen,
+ sizeof(gen));
+ }
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, fs->blocksize);
+ hdr->h_checksum = old_crc;
+
+ return 0;
+}
+
+int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr)
+{
+ __u32 calculated;
+ errcode_t retval;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &calculated);
+ if (retval)
+ return 0;
+
+ return ext2fs_le32_to_cpu(hdr->h_checksum) == calculated;
+}
+
+errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr)
+{
+ errcode_t retval;
+ __u32 crc;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &crc);
+ if (retval)
+ return retval;
+ hdr->h_checksum = ext2fs_cpu_to_le32(crc);
+ return 0;
+}
+
static __u16 do_nothing16(__u16 x)
{
return x;
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index ad0cb7a..177a97f 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -458,4 +458,7 @@ ec EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
ec EXT2_ET_DIR_CSUM_INVALID,
"Directory block checksum does not match directory block"
+ec EXT2_ET_EXT_ATTR_CSUM_INVALID,
+ "Extended attribute block checksum does not match block"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 3ebfd6b..dba187a 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -943,6 +943,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)))
@@ -1098,16 +1104,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]);
}
Print htree node checksums when dumping a directory index.
Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/htree.c | 17 ++++++++++++++++-
1 files changed, 16 insertions(+), 1 deletions(-)
diff --git a/debugfs/htree.c b/debugfs/htree.c
index 05745eb..2fb804e 100644
--- a/debugfs/htree.c
+++ b/debugfs/htree.c
@@ -120,8 +120,9 @@ static void htree_dump_int_node(ext2_filsys fs, ext2_ino_t ino,
{
struct ext2_dx_countlimit limit;
struct ext2_dx_entry e;
+ struct ext2_dx_tail *tail;
int hash, i;
-
+ int remainder;
limit = *((struct ext2_dx_countlimit *) ent);
limit.count = ext2fs_le16_to_cpu(limit.count);
@@ -130,6 +131,20 @@ static void htree_dump_int_node(ext2_filsys fs, ext2_ino_t ino,
fprintf(pager, "Number of entries (count): %d\n", limit.count);
fprintf(pager, "Number of entries (limit): %d\n", limit.limit);
+ remainder = fs->blocksize - (limit.limit *
+ sizeof(struct ext2_dx_entry));
+ if (ent == (struct ext2_dx_entry *)(rootnode + 1))
+ remainder -= sizeof(struct ext2_dx_root_info) + 24;
+ else
+ remainder -= 8;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ remainder == sizeof(struct ext2_dx_tail)) {
+ tail = (struct ext2_dx_tail *)(ent + limit.limit);
+ fprintf(pager, "Checksum: 0x%08x\n",
+ ext2fs_le32_to_cpu(tail->dt_checksum));
+ }
+
for (i=0; i < limit.count; i++) {
hash = i ? ext2fs_le32_to_cpu(ent[i].hash) : 0;
fprintf(pager, "Entry #%d: Hash 0x%08x%s, block %u\n", i,
Introduce small structures for recording directory tree checksums, and some API
changes to support writing out directory blocks with checksums.
Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/htree.c | 9 ++-
e2fsck/pass1.c | 2 -
e2fsck/pass2.c | 14 +++-
e2fsck/pass3.c | 10 ++-
e2fsck/rehash.c | 37 +++++++++--
lib/ext2fs/csum.c | 148 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/dir_iterate.c | 14 +++-
lib/ext2fs/dirblock.c | 98 +++++++++++++-----------------
lib/ext2fs/expanddir.c | 5 +-
lib/ext2fs/ext2_err.et.in | 3 +
lib/ext2fs/ext2fs.h | 18 +++++
lib/ext2fs/link.c | 6 ++
lib/ext2fs/mkdir.c | 2 -
lib/ext2fs/newdir.c | 15 ++++-
lib/ext2fs/swapfs.c | 62 +++++++++++++++++++
15 files changed, 361 insertions(+), 82 deletions(-)
diff --git a/debugfs/htree.c b/debugfs/htree.c
index 2fb804e..1932962 100644
--- a/debugfs/htree.c
+++ b/debugfs/htree.c
@@ -44,6 +44,11 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
ext2_dirhash_t hash, minor_hash;
unsigned int rec_len;
int hash_alg;
+ int csum_size = 0;
+
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
errcode = ext2fs_bmap2(fs, ino, inode, buf, 0, blk, 0, &pblk);
if (errcode) {
@@ -53,7 +58,7 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
}
printf("Reading directory block %llu, phys %llu\n", blk, pblk);
- errcode = ext2fs_read_dir_block2(current_fs, pblk, buf, 0);
+ errcode = ext2fs_read_dir_block4(current_fs, pblk, buf, 0, ino);
if (errcode) {
com_err("htree_dump_leaf_node", errcode,
"while reading block %llu (%llu)\n",
@@ -65,7 +70,7 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
(fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
hash_alg += 3;
- while (offset < fs->blocksize) {
+ while (offset < (fs->blocksize - csum_size)) {
dirent = (struct ext2_dir_entry *) (buf + offset);
errcode = ext2fs_get_rec_len(fs, dirent, &rec_len);
if (errcode) {
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 1ae117d..942c82f 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 23a847e..3a0d050 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -749,6 +749,7 @@ static int check_dir_block(ext2_filsys fs,
int dups_found = 0;
int ret;
int dx_csum_size = 0;
+ int failed_csum = 0;
cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -799,10 +800,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;
@@ -1140,7 +1145,7 @@ out_htree:
cd->pctx.dir = cd->pctx.ino;
if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
(dx_db->type == DX_DIRBLOCK_NODE))
- parse_int_node(fs, db, cd, dx_dir, buf, 0);
+ parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
}
#endif /* ENABLE_HTREE */
if (offset != fs->blocksize) {
@@ -1151,7 +1156,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))
@@ -1471,7 +1477,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 610b3bf..a77ef03 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -81,6 +81,7 @@ struct fill_dir_struct {
int dir_size;
int compress;
ino_t parent;
+ ext2_ino_t dir;
};
struct hash_entry {
@@ -125,7 +126,10 @@ static int fill_dir_block(ext2_filsys fs,
dirent = (struct ext2_dir_entry *) dir;
(void) ext2fs_set_rec_len(fs, fs->blocksize, dirent);
} else {
- fd->err = ext2fs_read_dir_block3(fs, *block_nr, dir, 0);
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0,
+ fd->dir);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (fd->err)
return BLOCK_ABORT;
}
@@ -416,7 +420,8 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
static errcode_t copy_dir_entries(e2fsck_t ctx,
struct fill_dir_struct *fd,
- struct out_dir *outdir)
+ struct out_dir *outdir,
+ ext2_ino_t ino)
{
ext2_filsys fs = ctx->fs;
errcode_t retval;
@@ -427,6 +432,8 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
int i, left;
ext2_dirhash_t prev_hash;
int offset, slack;
+ int csum_size = 0;
+ struct ext2_dir_entry_tail *t;
if (ctx->htree_slack_percentage == 255) {
profile_get_uint(ctx->profile, "options",
@@ -437,6 +444,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
ctx->htree_slack_percentage = 20;
}
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
outdir->max = 0;
retval = alloc_size_dir(fs, outdir,
(fd->dir_size / fs->blocksize) + 2);
@@ -451,9 +462,9 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
dirent = (struct ext2_dir_entry *) block_start;
prev_rec_len = 0;
rec_len = 0;
- left = fs->blocksize;
+ left = fs->blocksize - csum_size;
slack = fd->compress ? 12 :
- (fs->blocksize * ctx->htree_slack_percentage)/100;
+ ((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100;
if (slack < 12)
slack = 12;
for (i = 0; i < fd->num_array; i++) {
@@ -468,12 +479,17 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
if (retval)
return retval;
}
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(block_start,
+ fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
if ((retval = get_next_block(fs, outdir,
&block_start)))
return retval;
offset = 0;
}
- left = fs->blocksize - offset;
+ left = (fs->blocksize - csum_size) - offset;
dirent = (struct ext2_dir_entry *) (block_start + offset);
if (offset == 0) {
if (ent->hash == prev_hash)
@@ -502,6 +518,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
}
if (left)
retval = ext2fs_set_rec_len(fs, rec_len + left, dirent);
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(block_start, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
return retval;
}
@@ -659,6 +679,7 @@ struct write_dir_struct {
errcode_t err;
e2fsck_t ctx;
int cleared;
+ ext2_ino_t dir;
};
/*
@@ -690,7 +711,7 @@ static int write_dir_block(ext2_filsys fs,
return 0;
dir = wd->outdir->buf + (blockcnt * fs->blocksize);
- wd->err = ext2fs_write_dir_block3(fs, *block_nr, dir, 0);
+ wd->err = ext2fs_write_dir_block4(fs, *block_nr, dir, 0, wd->dir);
if (wd->err)
return BLOCK_ABORT;
return 0;
@@ -712,6 +733,7 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
wd.err = 0;
wd.ctx = ctx;
wd.cleared = 0;
+ wd.dir = ino;
retval = ext2fs_block_iterate3(fs, ino, 0, 0,
write_dir_block, &wd);
@@ -764,6 +786,7 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
fd.err = 0;
fd.dir_size = 0;
fd.compress = 0;
+ fd.dir = ino;
if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
(inode.i_size / fs->blocksize) < 2)
fd.compress = 1;
@@ -823,7 +846,7 @@ resort:
* Copy the directory entries. In a htree directory these
* will become the leaf nodes.
*/
- retval = copy_dir_entries(ctx, &fd, &outdir);
+ retval = copy_dir_entries(ctx, &fd, &outdir, ino);
if (retval)
goto errout;
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 8907459..1c06f5e 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -93,6 +93,122 @@ errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
return __get_dx_countlimit(fs, dirent, cc, offset, 0);
}
+void ext2fs_initialize_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry_tail *t)
+{
+ memset(t, 0, sizeof(struct ext2_dir_entry_tail));
+ ext2fs_set_rec_len(fs, sizeof(struct ext2_dir_entry_tail),
+ (struct ext2_dir_entry *)t);
+ t->det_reserved_name_len = EXT2_DIR_NAME_LEN_CSUM;
+}
+
+static errcode_t __get_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dir_entry_tail **tt,
+ int need_swab)
+{
+ struct ext2_dir_entry *d;
+ void *top;
+ struct ext2_dir_entry_tail *t;
+ unsigned int rec_len;
+ errcode_t retval = 0;
+ __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+ d = dirent;
+ top = EXT2_DIRENT_TAIL(dirent, fs->blocksize);
+
+ rec_len = translate(d->rec_len);
+ while (rec_len && !(rec_len & 0x3)) {
+ d = (struct ext2_dir_entry *)(((void *)d) + rec_len);
+ if ((void *)d >= top)
+ break;
+ rec_len = translate(d->rec_len);
+ }
+
+ if (d != top)
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+ t = (struct ext2_dir_entry_tail *)d;
+ if (t->det_reserved_zero1 ||
+ translate(t->det_rec_len) != sizeof(struct ext2_dir_entry_tail) ||
+ translate(t->det_reserved_name_len) != EXT2_DIR_NAME_LEN_CSUM)
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+ if (tt)
+ *tt = t;
+ return retval;
+}
+
+int ext2fs_dirent_has_tail(ext2_filsys fs, struct ext2_dir_entry *dirent)
+{
+ return __get_dirent_tail(fs, dirent, NULL, 0) == 0;
+}
+
+static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent, __u32 *crc,
+ int size)
+{
+ errcode_t retval;
+ char *buf = (char *)dirent;
+ __u32 gen;
+ struct ext2_inode inode;
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size);
+
+ return 0;
+}
+
+int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ errcode_t retval;
+ __u32 calculated;
+ struct ext2_dir_entry_tail *t;
+
+ retval = __get_dirent_tail(fs, dirent, &t, 1);
+ if (retval)
+ return 1;
+
+ /*
+ * The checksum field is overlaid with the dirent->name field
+ * so the swapfs.c functions won't change the endianness.
+ */
+ retval = ext2fs_dirent_csum(fs, inum, dirent, &calculated,
+ (void *)t - (void *)dirent);
+ if (retval)
+ return 0;
+ return ext2fs_le32_to_cpu(t->det_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dirent_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ errcode_t retval;
+ __u32 crc;
+ struct ext2_dir_entry_tail *t;
+
+ retval = __get_dirent_tail(fs, dirent, &t, 1);
+ if (retval)
+ return retval;
+
+ /* swapfs.c functions don't change the checksum endianness */
+ retval = ext2fs_dirent_csum(fs, inum, dirent, &crc,
+ (void *)t - (void *)dirent);
+ if (retval)
+ return retval;
+ t->det_checksum = ext2fs_cpu_to_le32(crc);
+ return 0;
+}
+
static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_dir_entry *dirent,
__u32 *crc, int count_offset, int count,
@@ -179,6 +295,38 @@ static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
return retval;
}
+int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+ return ext2fs_dirent_csum_verify(fs, inum, dirent);
+ if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+ return ext2fs_dx_csum_verify(fs, inum, dirent);
+
+ return 0;
+}
+
+errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+ return ext2fs_dirent_csum_set(fs, inum, dirent);
+ if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+ return ext2fs_dx_csum_set(fs, inum, dirent);
+
+ if (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)
+ return 0;
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+}
+
#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
(sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
index 5125d19..c24015c 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -192,17 +192,23 @@ int ext2fs_process_dir_block(ext2_filsys fs,
unsigned int rec_len, size;
int entry;
struct ext2_dir_entry *dirent;
+ int csum_size = 0;
if (blockcnt < 0)
return 0;
entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;
- ctx->errcode = ext2fs_read_dir_block3(fs, *blocknr, ctx->buf, 0);
+ ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
+ ctx->dir);
if (ctx->errcode)
return BLOCK_ABORT;
- while (offset < fs->blocksize) {
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ while (offset < (fs->blocksize - csum_size)) {
dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
if (ext2fs_get_rec_len(fs, dirent, &rec_len))
return BLOCK_ABORT;
@@ -259,8 +265,8 @@ next:
}
if (changed) {
- ctx->errcode = ext2fs_write_dir_block3(fs, *blocknr, ctx->buf,
- 0);
+ ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
+ 0, ctx->dir);
if (ctx->errcode)
return BLOCK_ABORT;
}
diff --git a/lib/ext2fs/dirblock.c b/lib/ext2fs/dirblock.c
index cb3a104..54b2777 100644
--- a/lib/ext2fs/dirblock.c
+++ b/lib/ext2fs/dirblock.c
@@ -20,45 +20,36 @@
#include "ext2_fs.h"
#include "ext2fs.h"
-errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
- void *buf, int flags EXT2FS_ATTR((unused)))
+errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block,
+ void *buf, int flags EXT2FS_ATTR((unused)),
+ ext2_ino_t ino)
{
errcode_t retval;
- char *p, *end;
- struct ext2_dir_entry *dirent;
- unsigned int name_len, rec_len;
-
+ int corrupt = 0;
retval = io_channel_read_blk64(fs->io, block, 1, buf);
if (retval)
return retval;
- p = (char *) buf;
- end = (char *) buf + fs->blocksize;
- while (p < end-8) {
- dirent = (struct ext2_dir_entry *) p;
-#ifdef WORDS_BIGENDIAN
- dirent->inode = ext2fs_swab32(dirent->inode);
- dirent->rec_len = ext2fs_swab16(dirent->rec_len);
- dirent->name_len = ext2fs_swab16(dirent->name_len);
-#endif
- name_len = dirent->name_len;
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_dir_block_csum_verify(fs, ino,
+ (struct ext2_dir_entry *)buf))
+ corrupt = 1;
+
#ifdef WORDS_BIGENDIAN
- if (flags & EXT2_DIRBLOCK_V2_STRUCT)
- dirent->name_len = ext2fs_swab16(dirent->name_len);
+ retval = ext2fs_dirent_swab_in(fs, buf, flags);
#endif
- if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
- return retval;
- if ((rec_len < 8) || (rec_len % 4)) {
- rec_len = 8;
- retval = EXT2_ET_DIR_CORRUPTED;
- } else if (((name_len & 0xFF) + 8) > rec_len)
- retval = EXT2_ET_DIR_CORRUPTED;
- p += rec_len;
- }
+ if (!retval && corrupt)
+ retval = EXT2_ET_DIR_CSUM_INVALID;
return retval;
}
+errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
+ void *buf, int flags EXT2FS_ATTR((unused)))
+{
+ return ext2fs_read_dir_block4(fs, block, buf, flags, 0);
+}
+
errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
void *buf, int flags EXT2FS_ATTR((unused)))
{
@@ -72,45 +63,40 @@ errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
}
-errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
- void *inbuf, int flags EXT2FS_ATTR((unused)))
+errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block,
+ void *inbuf, int flags EXT2FS_ATTR((unused)),
+ ext2_ino_t ino)
{
-#ifdef WORDS_BIGENDIAN
errcode_t retval;
- char *p, *end;
- char *buf = 0;
- unsigned int rec_len;
- struct ext2_dir_entry *dirent;
+ char *buf = inbuf;
+#ifdef WORDS_BIGENDIAN
retval = ext2fs_get_mem(fs->blocksize, &buf);
if (retval)
return retval;
memcpy(buf, inbuf, fs->blocksize);
- p = buf;
- end = buf + fs->blocksize;
- while (p < end) {
- dirent = (struct ext2_dir_entry *) p;
- if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
- return retval;
- if ((rec_len < 8) ||
- (rec_len % 4)) {
- ext2fs_free_mem(&buf);
- return (EXT2_ET_DIR_CORRUPTED);
- }
- p += rec_len;
- dirent->inode = ext2fs_swab32(dirent->inode);
- dirent->rec_len = ext2fs_swab16(dirent->rec_len);
- dirent->name_len = ext2fs_swab16(dirent->name_len);
-
- if (flags & EXT2_DIRBLOCK_V2_STRUCT)
- dirent->name_len = ext2fs_swab16(dirent->name_len);
- }
+ retval = ext2fs_dirent_swab_out(fs, buf, flags);
+ if (retval)
+ return retval;
+#endif
+ retval = ext2fs_dir_block_csum_set(fs, ino,
+ (struct ext2_dir_entry *)buf);
+ if (retval)
+ goto out;
+
retval = io_channel_write_blk64(fs->io, block, 1, buf);
+
+out:
+#ifdef WORDS_BIGENDIAN
ext2fs_free_mem(&buf);
- return retval;
-#else
- return io_channel_write_blk64(fs->io, block, 1, (char *) inbuf);
#endif
+ return retval;
+}
+
+errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
+ void *inbuf, int flags EXT2FS_ATTR((unused)))
+{
+ return ext2fs_write_dir_block4(fs, block, inbuf, flags, 0);
}
errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
index 41c4088..22558d6 100644
--- a/lib/ext2fs/expanddir.c
+++ b/lib/ext2fs/expanddir.c
@@ -24,6 +24,7 @@ struct expand_dir_struct {
int newblocks;
blk64_t goal;
errcode_t err;
+ ext2_ino_t dir;
};
static int expand_dir_proc(ext2_filsys fs,
@@ -62,7 +63,8 @@ static int expand_dir_proc(ext2_filsys fs,
return BLOCK_ABORT;
}
es->done = 1;
- retval = ext2fs_write_dir_block(fs, new_blk, block);
+ retval = ext2fs_write_dir_block4(fs, new_blk, block, 0,
+ es->dir);
} else {
retval = ext2fs_get_mem(fs->blocksize, &block);
if (retval) {
@@ -110,6 +112,7 @@ errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
es.err = 0;
es.goal = 0;
es.newblocks = 0;
+ es.dir = dir;
retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
0, expand_dir_proc, &es);
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 1e51384..ad0cb7a 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -455,4 +455,7 @@ ec EXT2_ET_EXTENT_CSUM_INVALID,
ec EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
"Directory block does not have space for checksum"
+ec EXT2_ET_DIR_CSUM_INVALID,
+ "Directory block checksum does not match directory block"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 5943ecc..3ebfd6b 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -943,6 +943,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,
@@ -1026,12 +1038,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,
@@ -1420,6 +1436,8 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
/* swapfs.c */
+extern errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags);
+extern errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags);
extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
int has_header);
extern void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header,
diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
index 2d03b57..2dec5dc 100644
--- a/lib/ext2fs/link.c
+++ b/lib/ext2fs/link.c
@@ -41,6 +41,7 @@ static int link_proc(struct ext2_dir_entry *dirent,
struct ext2_dir_entry *next;
unsigned int rec_len, min_rec_len, curr_rec_len;
int ret = 0;
+ int csum_size = 0;
rec_len = EXT2_DIR_REC_LEN(ls->namelen);
@@ -48,12 +49,15 @@ static int link_proc(struct ext2_dir_entry *dirent,
if (ls->err)
return DIRENT_ABORT;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(ls->fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
/*
* See if the following directory entry (if any) is unused;
* if so, absorb it into this one.
*/
next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
- if ((offset + (int) curr_rec_len < blocksize - 8) &&
+ if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
(next->inode == 0) &&
(offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
curr_rec_len += next->rec_len;
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index 861ddab..4a85439 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -100,7 +100,7 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
retval = ext2fs_write_new_inode(fs, ino, &inode);
if (retval)
goto cleanup;
- retval = ext2fs_write_dir_block(fs, blk, block);
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
if (retval)
goto cleanup;
diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c
index b0a1e47..2cd541d 100644
--- a/lib/ext2fs/newdir.c
+++ b/lib/ext2fs/newdir.c
@@ -34,6 +34,8 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
char *buf;
int rec_len;
int filetype = 0;
+ struct ext2_dir_entry_tail *t;
+ int csum_size = 0;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -43,7 +45,11 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
memset(buf, 0, fs->blocksize);
dir = (struct ext2_dir_entry *) buf;
- retval = ext2fs_set_rec_len(fs, fs->blocksize, dir);
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ retval = ext2fs_set_rec_len(fs, fs->blocksize - csum_size, dir);
if (retval)
return retval;
@@ -57,7 +63,7 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
dir->inode = dir_ino;
dir->name_len = 1 | filetype;
dir->name[0] = '.';
- rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1);
+ rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
dir->rec_len = EXT2_DIR_REC_LEN(1);
/*
@@ -73,6 +79,11 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
dir->name[1] = '.';
}
+
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(buf, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
*block = buf;
return 0;
}
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 7c99373..69916e5 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -350,4 +350,66 @@ void ext2fs_swap_mmp(struct mmp_struct *mmp)
mmp->mmp_check_interval = ext2fs_swab16(mmp->mmp_check_interval);
}
+errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
+{
+ errcode_t retval;
+ char *p, *end;
+ struct ext2_dir_entry *dirent;
+ unsigned int name_len, rec_len;
+
+ p = (char *) buf;
+ end = (char *) buf + fs->blocksize;
+ while (p < end-8) {
+ dirent = (struct ext2_dir_entry *) p;
+ dirent->inode = ext2fs_swab32(dirent->inode);
+ dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ name_len = dirent->name_len;
+ if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+ if (retval)
+ return retval;
+ if ((rec_len < 8) || (rec_len % 4)) {
+ rec_len = 8;
+ retval = EXT2_ET_DIR_CORRUPTED;
+ } else if (((name_len & 0xFF) + 8) > rec_len)
+ retval = EXT2_ET_DIR_CORRUPTED;
+ p += rec_len;
+ }
+
+ return 0;
+}
+
+errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags)
+{
+ errcode_t retval;
+ char *p, *end;
+ unsigned int rec_len;
+ struct ext2_dir_entry *dirent;
+
+ p = buf;
+ end = buf + fs->blocksize;
+ while (p < end) {
+ dirent = (struct ext2_dir_entry *) p;
+ retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+ if (retval)
+ return retval;
+ if ((rec_len < 8) ||
+ (rec_len % 4)) {
+ ext2fs_free_mem(&buf);
+ return EXT2_ET_DIR_CORRUPTED;
+ }
+ p += rec_len;
+ dirent->inode = ext2fs_swab32(dirent->inode);
+ dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+
+ if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ }
+
+ return 0;
+}
+
#endif
Verify the checksums of separate extended attribute blocks and offer to clear
it if there is a mismatch.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 18 ++++++++++++++++++
e2fsck/problem.c | 15 +++++++++++++++
e2fsck/problem.h | 6 ++++++
3 files changed, 39 insertions(+), 0 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 2fb5560..5484840 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1540,6 +1540,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
struct ext2_ext_attr_entry *entry;
int count;
region_t region = 0;
+ int failed_csum = 0;
blk = ext2fs_file_acl_block(fs, inode);
if (blk == 0)
@@ -1613,6 +1614,11 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
*/
pctx->blk = blk;
pctx->errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, pctx->ino);
+ if (pctx->errcode == EXT2_ET_EXT_ATTR_CSUM_INVALID) {
+ if (fix_problem(ctx, PR_1_EA_BLOCK_CSUM_INVALID, pctx))
+ goto clear_extattr;
+ failed_csum = 1;
+ }
if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
goto clear_extattr;
header = (struct ext2_ext_attr_header *) block_buf;
@@ -1694,6 +1700,18 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
}
region_free(region);
+ /*
+ * We only get here if there was no other errors that were fixed.
+ * If there was a checksum fail, ask to correct it.
+ */
+ if (failed_csum &&
+ fix_problem(ctx, PR_1_EA_BLOCK_ONLY_CSUM_INVALID, pctx)) {
+ pctx->errcode = ext2fs_write_ext_attr3(fs, blk, block_buf,
+ pctx->ino);
+ if (pctx->errcode)
+ return 0;
+ }
+
count = header->h_refcount - 1;
if (count)
ea_refcount_store(ctx->refcount, blk, count);
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 6dbd01c..bfc1ece 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -966,6 +966,21 @@ static struct e2fsck_problem problem_table[] = {
"extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"),
PROMPT_FIX, 0 },
+ /* Extended attribute block checksum for inode does not match. */
+ { PR_1_EA_BLOCK_CSUM_INVALID,
+ N_("Extended attribute @a @b %b checksum for @i %i does not "
+ "match. "),
+ PROMPT_CLEAR, 0 },
+
+ /*
+ * Extended attribute block passes checks, but checksum for inode does
+ * not match.
+ */
+ { PR_1_EA_BLOCK_CSUM_INVALID,
+ N_("Extended attribute @a @b %b passes checks, but checksum for "
+ "@i %i does not match. "),
+ PROMPT_FIX, 0 },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 3f2b2e9..3d952d5 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -562,6 +562,12 @@ struct problem_context {
/* extent block passes checks, but checksum does not match extent block */
#define PR_1_EXTENT_ONLY_CSUM_INVALID 0x010069
+/* ea block checksum invalid */
+#define PR_1_EA_BLOCK_CSUM_INVALID 0x010070
+
+/* ea block passes checks, but checksum invalid */
+#define PR_1_EA_BLOCK_ONLY_CSUM_INVALID 0x010071
+
/*
* Pass 1b errors
*/
Calculate and verify the superblock checksums. Each copy of the superblock
records the number of the group it's in and the FS UUID, so we can simply
checksum the whole block.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/closefs.c | 19 ++++++++++++-------
lib/ext2fs/csum.c | 35 +++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2fs.h | 4 ++++
lib/ext2fs/openfs.c | 6 ++++++
5 files changed, 60 insertions(+), 7 deletions(-)
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 1867be3..a0e28ba 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -246,15 +246,19 @@ static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group,
blk_t group_block,
struct ext2_super_block *super_shadow)
{
+ errcode_t retval;
dgrp_t sgrp = group;
if (sgrp > ((1 << 16) - 1))
sgrp = (1 << 16) - 1;
+
+ super_shadow->s_block_group_nr = sgrp;
#ifdef WORDS_BIGENDIAN
- super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
-#else
- fs->super->s_block_group_nr = sgrp;
+ ext2fs_swap_super(super_shadow);
#endif
+ retval = ext2fs_superblock_csum_set(fs, super_shadow);
+ if (retval)
+ return retval;
return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE,
super_shadow);
@@ -314,6 +318,7 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
&group_shadow);
if (retval)
goto errout;
+ memcpy(super_shadow, fs->super, sizeof(struct ext2_super_block));
memcpy(group_shadow, fs->group_desc, (size_t) fs->blocksize *
fs->desc_blocks);
@@ -334,10 +339,6 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
*/
fs->super->s_state &= ~EXT2_VALID_FS;
fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
-#ifdef WORDS_BIGENDIAN
- *super_shadow = *fs->super;
- ext2fs_swap_super(super_shadow);
-#endif
/*
* If this is an external journal device, don't write out the
@@ -412,6 +413,10 @@ write_primary_superblock_only:
ext2fs_swap_super(super_shadow);
#endif
+ retval = ext2fs_superblock_csum_set(fs, super_shadow);
+ if (retval)
+ return retval;
+
if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC))
retval = io_channel_flush(fs->io);
retval = write_primary_superblock(fs, super_shadow);
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 46f0cb5..4d01bcd 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,41 @@
#define STATIC static
#endif
+static __u32 ext2fs_superblock_csum(ext2_filsys fs, struct ext2_super_block *sb)
+{
+ int offset = offsetof(struct ext2_super_block, s_checksum);
+
+ return ext2fs_crc32c_le(~0, (unsigned char *)sb, offset);
+}
+
+int ext2fs_superblock_csum_verify(ext2_filsys fs, struct ext2_super_block *sb)
+{
+ __u32 calculated;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ calculated = ext2fs_superblock_csum(fs, sb);
+
+ return ext2fs_le32_to_cpu(sb->s_checksum) == calculated;
+}
+
+errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
+ struct ext2_super_block *sb)
+{
+ __u32 crc;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ crc = ext2fs_superblock_csum(fs, sb);
+ sb->s_checksum = ext2fs_cpu_to_le32(crc);
+
+ return 0;
+}
+
static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum,
blk64_t block,
struct ext2_ext_attr_header *hdr,
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 177a97f..0fab4e0 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -461,4 +461,7 @@ ec EXT2_ET_DIR_CSUM_INVALID,
ec EXT2_ET_EXT_ATTR_CSUM_INVALID,
"Extended attribute block checksum does not match block"
+ec EXT2_ET_SB_CSUM_INVALID,
+ "Superblock checksum does not match superblock"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index dba187a..e80fd17 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -943,6 +943,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 6cb629e..3b1e872 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);
When enabling metadata checksums, rewrite separate extended attribute blocks.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 26 ++++++++++++++++++++++++++
1 files changed, 26 insertions(+), 0 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 801e6ea..2ae436e 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -610,9 +610,11 @@ static void rewrite_inodes(ext2_filsys fs)
{
int length = EXT2_INODE_SIZE(fs->super);
struct ext2_inode *inode;
+ char *ea_buf;
ext2_inode_scan scan;
errcode_t retval;
ext2_ino_t ino;
+ blk64_t file_acl_block;
if (fs->super->s_creator_os != EXT2_OS_LINUX)
return;
@@ -629,6 +631,12 @@ static void rewrite_inodes(ext2_filsys fs)
exit(1);
}
+ retval = ext2fs_get_mem(fs->blocksize, &ea_buf);
+ if (retval) {
+ com_err("set_csum", retval, "while allocating memory");
+ exit(1);
+ }
+
do {
retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
if (retval) {
@@ -659,9 +667,27 @@ static void rewrite_inodes(ext2_filsys fs)
exit(1);
}
}
+
+ file_acl_block = ext2fs_file_acl_block(fs, inode);
+ if (!file_acl_block)
+ continue;
+ retval = ext2fs_read_ext_attr3(fs, file_acl_block, ea_buf, ino);
+ if (retval) {
+ com_err("rewrite_eablock", retval,
+ "while rewriting extended attribute");
+ exit(1);
+ }
+ retval = ext2fs_write_ext_attr3(fs, file_acl_block, ea_buf,
+ ino);
+ if (retval) {
+ com_err("rewrite_eablock", retval,
+ "while rewriting extended attribute");
+ exit(1);
+ }
} while (ino);
ext2fs_free_mem(&inode);
+ ext2fs_free_mem(&ea_buf);
ext2fs_close_inode_scan(scan);
}
Record the type of checksum algorithm we're using for metadata in the
superblock, in case we ever want/need to change the algorithm.
Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/set_fields.c | 1 +
lib/e2p/ls.c | 15 ++++++++++++++-
lib/ext2fs/csum.c | 9 +++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/openfs.c | 12 ++++++++----
6 files changed, 36 insertions(+), 5 deletions(-)
diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index 08bfd8d..871bf20 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -151,6 +151,7 @@ static struct field_set_info super_fields[] = {
{ "grp_quota_inum", &set_sb.s_grp_quota_inum, NULL, 4, parse_uint },
{ "overhead_blocks", &set_sb.s_overhead_blocks, NULL, 4, parse_uint },
{ "checksum", &set_sb.s_checksum, NULL, 4, parse_uint },
+ { "checksum_type", &set_sb.s_checksum_type, NULL, 1, parse_uint },
{ 0, 0, 0, 0 }
};
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index f05e16d..d2e84eb 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -196,6 +196,16 @@ static __u64 e2p_free_blocks_count(struct ext2_super_block *super)
#define EXT2_GOOD_OLD_REV 0
#endif
+static const char *checksum_type(__u8 type)
+{
+ switch (type) {
+ case EXT2_CRC32C_CHKSUM:
+ return "crc32c";
+ default:
+ return "unknown";
+ }
+}
+
void list_super2(struct ext2_super_block * sb, FILE *f)
{
int inode_blocks_per_group;
@@ -421,9 +431,12 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
fprintf(f, "Group quota inode: %u\n",
sb->s_grp_quota_inum);
- if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+ fprintf(f, "Checksum type: %s\n",
+ checksum_type(sb->s_checksum_type));
fprintf(f, "Checksum: 0x%08x\n",
sb->s_checksum);
+ }
}
void list_super (struct ext2_super_block * s)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 4d01bcd..11a24f0 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,15 @@
#define STATIC static
#endif
+int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ return sb->s_checksum_type == EXT2_CRC32C_CHKSUM;
+}
+
static __u32 ext2fs_superblock_csum(ext2_filsys fs, struct ext2_super_block *sb)
{
int offset = offsetof(struct ext2_super_block, s_checksum);
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 0fab4e0..01f9f7b 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -464,4 +464,7 @@ ec EXT2_ET_EXT_ATTR_CSUM_INVALID,
ec EXT2_ET_SB_CSUM_INVALID,
"Superblock checksum does not match superblock"
+ec EXT2_ET_UNKNOWN_CSUM,
+ "Unknown checksum algorithm"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index e80fd17..22c614c 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -943,6 +943,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 3b1e872..30ad913 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
Actually records the checksum algorithm type in the superblock when enabling
checksumming.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 2ae436e..ac5e945 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -701,6 +701,12 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
ext2fs_mark_bb_dirty(fs);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ fs->super->s_checksum_type = EXT2_CRC32C_CHKSUM;
+ else
+ fs->super->s_checksum_type = 0;
+ ext2fs_mark_super_dirty(fs);
}
/*
Checks that directory leaf blocks have the necessary fake dir_entry at the end
of the block to hold a checksum and that the checksum is valid. It will resize
the block and/or rebuild the directory if necessary.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass2.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++-------
e2fsck/pass3.c | 4 +++
e2fsck/problem.c | 15 ++++++++++
e2fsck/problem.h | 9 ++++++
4 files changed, 97 insertions(+), 10 deletions(-)
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 3a0d050..108bdb5 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -748,8 +748,9 @@ static int check_dir_block(ext2_filsys fs,
struct problem_context pctx;
int dups_found = 0;
int ret;
- int dx_csum_size = 0;
+ int dx_csum_size = 0, de_csum_size = 0;
int failed_csum = 0;
+ int is_leaf = 1;
cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -762,8 +763,10 @@ static int check_dir_block(ext2_filsys fs,
return DIRENT_ABORT;
if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
dx_csum_size = sizeof(struct ext2_dx_tail);
+ de_csum_size = sizeof(struct ext2_dir_entry_tail);
+ }
/*
* Make sure the inode is still in use (could have been
@@ -809,11 +812,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);
@@ -861,10 +868,43 @@ static int check_dir_block(ext2_filsys fs,
((fs->blocksize - (8 + dx_csum_size)) /
sizeof(struct ext2_dx_entry))))
dx_db->type = DX_DIRBLOCK_NODE;
+ is_leaf = 0;
}
out_htree:
#endif /* ENABLE_HTREE */
+ /* Verify checksum. */
+ if (is_leaf && de_csum_size) {
+ /* No space for csum? Rebuild dirs in pass 3A. */
+ if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
+ de_csum_size = 0;
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ goto skip_checksum;
+ if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM,
+ &cd->pctx))
+ goto skip_checksum;
+ e2fsck_rehash_dir_later(ctx, ino);
+ goto skip_checksum;
+ }
+ if (failed_csum) {
+ char *buf2;
+ if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_CSUM_INVALID,
+ &cd->pctx))
+ goto skip_checksum;
+ ext2fs_new_dir_block(fs,
+ db->blockcnt == 0 ? ino : 0,
+ EXT2_ROOT_INO, &buf2);
+ memcpy(buf, buf2, fs->blocksize);
+ ext2fs_free_mem(&buf2);
+ dir_modified++;
+ failed_csum = 0;
+ }
+ }
+ /* htree nodes don't use fake dirents to store checksums */
+ if (!is_leaf)
+ de_csum_size = 0;
+
+skip_checksum:
dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
prev = 0;
do {
@@ -1112,10 +1152,7 @@ out_htree:
pctx.ino = ino;
pctx.dirent = dirent;
fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
- if (!ctx->dirs_to_hash)
- ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
- if (ctx->dirs_to_hash)
- ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+ e2fsck_rehash_dir_later(ctx, ino);
dups_found++;
} else
dict_alloc_insert(&de_dict, dirent, dirent);
@@ -1131,7 +1168,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
@@ -1148,22 +1185,44 @@ out_htree:
parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
}
#endif /* ENABLE_HTREE */
- if (offset != fs->blocksize) {
- cd->pctx.num = rec_len - fs->blocksize + offset;
+
+ if (offset != fs->blocksize - de_csum_size) {
+ cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
+ offset;
if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
dirent->rec_len = cd->pctx.num;
dir_modified++;
}
}
if (dir_modified) {
+ /* leaf block with no tail? Rehash dirs later. */
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ is_leaf &&
+ !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf))
+ e2fsck_rehash_dir_later(ctx, ino);
+
+write_and_fix:
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
0, ino);
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (cd->pctx.errcode) {
if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
&cd->pctx))
goto abort_free_dict;
}
ext2fs_mark_changed(fs);
+ } else if (is_leaf && failed_csum && !dir_modified) {
+ /*
+ * If a leaf node that fails csum makes it this far without
+ * alteration, ask the user if the checksum should be fixed.
+ */
+ if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID,
+ &cd->pctx))
+ goto write_and_fix;
}
dict_free_nodes(&de_dict);
return 0;
diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 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 894b2f8..6dbd01c 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1398,6 +1398,21 @@ static struct e2fsck_problem problem_table[] = {
N_("@p @h %d: internal node fails checksum\n"),
PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+ /* leaf node fails checksum */
+ { PR_2_LEAF_NODE_CSUM_INVALID,
+ N_("@d @i %i, %B, offset %N: @d fails checksum\n"),
+ PROMPT_SALVAGE, 0 },
+
+ /* leaf node has no checksum */
+ { PR_2_LEAF_NODE_MISSING_CSUM,
+ N_("@d @i %i, %B, offset %N: @d has no checksum\n"),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* leaf node passes checks, but fails checksum */
+ { PR_2_LEAF_NODE_CSUM_INVALID,
+ N_("@d @i %i, %B, offset %N: @d passes checks, but fails checksum\n"),
+ PROMPT_FIX, 0 },
+
/* Pass 3 errors */
/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 3185da9..3f2b2e9 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -833,6 +833,15 @@ struct problem_context {
/* htree node fails checksum */
#define PR_2_HTREE_NODE_CSUM_INVALID 0x02004A
+/* dir leaf node fails checksum */
+#define PR_2_LEAF_NODE_CSUM_INVALID 0x02004B
+
+/* no space in leaf for checksum */
+#define PR_2_LEAF_NODE_MISSING_CSUM 0x02004C
+
+/* dir leaf node passes checks, but fails checksum */
+#define PR_2_LEAF_NODE_ONLY_CSUM_INVALID 0x02004D
+
/*
* Pass 3 errors
*/
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) {
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 7888a2f..14c047a 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -2419,6 +2419,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);
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 | 60 +++++++++++++++++++++++++++++++++++------------------
2 files changed, 42 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 11a24f0..6d8125e 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -696,32 +696,52 @@ 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(fs->csum_seed, (unsigned char *)&group,
+ sizeof(group));
+ crc32 = ext2fs_crc32c_le(crc32, (unsigned char *)desc,
+ size);
+ desc->bg_checksum = old_crc;
+
+ crc = crc32 & 0xFFFF;
+ goto out;
+ }
+
+ /* old crc16 code */
+ size_t offset = offsetof(struct ext2_group_desc, bg_checksum);
+ crc = ext2fs_crc16(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ crc = ext2fs_crc16(crc, &group, sizeof(group));
+ crc = ext2fs_crc16(crc, desc, offset);
+ offset += sizeof(desc->bg_checksum); /* skip checksum */
+ /* for checksum of struct ext4_group_desc do the rest...*/
+ if (offset < size) {
+ crc = ext2fs_crc16(crc, (char *)desc + offset,
+ size - offset);
}
+out:
return crc;
}
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 | 50 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index ac5e945..2d0ea50 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|
@@ -693,8 +696,12 @@ static void rewrite_inodes(ext2_filsys fs)
static void rewrite_metadata_checksums(ext2_filsys fs)
{
+ int i;
+
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
ext2fs_init_csum_seed(fs);
+ for (i = 0; i < fs->group_desc_count; i++)
+ ext2fs_group_desc_csum_set(fs, i);
rewrite_inodes(fs);
ext2fs_read_bitmaps(fs);
ext2fs_mark_ib_dirty(fs);
@@ -710,6 +717,29 @@ 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;
+
+ ext2fs_init_csum_seed(fs);
+ 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)
@@ -881,6 +911,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))
@@ -2543,6 +2587,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 "
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 14c047a..80218bd 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);
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 bfc1ece..46c20ba 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 3d952d5..d8bb675 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 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 2d0ea50..cba4d4c 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -706,6 +706,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,
Calculate and verify a checksum of the MMP block.
Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/debugfs.c | 1 +
debugfs/set_fields.c | 1 +
lib/ext2fs/csum.c | 34 ++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2fs.h | 3 +++
lib/ext2fs/mmp.c | 19 +++++++++++++++++--
lib/ext2fs/swapfs.c | 1 +
7 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 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 6d8125e..99ca652 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,40 @@
#define STATIC static
#endif
+static __u32 ext2fs_mmp_csum(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ int offset = offsetof(struct mmp_struct, mmp_checksum);
+
+ return ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)mmp, offset);
+}
+
+int ext2fs_mmp_csum_verify(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ __u32 calculated;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ calculated = ext2fs_mmp_csum(fs, mmp);
+
+ return ext2fs_le32_to_cpu(mmp->mmp_checksum) == calculated;
+}
+
+errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ __u32 crc;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ crc = ext2fs_mmp_csum(fs, mmp);
+ mmp->mmp_checksum = ext2fs_cpu_to_le32(crc);
+
+ return 0;
+}
+
int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb)
{
if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 01f9f7b..a42ed9a 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -467,4 +467,7 @@ ec EXT2_ET_SB_CSUM_INVALID,
ec EXT2_ET_UNKNOWN_CSUM,
"Unknown checksum algorithm"
+ec EXT2_ET_MMP_CSUM_INVALID,
+ "MMP block checksum does not match MMP block"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 22c614c..10113f7 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -943,6 +943,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);
@@ -1431,6 +1433,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;
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;
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;
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 23dec30..b8f386e 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -311,6 +311,16 @@ static void list_bad_blocks(ext2_filsys fs, int dump)
ext2fs_badblocks_list_free(bb_list);
}
+static char *journal_checksum_type_str(__u8 type)
+{
+ switch (type) {
+ case JBD2_CRC32C_CHKSUM:
+ return "crc32c";
+ default:
+ return "unknown";
+ }
+}
+
static void print_inline_journal_information(ext2_filsys fs)
{
journal_superblock_t *jsb;
@@ -377,6 +387,15 @@ static void print_inline_journal_information(ext2_filsys fs)
(unsigned int)ntohl(jsb->s_maxlen),
(unsigned int)ntohl(jsb->s_sequence),
(unsigned int)ntohl(jsb->s_start));
+ if (jsb->s_feature_compat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM))
+ printf(_("Journal checksum type: crc32\n"));
+ if (jsb->s_feature_incompat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2))
+ printf(_("Journal checksum type: %s\n"
+ "Journal checksum: 0x%08x\n"),
+ journal_checksum_type_str(jsb->s_checksum_type),
+ ext2fs_be32_to_cpu(jsb->s_checksum));
}
static void print_journal_information(ext2_filsys fs)
@@ -402,6 +421,16 @@ static void print_journal_information(ext2_filsys fs)
exit(1);
}
+ if (jsb->s_feature_compat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM))
+ printf(_("Journal checksum type: crc32\n"));
+ if (jsb->s_feature_incompat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2))
+ printf(_("Journal checksum type: %s\n"
+ "Journal checksum: 0x%08x\n"),
+ journal_checksum_type_str(jsb->s_checksum_type),
+ ext2fs_be32_to_cpu(jsb->s_checksum));
+
printf(_("\nJournal block size: %u\n"
"Journal length: %u\n"
"Journal first block: %u\n"
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>
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
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 10113f7..5c51e7d 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -570,7 +570,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|\
@@ -579,7 +580,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|\
@@ -589,7 +591,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|\
@@ -597,7 +600,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
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);
}
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 39ef6e4..0303124 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);
}
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 5c51e7d..3d62a79 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -943,7 +943,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');
- }