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.
This patchset has been tested on 3.1.0-rc9 on x64, i386, ppc64, and ppc32. The
patches seems to work fine on all four platforms.
--D
[1] https://ext4.wiki.kernel.org/index.php/Ext4_Metadata_Checksums
Change libext2fs to read and write full-size inodes in preparation for the
metadata checksumming patchset, which will require this. Due to ABI
compatibility requirements, this change must be hidden from client programs.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass1.c | 3 +
lib/ext2fs/ext2fsP.h | 2 -
lib/ext2fs/inode.c | 139 +++++++++++++++++++++++++++++---------------------
3 files changed, 83 insertions(+), 61 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 7ce6697..d051703 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 b182d7f..7da0d90 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -73,7 +73,7 @@ struct ext2_inode_cache {
struct ext2_inode_cache_ent {
ext2_ino_t ino;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
};
/* Function prototypes */
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 6c524ff..7b0db60 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -74,7 +74,9 @@ errcode_t ext2fs_flush_icache(ext2_filsys fs)
static errcode_t create_icache(ext2_filsys fs)
{
+ int i;
errcode_t retval;
+ void *p;
if (fs->icache)
return 0;
@@ -93,13 +95,20 @@ static errcode_t create_icache(ext2_filsys fs)
fs->icache->cache_size = 4;
fs->icache->refcount = 1;
retval = ext2fs_get_array(fs->icache->cache_size,
- sizeof(struct ext2_inode_cache_ent),
+ sizeof(struct ext2_inode_cache_ent) +
+ EXT2_INODE_SIZE(fs->super),
&fs->icache->cache);
if (retval) {
ext2fs_free_mem(&fs->icache->buffer);
ext2fs_free_mem(&fs->icache);
return retval;
}
+
+ for (i = 0, p = (void *)(fs->icache->cache + fs->icache->cache_size);
+ i < fs->icache->cache_size;
+ i++, p += EXT2_INODE_SIZE(fs->super))
+ fs->icache->cache[i].inode = p;
+
ext2fs_flush_icache(fs);
return 0;
}
@@ -407,6 +416,8 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
{
errcode_t retval;
int extra_bytes = 0;
+ const int length = EXT2_INODE_SIZE(scan->fs->super);
+ struct ext2_inode *iptr = inode;
EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
@@ -469,6 +480,12 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
#endif
}
+ if (bufsize < length) {
+ retval = ext2fs_get_mem(length, &iptr);
+ if (retval)
+ return retval;
+ }
+
retval = 0;
if (extra_bytes) {
memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
@@ -477,26 +494,26 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->bytes_left -= scan->inode_size - extra_bytes;
#ifdef WORDS_BIGENDIAN
- memset(inode, 0, bufsize);
+ memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
- (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) iptr,
(struct ext2_inode_large *) scan->temp_buffer,
- 0, bufsize);
+ 0, length);
#else
- *inode = *((struct ext2_inode *) scan->temp_buffer);
+ memcpy(iptr, scan->temp_buffer, length);
#endif
if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
} else {
#ifdef WORDS_BIGENDIAN
- memset(inode, 0, bufsize);
+ memset(iptr, 0, length);
ext2fs_swap_inode_full(scan->fs,
- (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) iptr,
(struct ext2_inode_large *) scan->ptr,
- 0, bufsize);
+ 0, length);
#else
- memcpy(inode, scan->ptr, bufsize);
+ memcpy(iptr, scan->ptr, length);
#endif
scan->ptr += scan->inode_size;
scan->bytes_left -= scan->inode_size;
@@ -507,6 +524,10 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
scan->inodes_left--;
scan->current_inode++;
*ino = scan->current_inode;
+ if (inode != iptr) {
+ memcpy(inode, iptr, bufsize);
+ ext2fs_free_mem(&iptr);
+ }
return retval;
}
@@ -527,8 +548,10 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
unsigned long group, block, offset;
char *ptr;
errcode_t retval;
- int clen, i, inodes_per_block, length;
+ int clen, i, inodes_per_block;
io_channel io;
+ int length = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode *iptr = inode;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -547,13 +570,10 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
return retval;
}
/* Check to see if it's in the inode cache */
- if (bufsize == sizeof(struct ext2_inode)) {
- /* only old good inode can be retrieved from the cache */
- for (i=0; i < fs->icache->cache_size; i++) {
- if (fs->icache->cache[i].ino == ino) {
- *inode = fs->icache->cache[i].inode;
- return 0;
- }
+ for (i = 0; i < fs->icache->cache_size; i++) {
+ if (fs->icache->cache[i].ino == ino) {
+ memcpy(inode, fs->icache->cache[i].inode, bufsize);
+ return 0;
}
}
if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
@@ -578,11 +598,13 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
}
offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
- length = EXT2_INODE_SIZE(fs->super);
- if (bufsize < length)
- length = bufsize;
+ if (bufsize < length) {
+ retval = ext2fs_get_mem(length, &iptr);
+ if (retval)
+ return retval;
+ }
- ptr = (char *) inode;
+ ptr = (char *) iptr;
while (length) {
clen = length;
if ((offset + length) > fs->blocksize)
@@ -604,18 +626,24 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
ptr += clen;
block_nr++;
}
+ length = EXT2_INODE_SIZE(fs->super);
#ifdef WORDS_BIGENDIAN
- ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode,
- (struct ext2_inode_large *) inode,
- 0, bufsize);
+ ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
+ (struct ext2_inode_large *) iptr,
+ 0, length);
#endif
/* Update the inode cache */
fs->icache->cache_last = (fs->icache->cache_last + 1) %
fs->icache->cache_size;
fs->icache->cache[fs->icache->cache_last].ino = ino;
- fs->icache->cache[fs->icache->cache_last].inode = *inode;
+ memcpy(fs->icache->cache[fs->icache->cache_last].inode, iptr, length);
+
+ if (iptr != inode) {
+ memcpy(inode, iptr, bufsize);
+ ext2fs_free_mem(&iptr);
+ }
return 0;
}
@@ -633,9 +661,10 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
blk64_t block_nr;
unsigned long group, block, offset;
errcode_t retval = 0;
- struct ext2_inode_large temp_inode, *w_inode;
+ struct ext2_inode_large *w_inode;
char *ptr;
- int clen, i, length;
+ int clen, i;
+ int length = EXT2_INODE_SIZE(fs->super);
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -646,46 +675,43 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
return retval;
}
+ if ((ino == 0) || (ino > fs->super->s_inodes_count))
+ return EXT2_ET_BAD_INODE_NUM;
+
+ /* Prepare our shadow buffer for read/modify/byteswap/write */
+ retval = ext2fs_get_mem(length, &w_inode);
+ if (retval)
+ return retval;
+ if (bufsize < length)
+ retval = ext2fs_read_inode_full(fs, ino,
+ (struct ext2_inode *)w_inode,
+ length);
+ if (retval)
+ goto errout;
+
/* Check to see if the inode cache needs to be updated */
if (fs->icache) {
for (i=0; i < fs->icache->cache_size; i++) {
if (fs->icache->cache[i].ino == ino) {
- fs->icache->cache[i].inode = *inode;
+ memcpy(fs->icache->cache[i].inode, inode,
+ bufsize);
break;
}
}
} else {
retval = create_icache(fs);
if (retval)
- return retval;
+ goto errout;
}
+ memcpy(w_inode, inode, bufsize);
- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
-
- if ((ino == 0) || (ino > fs->super->s_inodes_count))
- return EXT2_ET_BAD_INODE_NUM;
-
- length = bufsize;
- if (length < EXT2_INODE_SIZE(fs->super))
- length = EXT2_INODE_SIZE(fs->super);
-
- if (length > (int) sizeof(struct ext2_inode_large)) {
- w_inode = malloc(length);
- if (!w_inode) {
- retval = ENOMEM;
- goto errout;
- }
- } else
- w_inode = &temp_inode;
- memset(w_inode, 0, length);
+ if (!(fs->flags & EXT2_FLAG_RW)) {
+ retval = EXT2_ET_RO_FILSYS;
+ goto errout;
+ }
#ifdef WORDS_BIGENDIAN
- ext2fs_swap_inode_full(fs, w_inode,
- (struct ext2_inode_large *) inode,
- 1, bufsize);
-#else
- memcpy(w_inode, inode, bufsize);
+ ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
#endif
group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
@@ -700,10 +726,6 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
- length = EXT2_INODE_SIZE(fs->super);
- if (length > bufsize)
- length = bufsize;
This patch adds the ability for the libext2fs functions to read and write the
inode checksum. It also fixes a few fields that were omitted from the byte
swapping routines.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 95 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 9 ++++
lib/ext2fs/ext2_fs.h | 5 ++
lib/ext2fs/ext2fs.h | 4 ++
lib/ext2fs/inode.c | 46 +++++++++++++++++-----
5 files changed, 148 insertions(+), 11 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 596923e..b8945ad 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,101 @@
#define STATIC static
#endif
+static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode,
+ __u32 *crc)
+{
+ __u32 ncrc;
+ struct ext2_inode_large *desc = inode;
+ size_t size = fs->super->s_inode_size;
+ __u16 old_lo;
+ __u16 old_hi;
+ errcode_t retval = 0;
+
+ old_lo = inode->i_checksum_lo;
+ inode->i_checksum_lo = 0;
+ if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_LOCATION) {
+ old_hi = inode->i_checksum_hi;
+ inode->i_checksum_hi = 0;
+ }
+
+#ifdef WORDS_BIGENDIAN
+ struct ext2_inode_large *swabinode;
+ retval = ext2fs_get_mem(size, &swabinode);
+ if (retval)
+ goto err;
+
+ /* Have to swab back to little-endian to do the checksum */
+ memcpy(swabinode, inode, size);
+ ext2fs_swap_inode_full(fs, swabinode, swabinode, 1, size);
+ desc = swabinode;
+#endif
+
+ inum = ext2fs_cpu_to_le32(inum);
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)desc, size);
+ *crc = ncrc;
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_free_mem(&swabinode);
+err:
+#endif
+
+ inode->i_checksum_lo = old_lo;
+ if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_LOCATION)
+ inode->i_checksum_hi = old_hi;
+ return retval;
+}
+
+int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode)
+{
+ errcode_t retval;
+ __u32 provided, calculated;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX ||
+ !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ provided = inode->i_checksum_lo;
+ retval = ext2fs_inode_csum(fs, inum, inode, &calculated);
+ if (retval)
+ return 0;
+ if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_LOCATION)
+ provided |= ((__u32)inode->i_checksum_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;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX ||
+ !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ retval = ext2fs_inode_csum(fs, inum, inode, &crc);
+ if (retval)
+ return retval;
+ inode->i_checksum_lo = crc & 0xFFFF;
+ if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_LOCATION)
+ inode->i_checksum_hi = 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 e759b6f..fa66b85 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -443,4 +443,13 @@ 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 is incorrect"
+
+ec EXT2_ET_INODE_CORRUPT,
+ "Inode checksum indicates corruption"
+
+ec EXT2_ET_INODE_CSUM_NONZERO,
+ "Inode checksum should not be set"
+
end
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 0f8cde8..6fd82f1 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -452,6 +452,10 @@ struct ext2_inode_large {
__u32 i_version_hi; /* high 32 bits for 64-bit version */
};
+#define EXT4_INODE_CSUM_HI_EXTRA_LOCATION \
+ (offsetof(struct ext2_inode_large, i_checksum_hi) + sizeof(__u16) - \
+ EXT2_GOOD_OLD_INODE_SIZE)
+
#define i_dir_acl i_size_high
#if defined(__KERNEL__) || defined(__linux__)
@@ -462,6 +466,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__)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 4a3e7af..04db632 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -925,6 +925,10 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);
/* csum.c */
+extern errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode);
+extern int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode);
extern void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group);
extern int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group);
extern errcode_t ext2fs_set_gdt_csum(ext2_filsys fs);
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 7b0db60..6236976 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);
@@ -521,11 +521,18 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
}
+ /* Verify the inode checksum. */
+ if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(scan->fs, scan->current_inode, iptr))
+ retval = EXT2_ET_INODE_CSUM_INVALID;
+
scan->inodes_left--;
scan->current_inode++;
*ino = scan->current_inode;
- if (inode != iptr) {
- memcpy(inode, iptr, bufsize);
+
+ if (iptr != (struct ext2_inode_large *)inode) {
+ if (!retval)
+ memcpy(inode, iptr, bufsize);
ext2fs_free_mem(&iptr);
}
return retval;
@@ -547,11 +554,11 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
blk64_t block_nr;
unsigned long group, block, offset;
char *ptr;
- errcode_t retval;
+ errcode_t retval = 0;
int clen, i, inodes_per_block;
io_channel io;
int length = EXT2_INODE_SIZE(fs->super);
- struct ext2_inode *iptr = inode;
+ struct ext2_inode_large *iptr = (struct ext2_inode_large *)inode;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -633,6 +640,12 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
(struct ext2_inode_large *) iptr,
0, length);
#endif
+ /* Verify the inode checksum. */
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(fs, ino, iptr)) {
+ retval = EXT2_ET_INODE_CSUM_INVALID;
+ goto err;
+ }
/* Update the inode cache */
fs->icache->cache_last = (fs->icache->cache_last + 1) %
@@ -640,12 +653,14 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
fs->icache->cache[fs->icache->cache_last].ino = ino;
memcpy(fs->icache->cache[fs->icache->cache_last].inode, iptr, length);
- if (iptr != inode) {
- memcpy(inode, iptr, bufsize);
+err:
+ if (iptr != (struct ext2_inode_large *)inode) {
+ if (!retval)
+ memcpy(inode, iptr, bufsize);
ext2fs_free_mem(&iptr);
}
- return 0;
+ return retval;
}
errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino,
@@ -682,12 +697,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) {
@@ -710,6 +730,10 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
goto errout;
}
+ retval = ext2fs_inode_csum_set(fs, ino, w_inode);
+ if (retval)
+ goto errout;
+
#ifdef WORDS_BIGENDIAN
ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
#endif
Add a feature flag to enable metadata checksumming in e2fsprogs. Also add a
runtime flag to disable checksum verification; this flag will be used by
debugfs to salvage filesystems and tune2fs when resetting checksums.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/blkid/probe.h | 1 +
lib/ext2fs/ext2fs.h | 4 +++-
2 files changed, 4 insertions(+), 1 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/ext2fs.h b/lib/ext2fs/ext2fs.h
index b04b0d1..4a3e7af 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
@@ -584,7 +585,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)
/*
* These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed
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 | 43 +++++++++++++++++++++++++++++++++++++++++++
e2fsck/problem.c | 15 +++++++++++++++
e2fsck/problem.h | 9 +++++++++
3 files changed, 67 insertions(+), 0 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index d051703..96f87e8 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -540,6 +540,41 @@ extern void e2fsck_setup_tdb_icount(e2fsck_t ctx, int flags,
*ret = 0;
}
+static int validate_inode_checksum(ext2_filsys fs,
+ e2fsck_t ctx,
+ struct problem_context *pctx,
+ ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ struct ext2_inode_large *linode = (struct ext2_inode_large *)inode;
+
+ /* Ignore non-Linux filesystems */
+ if (fs->super->s_creator_os != EXT2_OS_LINUX)
+ return 0;
+
+ /* Check for invalid inode checksum */
+ if (ext2fs_inode_csum_verify(fs, ino, linode))
+ return 0;
+
+ /*
+ * TODO: Change the following check to use the inode badness patch.
+ * For the moment we'll just assume that the user wants to clear the
+ * bad inode.
+ */
+ if (fix_problem(ctx, PR_1_INODE_CORRUPT, pctx)) {
+ e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
+ if (ino == EXT2_BAD_INO)
+ ext2fs_mark_inode_bitmap2(ctx->inode_used_map,
+ ino);
+ return PR_1_INODE_CORRUPT;
+ } else if (fix_problem(ctx, PR_1_INODE_CSUM_INVALID, pctx)) {
+ e2fsck_write_inode(ctx, ino, inode, "pass1");
+ return PR_1_INODE_CSUM_INVALID;
+ }
+
+ return 0;
+}
+
void e2fsck_pass1(e2fsck_t ctx)
{
int i;
@@ -717,8 +752,10 @@ void e2fsck_pass1(e2fsck_t ctx)
fatal_error(ctx, 0);
}
old_op = ehandler_operation(_("getting next inode from scan"));
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
inode, inode_size);
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
ehandler_operation(old_op);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;
@@ -750,6 +787,12 @@ void e2fsck_pass1(e2fsck_t ctx)
}
}
+ check_inode_extra_space(ctx, &pctx);
+ /* Validate inode checksum. i_extra_isize must be sane. */
+ if (validate_inode_checksum(fs, ctx, &pctx, ino, inode) ==
+ PR_1_INODE_CORRUPT)
+ continue;
+
/*
* Test for incorrect extent flag settings.
*
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 6ee3fa4..86cd954 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -934,6 +934,21 @@ 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 probably not set */
+ { PR_1_INODE_CSUM_INVALID,
+ N_("@i %i checksum incorrect. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* inode checksum probably set, but does not match */
+ { PR_1_INODE_CORRUPT,
+ N_("@i %i checksum shows corruption. "),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* inode checksumming disabled, yet checksum is probably set? */
+ { PR_1_INODE_CSUM_NONZERO,
+ N_("@i %i checksum should not be set. "),
PROMPT_CLEAR, PR_PREEN_OK },
/* Invalid bad inode */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 9db29d8..e1c7f26 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -550,6 +550,15 @@ struct problem_context {
/* Invalid bad inode */
#define PR_1_INVALID_BAD_INODE 0x010065
+/* inode checksum probably not set */
+#define PR_1_INODE_CSUM_INVALID 0x010066
+
+/* inode checksum probably set, but does not match */
+#define PR_1_INODE_CORRUPT 0x010067
+
+/* inode checksum should not be set */
+#define PR_1_INODE_CSUM_NONZERO 0x010068
+
/*
* Pass 1b errors
*/
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 | 53 +++++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_fs.h | 4 ++++
lib/ext2fs/ext2fs.h | 6 +++++
lib/ext2fs/rw_bitmaps.c | 17 +++++++++++++++
6 files changed, 112 insertions(+), 12 deletions(-)
diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index fd203b4..30987fc 100644
--- a/lib/ext2fs/blknum.c
+++ b/lib/ext2fs/blknum.c
@@ -230,6 +230,21 @@ void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk)
}
/*
+ * Return the inode bitmap checksum of a group
+ */
+__u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group)
+{
+ struct ext4_group_desc *gdp;
+ __u32 csum;
+
+ gdp = ext4fs_group_desc(fs, fs->group_desc, group);
+ csum = gdp->bg_inode_bitmap_csum_lo;
+ if (fs->super->s_desc_size < EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION)
+ csum |= ((__u32)gdp->bg_inode_bitmap_csum_hi << 16);
+ return csum;
+}
+
+/*
* Return the inode bitmap block of a group
*/
blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group)
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 973c2a2..1867be3 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -288,6 +288,23 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
fs->super->s_wtime = fs->now ? fs->now : time(NULL);
fs->super->s_block_group_nr = 0;
+
+ /*
+ * If the write_bitmaps() function is present, call it to
+ * flush the bitmaps. This is done this way so that a simple
+ * program that doesn't mess with the bitmaps doesn't need to
+ * drag in the bitmaps.c code.
+ *
+ * Bitmap checksums live in the group descriptor, so the
+ * bitmaps need to be written before the descriptors.
+ */
+ if (fs->write_bitmaps) {
+ retval = fs->write_bitmaps(fs);
+ if (retval)
+ goto errout;
+ }
+
+ /* Prepare the group descriptors for writing */
#ifdef WORDS_BIGENDIAN
retval = EXT2_ET_NO_MEMORY;
retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
@@ -378,18 +395,6 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
ext2fs_numeric_progress_close(fs, &progress, NULL);
- /*
- * If the write_bitmaps() function is present, call it to
- * flush the bitmaps. This is done this way so that a simple
- * program that doesn't mess with the bitmaps doesn't need to
- * drag in the bitmaps.c code.
- */
- if (fs->write_bitmaps) {
- retval = fs->write_bitmaps(fs);
- if (retval)
- goto errout;
- }
-
write_primary_superblock_only:
/*
* Write out master superblock. This has to be done
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index b8945ad..91b1892 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,59 @@
#define STATIC static
#endif
+static __u32 ext2fs_bitmap_csum(ext2_filsys fs, dgrp_t group, char *bitmap,
+ int size)
+{
+ __u32 crc;
+
+ group = ext2fs_cpu_to_le32(group);
+ crc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ crc = ext2fs_crc32c_le(crc, (unsigned char *)&group, sizeof(group));
+ crc = ext2fs_crc32c_le(crc, (unsigned char *)bitmap, size);
+
+ return crc;
+}
+
+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_bitmap_csum(fs, group, bitmap, size);
+ if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION)
+ provided |= ((__u32)gdp->bg_inode_bitmap_csum_hi << 16);
+ else
+ calculated &= 0xFFFF;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ __u32 crc;
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ crc = ext2fs_bitmap_csum(fs, group, bitmap, size);
+ gdp->bg_inode_bitmap_csum_lo = crc & 0xFFFF;
+ if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION)
+ gdp->bg_inode_bitmap_csum_hi = crc >> 16;
+
+ return 0;
+}
+
static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_inode_large *inode,
__u32 *crc)
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 6fd82f1..e7f8bb4 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -191,6 +191,10 @@ struct ext4_group_desc
__u32 bg_reserved;
};
+#define EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION \
+ (offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
+ sizeof(__u16))
+
#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 04db632..d71181b 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -788,6 +788,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);
@@ -818,6 +819,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);
@@ -925,6 +927,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..a325fdb 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,
@@ -163,6 +169,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
blk64_t blk_cnt;
ext2_ino_t ino_itr = 1;
ext2_ino_t ino_cnt;
+ struct ext4_group_desc *gdp;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -288,6 +295,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 */
+ gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc,
+ i);
+ if (!(fs->flags &
+ EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_bitmap_csum_verify(fs, i,
+ inode_bitmap, inode_nbytes))
+ return EXT2_ET_INODE_BITMAP_READ;
} else
memset(inode_bitmap, 0, inode_nbytes);
cnt = inode_nbytes << 3;
Write out checksummed inodes even when writing out a zeroed table.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/mke2fs.c | 30 +++++++++++++++++++++++++++++-
1 files changed, 29 insertions(+), 1 deletions(-)
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 244e0c8..6e9f399 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -304,6 +304,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;
@@ -349,6 +370,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)
@@ -847,7 +874,8 @@ static __u32 ok_features[3] = {
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|
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
};
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 | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 102 insertions(+), 2 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 36368fb..92d17e9 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -91,6 +91,7 @@ static char *extended_cmd;
static unsigned long new_inode_size;
static char *ext_mount_opts;
static int usrquota, grpquota;
+static int rewrite_checksums;
int journal_size, journal_flags;
char *journal_device;
@@ -142,7 +143,8 @@ static __u32 ok_features[3] = {
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
- EXT4_FEATURE_RO_COMPAT_QUOTA
+ EXT4_FEATURE_RO_COMPAT_QUOTA |
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
};
static __u32 clear_ok_features[3] = {
@@ -160,7 +162,8 @@ static __u32 clear_ok_features[3] = {
EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
- EXT4_FEATURE_RO_COMPAT_QUOTA
+ EXT4_FEATURE_RO_COMPAT_QUOTA |
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
};
/*
@@ -351,6 +354,16 @@ static int update_mntopts(ext2_filsys fs, char *mntopts)
return 0;
}
+static int check_fsck_needed(ext2_filsys fs)
+{
+ if (fs->super->s_state & EXT2_VALID_FS)
+ return 0;
+ printf("\n%s\n", _(please_fsck));
+ if (mount_flags & EXT2_MF_READONLY)
+ printf(_("(and reboot afterwards!)\n"));
+ return 1;
+}
+
static void request_fsck_afterwards(ext2_filsys fs)
{
static int requested = 0;
@@ -364,6 +377,59 @@ static void request_fsck_afterwards(ext2_filsys fs)
}
/*
+ * Forcibly set checksums in all inodes.
+ */
+static void rewrite_inodes(ext2_filsys fs)
+{
+ int length = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode *inode;
+ ext2_inode_scan scan;
+ errcode_t retval;
+ ext2_ino_t ino;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX)
+ return;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval) {
+ com_err("set_csum", retval, "while opening inode scan");
+ exit(1);
+ }
+
+ retval = ext2fs_get_mem(length, &inode);
+ if (retval) {
+ com_err("set_csum", retval, "while allocating memory");
+ exit(1);
+ }
+
+ do {
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
+ if (retval) {
+ com_err("set_csum", retval, "while getting next inode");
+ exit(1);
+ }
+ if (!ino)
+ break;
+
+ retval = ext2fs_write_inode_full(fs, ino, inode, length);
+ if (retval) {
+ com_err("set_csum", retval, "while writing inode");
+ exit(1);
+ }
+ } while (ino);
+
+ ext2fs_free_mem(&inode);
+ ext2fs_close_inode_scan(scan);
+}
+
+static void rewrite_metadata_checksums(ext2_filsys fs)
+{
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ rewrite_inodes(fs);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+}
+
+/*
* Update the feature set as provided by the user.
*/
static int update_feature_set(ext2_filsys fs, char *features)
@@ -536,6 +602,20 @@ mmp_error:
}
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (check_fsck_needed(fs))
+ exit(1);
+ rewrite_checksums = 1;
+ }
+
+ if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (check_fsck_needed(fs))
+ exit(1);
+ rewrite_checksums = 1;
+ }
+
+ if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
for (i = 0; i < fs->group_desc_count; i++) {
gd = ext2fs_group_desc(fs, fs->group_desc, i);
@@ -2112,6 +2192,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) {
/*
@@ -2143,7 +2240,10 @@ retry_open:
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
}
ext2fs_mark_super_dirty(fs);
+ 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 "
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 | 5 ++++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in
index fe07f34..b5d208f 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 dd1435b..32a5864 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -2269,7 +2269,7 @@ int main(int argc, char **argv)
fprintf (stderr, "%s %s (%s)\n", debug_prog_name,
E2FSPROGS_VERSION, E2FSPROGS_DATE);
- while ((c = getopt (argc, argv, "iwcR:f:b:s:Vd:D")) != EOF) {
+ while ((c = getopt(argc, argv, "niwcR:f:b:s:Vd:D")) != EOF) {
switch (c) {
case 'R':
request = optarg;
@@ -2283,6 +2283,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;
case 'w':
open_flags |= EXT2_FLAG_RW;
break;
Display the inode bitmap checksum for each block group.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/dumpe2fs.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 5b114e9..98bee2b 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -226,6 +226,11 @@ static void list_desc (ext2_filsys fs)
print_number(ext2fs_inode_bitmap_loc(fs, i));
print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0,
first_block, last_block);
+ if (fs->super->s_desc_size >= EXT2_MIN_DESC_SIZE_64BIT &&
+ fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ printf(_(", csum 0x%08x"),
+ ext2fs_inode_bitmap_checksum(fs, i));
fputs(_("\n Inode table at "), stdout);
print_range(ext2fs_inode_table_loc(fs, i),
ext2fs_inode_table_loc(fs, i) +
Rewrite the block bitmap when the checksum doesn't match. This is ok since
e2fsck will have already computed the correct inode bitmap.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass5.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
e2fsck/problem.c | 5 +++++
e2fsck/problem.h | 3 +++
3 files changed, 67 insertions(+), 0 deletions(-)
diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index a60e84a..104ab84 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)
{
@@ -49,7 +50,9 @@ void e2fsck_pass5(e2fsck_t ctx)
if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
return;
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
e2fsck_read_bitmaps(ctx);
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
check_block_bitmaps(ctx);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
@@ -64,6 +67,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 +79,60 @@ 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)
+ return;
+
+ 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 */
+ 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 86cd954..9134f92 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1694,6 +1694,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 e1c7f26..d6b36ef 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1022,6 +1022,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
*/
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 32a5864..d71de7d 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -792,6 +792,19 @@ void internal_dump_inode(FILE *out, const char *prefix,
if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
internal_dump_inode_extra(out, prefix, inode_num,
(struct ext2_inode_large *) inode);
+ if (current_fs->super->s_creator_os == EXT2_OS_LINUX &&
+ current_fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+ __u32 crc = inode->i_checksum_lo;
+ if (is_large_inode &&
+ large_inode->i_extra_isize >=
+ (offsetof(struct ext2_inode_large,
+ i_checksum_hi) -
+ EXT2_GOOD_OLD_INODE_SIZE))
+ crc |= ((__u32)large_inode->i_checksum_hi) << 16;
+ fprintf(out, "Inode checksum: 0x%08x\n", crc);
+ }
+
if (LINUX_S_ISLNK(inode->i_mode) && ext2fs_inode_data_blocks(current_fs,inode) == 0)
fprintf(out, "%sFast_link_dest: %.*s\n", prefix,
(int) inode->i_size, (char *)inode->i_block);
Display the block bitmap checksum when displaying block groups.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/dumpe2fs.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 98bee2b..33c0933 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -222,6 +222,11 @@ static void list_desc (ext2_filsys fs)
print_number(ext2fs_block_bitmap_loc(fs, i));
print_bg_rel_offset(fs, ext2fs_block_bitmap_loc(fs, i), 0,
first_block, last_block);
+ if (fs->super->s_desc_size >= EXT2_MIN_DESC_SIZE_64BIT &&
+ fs->super->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ printf(_(", csum 0x%08x"),
+ ext2fs_block_bitmap_checksum(fs, i));
fputs(_(", Inode bitmap at "), stdout);
print_number(ext2fs_inode_bitmap_loc(fs, i));
print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0,
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 92d17e9..bff7e64 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -426,6 +426,9 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
{
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
rewrite_inodes(fs);
+ ext2fs_read_bitmaps(fs);
+ ext2fs_mark_ib_dirty(fs);
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
}
Provide a field in the block group descriptor to store block bitmap checksum,
and some helper functions to calculate and verify it.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/blknum.c | 15 +++++++++++++++
lib/ext2fs/csum.c | 39 +++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_fs.h | 3 +++
lib/ext2fs/ext2fs.h | 6 ++++++
lib/ext2fs/rw_bitmaps.c | 16 ++++++++++++++++
5 files changed, 79 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index 30987fc..64f9b94 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 91b1892..524d0e5 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -83,6 +83,45 @@ 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_bitmap_csum(fs, group, 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_bitmap_csum(fs, group, 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)
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index e7f8bb4..cdfc4a8 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -194,6 +194,9 @@ struct ext4_group_desc
#define EXT4_BG_INODE_BITMAP_CSUM_HI_LOCATION \
(offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
sizeof(__u16))
+#define EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION \
+ (offsetof(struct ext4_group_desc, bg_block_bitmap_csum_hi) + \
+ sizeof(__u16))
#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index d71181b..3b514a7 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -789,6 +789,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);
@@ -816,6 +817,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);
@@ -927,6 +929,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 a325fdb..df755cc 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,
@@ -273,6 +280,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 */
+ gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc,
+ i);
+ if (!(fs->flags &
+ EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_block_bitmap_csum_verify(fs, i,
+ block_bitmap, block_nbytes))
+ return EXT2_ET_BLOCK_BITMAP_READ;
} else
memset(block_bitmap, 0, block_nbytes);
cnt = block_nbytes << 3;
Check htree internal node checksums. If broken, ask user to clear the htree
index and recreate it later.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/pass2.c | 32 ++++++++++++++++++++++++++++----
e2fsck/problem.c | 10 ++++++++++
e2fsck/problem.h | 6 ++++++
e2fsck/rehash.c | 31 ++++++++++++++++++++++++++++---
4 files changed, 72 insertions(+), 7 deletions(-)
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 0ba2957..11c469b 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -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);
@@ -546,10 +547,24 @@ static void parse_int_node(ext2_filsys fs,
printf("\t Flags: %d\n", root->unused_flags);
#endif
- ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+ ent = (struct ext2_dx_entry *)
+ (block_buf + 24 + root->info_length);
+
+ if (!ext2fs_dx_csum_verify(fs, cd->pctx.ino,
+ (struct ext2_dir_entry *)block_buf) &&
+ 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 (!ext2fs_dx_csum_verify(fs, cd->pctx.ino,
+ (struct ext2_dir_entry *)block_buf) &&
+ 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 +575,12 @@ static void parse_int_node(ext2_filsys fs,
#endif
count = ext2fs_le16_to_cpu(limit->count);
- expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
- sizeof(struct ext2_dx_entry);
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+ expect_limit = (fs->blocksize -
+ (csum_size + ((char *) ent - block_buf))) /
+ sizeof(struct ext2_dx_entry);
if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
@@ -729,6 +748,7 @@ static int check_dir_block(ext2_filsys fs,
struct problem_context pctx;
int dups_found = 0;
int ret;
+ int csum_size = 0;
cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -740,6 +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))
+ 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 + csum_size)) /
sizeof(struct ext2_dx_entry))))
dx_db->type = DX_DIRBLOCK_NODE;
}
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 62dcae3..d5c8796 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1378,6 +1378,16 @@ static struct e2fsck_problem problem_table[] = {
N_("i_file_acl_hi @F %N, @s zero.\n"),
PROMPT_CLEAR, PR_PREEN_OK },
+ /* htree root node fails checksum */
+ { PR_2_HTREE_ROOT_CSUM_INVALID,
+ N_("@p @h %d: root node fails checksum\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* htree internal node fails checksum */
+ { PR_2_HTREE_NODE_CSUM_INVALID,
+ N_("@p @h %d: node fails checksum\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
/* Pass 3 errors */
/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index a584369..22c6b9b 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -824,6 +824,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..5fbe862 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -495,6 +495,7 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
struct ext2_dx_root_info *root;
struct ext2_dx_countlimit *limits;
int filetype = 0;
+ int csum_size = 0;
if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
filetype = EXT2_FT_DIR << 8;
@@ -519,8 +520,13 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
root->indirect_levels = 0;
root->unused_flags = 0;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+
limits = (struct ext2_dx_countlimit *) (buf+32);
- limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
+ limits->limit = (fs->blocksize - (32 + csum_size)) /
+ sizeof(struct ext2_dx_entry);
limits->count = 0;
return root;
@@ -531,14 +537,20 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
{
struct ext2_dir_entry *dir;
struct ext2_dx_countlimit *limits;
+ int csum_size = 0;
memset(buf, 0, fs->blocksize);
dir = (struct ext2_dir_entry *) buf;
dir->inode = 0;
(void) ext2fs_set_rec_len(fs, fs->blocksize, dir);
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dx_tail);
+
limits = (struct ext2_dx_countlimit *) (buf+8);
- limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
+ limits->limit = (fs->blocksize - (8 + csum_size)) /
+ sizeof(struct ext2_dx_entry);
limits->count = 0;
return (struct ext2_dx_entry *) limits;
@@ -588,9 +600,14 @@ static errcode_t calculate_tree(ext2_filsys fs,
if (c1 == 0)
return ENOSPC;
if (c2 == 0) {
- if (limit)
+ if (limit) {
limit->limit = limit->count =
ext2fs_cpu_to_le16(limit->limit);
+ retval = ext2fs_dx_csum_set(fs, ino,
+ (struct ext2_dir_entry *)(((void *)limit) - 8));
+ if (retval)
+ return retval;
+ }
root = (struct ext2_dx_entry *)
(outdir->buf + root_offset);
root->block = ext2fs_cpu_to_le32(outdir->num);
@@ -615,10 +632,18 @@ static errcode_t calculate_tree(ext2_filsys fs,
}
limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
limit->limit = ext2fs_cpu_to_le16(limit->limit);
+ retval = ext2fs_dx_csum_set(fs, ino,
+ (struct ext2_dir_entry *)(((void *)limit) - 8));
+ if (retval)
+ return retval;
}
root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
+ retval = ext2fs_dx_csum_set(fs, ino,
+ (struct ext2_dir_entry *)outdir->buf);
+ if (retval)
+ return retval;
return 0;
}
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 bff7e64..24040e6 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -428,6 +428,7 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
rewrite_inodes(fs);
ext2fs_read_bitmaps(fs);
ext2fs_mark_ib_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
}
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 | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
e2fsck/problem.c | 5 +++++
e2fsck/problem.h | 3 +++
3 files changed, 66 insertions(+), 0 deletions(-)
diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index 104ab84..c04ff37 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)
{
@@ -68,6 +69,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;
@@ -133,6 +135,62 @@ 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)
+ return;
+
+ 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 */
+ 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 9134f92..62dcae3 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1699,6 +1699,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 d6b36ef..a584369 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1025,6 +1025,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 06daf12..218e566 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -227,7 +227,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,
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 | 4 +
e2fsck/pass2.c | 9 ++-
e2fsck/pass3.c | 10 ++-
e2fsck/rehash.c | 49 ++++++++------
lib/ext2fs/csum.c | 165 ++++++++++++++++++++++++++++++++++++++++++++--
lib/ext2fs/dir_iterate.c | 14 +++-
lib/ext2fs/dirblock.c | 96 +++++++++++----------------
lib/ext2fs/expanddir.c | 5 +
lib/ext2fs/ext2_fs.h | 19 +++++
lib/ext2fs/ext2fs.h | 22 ++++++
lib/ext2fs/link.c | 6 +-
lib/ext2fs/mkdir.c | 2 -
lib/ext2fs/newdir.c | 15 ++++
lib/ext2fs/swapfs.c | 62 +++++++++++++++++
15 files changed, 385 insertions(+), 102 deletions(-)
diff --git a/debugfs/htree.c b/debugfs/htree.c
index cfdfdd6..90e5563 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 96f87e8..e2fe53c 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -473,7 +473,9 @@ 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);
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ retval = ext2fs_read_dir_block4(ctx->fs, blk, buf, 0, pctx->ino);
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
ehandler_operation(0);
if (retval)
return;
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 11c469b..6b103fc 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -799,7 +799,9 @@ 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);
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino);
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
ehandler_operation(0);
if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
cd->pctx.errcode = 0; /* We'll handle this ourselves */
@@ -1151,7 +1153,8 @@ out_htree:
}
}
if (dir_modified) {
- cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
+ cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
+ 0, ino);
if (cd->pctx.errcode) {
if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
&cd->pctx))
@@ -1471,7 +1474,7 @@ static int allocate_dir_block(e2fsck_t ctx,
return 1;
}
- pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
+ pctx->errcode = ext2fs_write_dir_block4(fs, blk, block, 0, db->ino);
ext2fs_free_mem(&block);
if (pctx->errcode) {
pctx->str = "ext2fs_write_dir_block";
diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 7164aa9..86a509c 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -197,7 +197,8 @@ static void check_root(e2fsck_t ctx)
return;
}
- pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
+ pctx.errcode = ext2fs_write_dir_block4(fs, blk, block, 0,
+ EXT2_ROOT_INO);
if (pctx.errcode) {
pctx.str = "ext2fs_write_dir_block";
fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
@@ -443,7 +444,7 @@ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
return 0;
}
- retval = ext2fs_write_dir_block(fs, blk, block);
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
ext2fs_free_mem(&block);
if (retval) {
pctx.errcode = retval;
@@ -684,6 +685,7 @@ struct expand_dir_struct {
blk64_t last_block;
errcode_t err;
e2fsck_t ctx;
+ ext2_ino_t dir;
};
static int expand_dir_proc(ext2_filsys fs,
@@ -724,7 +726,8 @@ static int expand_dir_proc(ext2_filsys fs,
return BLOCK_ABORT;
}
es->num--;
- retval = ext2fs_write_dir_block(fs, new_blk, block);
+ retval = ext2fs_write_dir_block4(fs, new_blk, block, 0,
+ es->dir);
} else {
retval = ext2fs_get_mem(fs->blocksize, &block);
if (retval) {
@@ -777,6 +780,7 @@ errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
es.err = 0;
es.newblocks = 0;
es.ctx = ctx;
+ es.dir = dir;
retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
0, expand_dir_proc, &es);
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 5fbe862..ff5e000 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -62,6 +62,7 @@ struct fill_dir_struct {
int dir_size;
int compress;
ino_t parent;
+ ext2_ino_t dir;
};
struct hash_entry {
@@ -106,7 +107,10 @@ static int fill_dir_block(ext2_filsys fs,
dirent = (struct ext2_dir_entry *) dir;
(void) ext2fs_set_rec_len(fs, fs->blocksize, dirent);
} else {
- fd->err = ext2fs_read_dir_block3(fs, *block_nr, dir, 0);
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0,
+ fd->dir);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (fd->err)
return BLOCK_ABORT;
}
@@ -397,7 +401,8 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
static errcode_t copy_dir_entries(e2fsck_t ctx,
struct fill_dir_struct *fd,
- struct out_dir *outdir)
+ struct out_dir *outdir,
+ ext2_ino_t ino)
{
ext2_filsys fs = ctx->fs;
errcode_t retval;
@@ -408,6 +413,8 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
int i, left;
ext2_dirhash_t prev_hash;
int offset, slack;
+ int csum_size = 0;
+ struct ext2_dir_entry_tail *t;
if (ctx->htree_slack_percentage == 255) {
profile_get_uint(ctx->profile, "options",
@@ -418,6 +425,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
ctx->htree_slack_percentage = 20;
}
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
outdir->max = 0;
retval = alloc_size_dir(fs, outdir,
(fd->dir_size / fs->blocksize) + 2);
@@ -432,9 +443,9 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
dirent = (struct ext2_dir_entry *) block_start;
prev_rec_len = 0;
rec_len = 0;
- left = fs->blocksize;
+ left = fs->blocksize - csum_size;
slack = fd->compress ? 12 :
- (fs->blocksize * ctx->htree_slack_percentage)/100;
+ ((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100;
if (slack < 12)
slack = 12;
for (i = 0; i < fd->num_array; i++) {
@@ -449,12 +460,17 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
if (retval)
return retval;
}
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(block_start,
+ fs->blocksize, csum_size);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
if ((retval = get_next_block(fs, outdir,
&block_start)))
return retval;
offset = 0;
}
- left = fs->blocksize - offset;
+ left = (fs->blocksize - csum_size) - offset;
dirent = (struct ext2_dir_entry *) (block_start + offset);
if (offset == 0) {
if (ent->hash == prev_hash)
@@ -483,6 +499,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
}
if (left)
retval = ext2fs_set_rec_len(fs, rec_len + left, dirent);
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(block_start, fs->blocksize, csum_size);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
return retval;
}
@@ -603,10 +623,6 @@ static errcode_t calculate_tree(ext2_filsys fs,
if (limit) {
limit->limit = limit->count =
ext2fs_cpu_to_le16(limit->limit);
- retval = ext2fs_dx_csum_set(fs, ino,
- (struct ext2_dir_entry *)(((void *)limit) - 8));
- if (retval)
- return retval;
}
root = (struct ext2_dx_entry *)
(outdir->buf + root_offset);
@@ -632,18 +648,10 @@ static errcode_t calculate_tree(ext2_filsys fs,
}
limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
limit->limit = ext2fs_cpu_to_le16(limit->limit);
- retval = ext2fs_dx_csum_set(fs, ino,
- (struct ext2_dir_entry *)(((void *)limit) - 8));
- if (retval)
- return retval;
}
root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
- retval = ext2fs_dx_csum_set(fs, ino,
- (struct ext2_dir_entry *)outdir->buf);
- if (retval)
- return retval;
return 0;
}
@@ -653,6 +661,7 @@ struct write_dir_struct {
errcode_t err;
e2fsck_t ctx;
int cleared;
+ ext2_ino_t dir;
};
/*
@@ -684,7 +693,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;
@@ -706,6 +715,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);
@@ -758,6 +768,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;
@@ -817,7 +828,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 cd788c8..133cf84 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,130 @@
#define STATIC static
#endif
+void ext2fs_initialize_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry_tail *t)
+{
+ memset(t, 0, sizeof(struct ext2_dir_entry_tail));
+ ext2fs_set_rec_len(fs, sizeof(struct ext2_dir_entry_tail),
+ (struct ext2_dir_entry *)t);
+ t->reserved_name_len = EXT2_DIR_NAME_LEN_CSUM;
+}
+
+static struct ext2_dir_entry_tail *get_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry *dirent)
+{
+ struct ext2_dir_entry *d, *top;
+ struct ext2_dir_entry_tail *t;
+ unsigned int rec_len;
+ errcode_t retval;
+
+ d = dirent;
+ top = (struct ext2_dir_entry *)
+ (((void *)dirent) +
+ (fs->blocksize - sizeof(struct ext2_dir_entry_tail)));
+
+ retval = ext2fs_get_rec_len(fs, d, &rec_len);
+ while (d < top && !retval && rec_len) {
+ d = (struct ext2_dir_entry *)(((void *)d) + rec_len);
+ retval = ext2fs_get_rec_len(fs, d, &rec_len);
+ }
+
+ if (d != top)
+ return NULL;
+
+ t = (struct ext2_dir_entry_tail *)d;
+ if (t->reserved_zero1 ||
+ t->rec_len != sizeof(struct ext2_dir_entry_tail) ||
+ t->reserved_name_len != EXT2_DIR_NAME_LEN_CSUM)
+ return NULL;
+
+ return t;
+}
+
+int ext2fs_dirent_has_tail(ext2_filsys fs, struct ext2_dir_entry *dirent)
+{
+ return get_dirent_tail(fs, dirent) != NULL;
+}
+
+static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent, __u32 *crc,
+ int size)
+{
+ errcode_t retval = 0;
+ char *buf = (char *)dirent;
+ __u32 ncrc;
+
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ memcpy(buf, dirent, fs->blocksize);
+ retval = ext2fs_dirent_swab_out(fs, buf, 0);
+ if (retval)
+ goto out;
+#endif
+
+ inum = ext2fs_cpu_to_le32(inum);
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, size);
+ *crc = ncrc;
+
+#ifdef WORDS_BIGENDIAN
+out:
+ ext2fs_free_mem(&buf);
+#endif
+
+ return retval;
+}
+
+int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ errcode_t retval;
+ __u32 calculated;
+ struct ext2_dir_entry_tail *t;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ t = get_dirent_tail(fs, dirent);
+ if (!t)
+ return 1;
+
+ /*
+ * The checksum field is overlaid with the dirent->name field
+ * so the swapfs.c functions won't change the endianness.
+ */
+ retval = ext2fs_dirent_csum(fs, inum, dirent, &calculated,
+ (void *)t - (void *)dirent);
+ if (retval)
+ return 0;
+ return ext2fs_le32_to_cpu(t->checksum) == calculated;
+}
+
+static errcode_t ext2fs_dirent_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ errcode_t retval;
+ __u32 crc;
+ struct ext2_dir_entry_tail *t;
+
+ t = get_dirent_tail(fs, dirent);
+ if (!t)
+ return 0;
+
+ /* swapfs.c functions don't change the checksum endianness */
+ retval = ext2fs_dirent_csum(fs, inum, dirent, &crc,
+ (void *)t - (void *)dirent);
+ if (retval)
+ return retval;
+ t->checksum = ext2fs_cpu_to_le32(crc);
+ return 0;
+}
+
static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_dir_entry *dirent,
__u32 *crc, int count_offset, int count)
@@ -102,8 +226,8 @@ int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum,
return ext2fs_le32_to_cpu(t->checksum) == calculated;
}
-errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
- struct ext2_dir_entry *dirent)
+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;
@@ -111,10 +235,6 @@ errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
struct ext2_dx_tail *t;
int count_offset, limit, count;
- if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
- return 0;
-
c = ext2fs_get_dx_countlimit(fs, dirent, &count_offset);
if (!c)
return 0;
@@ -133,6 +253,39 @@ 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))
+ return ext2fs_dirent_csum_verify(fs, inum, dirent);
+ if (ext2fs_get_dx_countlimit(fs, dirent, NULL))
+ 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))
+ return ext2fs_dirent_csum_set(fs, inum, dirent);
+ if (ext2fs_get_dx_countlimit(fs, dirent, NULL))
+ return ext2fs_dx_csum_set(fs, inum, dirent);
+
+ if (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)
+ return 0;
+ printf("%s: Not htree or leaf dir block?\n", __func__);
+ abort();
+}
+
#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..347d422 100644
--- a/lib/ext2fs/dirblock.c
+++ b/lib/ext2fs/dirblock.c
@@ -20,45 +20,34 @@
#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;
-
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;
-#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);
+ if (retval)
+ return retval;
#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 (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_dir_block_csum_verify(fs, ino,
+ (struct ext2_dir_entry *)buf))
+ return EXT2_ET_DIR_CORRUPTED;
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 +61,38 @@ 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;
+
+ retval = ext2fs_dir_block_csum_set(fs, ino,
+ (struct ext2_dir_entry *)inbuf);
+ if (retval)
+ return retval;
+#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 = io_channel_write_blk64(fs->io, block, 1, buf);
+#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_fs.h b/lib/ext2fs/ext2_fs.h
index 65d205a..3d2ade0 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -798,6 +798,17 @@ struct ext2_dir_entry_2 {
};
/*
+ * This is a bogus directory entry at the end of each leaf block that
+ * records checksums.
+ */
+struct ext2_dir_entry_tail {
+ __u32 reserved_zero1; /* Pretend to be unused */
+ __u16 rec_len; /* 12 */
+ __u16 reserved_name_len; /* 0xDE00, fake namelen/filetype */
+ __u32 checksum; /* crc32c(uuid+inode+dirent) */
+};
+
+/*
* Ext2 directory file types. Only the low 3 bits are used. The
* other bits are reserved for now.
*/
@@ -813,6 +824,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
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 7836fd2..77a92af 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -929,8 +929,20 @@ 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_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
- struct ext2_dir_entry *dirent);
+#define EXT2_DIRENT_TAIL(block, blocksize, csum_size) \
+ ((struct ext2_dir_entry_tail *)(((void *)(block)) + \
+ (blocksize) - (csum_size)))
+
+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 int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent);
extern int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum,
struct ext2_dir_entry *dirent);
extern errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs,
@@ -1012,12 +1024,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 struct ext2_dx_countlimit *ext2fs_get_dx_countlimit(ext2_filsys fs,
@@ -1408,6 +1424,8 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
/* swapfs.c */
+extern errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags);
+extern errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags);
extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
int has_header);
extern void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header,
diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
index 2d03b57..2dec5dc 100644
--- a/lib/ext2fs/link.c
+++ b/lib/ext2fs/link.c
@@ -41,6 +41,7 @@ static int link_proc(struct ext2_dir_entry *dirent,
struct ext2_dir_entry *next;
unsigned int rec_len, min_rec_len, curr_rec_len;
int ret = 0;
+ int csum_size = 0;
rec_len = EXT2_DIR_REC_LEN(ls->namelen);
@@ -48,12 +49,15 @@ static int link_proc(struct ext2_dir_entry *dirent,
if (ls->err)
return DIRENT_ABORT;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(ls->fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
/*
* See if the following directory entry (if any) is unused;
* if so, absorb it into this one.
*/
next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
- if ((offset + (int) curr_rec_len < blocksize - 8) &&
+ if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
(next->inode == 0) &&
(offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
curr_rec_len += next->rec_len;
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index b12bf2d..0d1b7b8 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -95,7 +95,7 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
/*
* Write out the inode and inode data block
*/
- retval = ext2fs_write_dir_block(fs, blk, block);
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
if (retval)
goto cleanup;
retval = ext2fs_write_new_inode(fs, ino, &inode);
diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c
index b0a1e47..7c3ac65 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, csum_size);
+ 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 and calculate extent tree block checksums when processing filesystems.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 75 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2fs.h | 6 ++++
lib/ext2fs/ext3_extents.h | 11 +++++++
lib/ext2fs/extent.c | 21 +++++++++++++
4 files changed, 113 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 524d0e5..7f2d5af 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,81 @@
#define STATIC static
#endif
+#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
+ (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
+
+static struct ext3_extent_tail *get_extent_tail(struct ext3_extent_header *h)
+{
+ return (struct ext3_extent_tail *)(((void *)h) +
+ EXT3_EXTENT_TAIL_OFFSET(h));
+}
+
+static errcode_t ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh,
+ __u32 *crc)
+{
+ int size;
+ __u32 ncrc;
+ errcode_t retval = 0;
+
+ size = EXT3_EXTENT_TAIL_OFFSET(eh) + offsetof(struct ext3_extent_tail,
+ et_checksum);
+
+ inum = ext2fs_cpu_to_le32(inum);
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)eh, size);
+ *crc = ncrc;
+
+ return retval;
+}
+
+int ext2fs_extent_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh)
+{
+ errcode_t retval;
+ __u32 provided, calculated;
+ struct ext3_extent_tail *t = get_extent_tail(eh);
+
+ /*
+ * The extent tree structures are accessed in LE order, so we must
+ * swap the checksum bytes here.
+ */
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ provided = ext2fs_le32_to_cpu(t->et_checksum);
+ retval = ext2fs_extent_block_csum(fs, inum, eh, &calculated);
+ if (retval)
+ return 0;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh)
+{
+ errcode_t retval;
+ __u32 crc;
+ struct ext3_extent_tail *t = get_extent_tail(eh);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ /*
+ * The extent tree structures are accessed in LE order, so we must
+ * swap the checksum bytes here.
+ */
+ retval = ext2fs_extent_block_csum(fs, inum, eh, &crc);
+ if (retval)
+ return retval;
+ t->et_checksum = ext2fs_cpu_to_le32(crc);
+ return retval;
+}
+
static __u32 ext2fs_bitmap_csum(ext2_filsys fs, dgrp_t group, char *bitmap,
int size)
{
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 3b514a7..bf2a9e0 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -929,6 +929,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/ext3_extents.h b/lib/ext2fs/ext3_extents.h
index 88fabc9..99a4f47 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 all valid block sizes (1k, 2k, 4k).
+ * 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
*/
diff --git a/lib/ext2fs/extent.c b/lib/ext2fs/extent.c
index eb096d6..9cb5984 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_HEADER_BAD;
+ }
+
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);
Verify and calculate checksums of htree internal node blocks.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/dirhash.c | 41 ++++++++++++++++++++
lib/ext2fs/ext2fs.h | 6 +++
3 files changed, 150 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 7f2d5af..cd788c8 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,109 @@
#define STATIC static
#endif
+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)
+{
+ errcode_t retval = 0;
+ char *buf = (char *)dirent;
+ int size;
+ __u32 ncrc;
+
+ size = count_offset + (count * sizeof(struct ext2_dx_entry));
+
+ /*
+ * The dx_root/dx_entry structures are always accessed via
+ * le*_to_cpu helpers and don't need swapping here. However, the
+ * dirent container fields need to be swapped.
+ */
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ memcpy(buf, dirent, fs->blocksize);
+ retval = ext2fs_dirent_swab_out(fs, buf, 0);
+ if (retval)
+ goto out;
+#endif
+
+ inum = ext2fs_cpu_to_le32(inum);
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, size);
+ *crc = ncrc;
+
+#ifdef WORDS_BIGENDIAN
+out:
+ ext2fs_free_mem(&buf);
+#endif
+
+ return retval;
+}
+
+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;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ c = ext2fs_get_dx_countlimit(fs, dirent, &count_offset);
+ if (!c)
+ 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);
+ if (retval)
+ return 0;
+
+ return ext2fs_le32_to_cpu(t->checksum) == calculated;
+}
+
+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;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ c = ext2fs_get_dx_countlimit(fs, dirent, &count_offset);
+ if (!c)
+ return 0;
+ 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;
+ 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);
+ if (retval)
+ return retval;
+ t->checksum = ext2fs_cpu_to_le32(crc);
+ return retval;
+}
+
#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
(sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
diff --git a/lib/ext2fs/dirhash.c b/lib/ext2fs/dirhash.c
index c4ac94e..305e9ad 100644
--- a/lib/ext2fs/dirhash.c
+++ b/lib/ext2fs/dirhash.c
@@ -259,3 +259,44 @@ errcode_t ext2fs_dirhash(int version, const char *name, int len,
*ret_minor_hash = minor_hash;
return 0;
}
+
+struct ext2_dx_countlimit *ext2fs_get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent, int *offset)
+{
+ 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;
+ errcode_t retval;
+
+ retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+ if (retval)
+ return NULL;
+
+ if (rec_len == fs->blocksize && dirent->name_len == 0)
+ count_offset = 8;
+ else if (rec_len == 12) {
+ dp = (struct ext2_dir_entry *)(((void *)dirent) + rec_len);
+ retval = ext2fs_get_rec_len(fs, dp, &rec_len);
+ if (retval || rec_len != fs->blocksize - 12)
+ return NULL;
+ root = (struct ext2_dx_root_info *)(((void *)dp + 12));
+ if (root->reserved_zero ||
+ root->info_length != sizeof(struct ext2_dx_root_info))
+ return NULL;
+ count_offset = 32;
+ } else
+ return NULL;
+
+ 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 NULL;
+
+ if (offset)
+ *offset = count_offset;
+ return c;
+}
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index bf2a9e0..7836fd2 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -929,6 +929,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_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent);
+extern int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent);
extern errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs,
ext2_ino_t inum,
struct ext3_extent_header *eh);
@@ -1016,6 +1020,8 @@ extern errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
void *buf, int flags);
/* dirhash.c */
+extern struct ext2_dx_countlimit *ext2fs_get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent, int *offset);
extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
const __u32 *seed,
ext2_dirhash_t *ret_hash,
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 24040e6..9edcfac 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);
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 06e7737..cfdfdd6 100644
--- a/debugfs/htree.c
+++ b/debugfs/htree.c
@@ -120,8 +120,9 @@ static void htree_dump_int_node(ext2_filsys fs, ext2_ino_t ino,
{
struct ext2_dx_countlimit limit;
struct ext2_dx_entry e;
+ struct ext2_dx_tail *tail;
int hash, i;
-
+ int remainder;
limit = *((struct ext2_dx_countlimit *) ent);
limit.count = ext2fs_le16_to_cpu(limit.count);
@@ -130,6 +131,20 @@ static void htree_dump_int_node(ext2_filsys fs, ext2_ino_t ino,
fprintf(pager, "Number of entries (count): %d\n", limit.count);
fprintf(pager, "Number of entries (limit): %d\n", limit.limit);
+ remainder = fs->blocksize - (limit.limit *
+ sizeof(struct ext2_dx_entry));
+ if (ent == (struct ext2_dx_entry *)(rootnode + 1))
+ remainder -= sizeof(struct ext2_dx_root_info) + 24;
+ else
+ remainder -= 8;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ remainder == sizeof(struct ext2_dx_tail)) {
+ tail = (struct ext2_dx_tail *)(ent + limit.limit);
+ fprintf(pager, "Checksum: 0x%08x\n",
+ ext2fs_le32_to_cpu(tail->checksum));
+ }
+
for (i=0; i < limit.count; i++) {
hash = i ? ext2fs_le32_to_cpu(ent[i].hash) : 0;
fprintf(pager, "Entry #%d: Hash 0x%08x%s, block %u\n", i,
Introduce small structures for recording directory tree checksums
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext2_fs.h | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index cdfc4a8..65d205a 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -241,6 +241,14 @@ struct ext2_dx_countlimit {
__u16 count;
};
+/*
+ * This goes at the end of each htree block. If you want to use the
+ * reserved field, you'll have to update the checksum code to include it.
+ */
+struct ext2_dx_tail {
+ __u32 reserved;
+ __u32 checksum; /* crc32c(uuid+inum+dxblock) */
+};
/*
* Macro-instructions used to manage group descriptors
Checks that directory leaf blocks have the necessary fake dir_entry at the end
of the block to hold a checksum and that the checksum is valid. It will resize
the block and/or rebuild the directory if necessary.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/e2fsck.h | 1 +
e2fsck/pass2.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++------
e2fsck/pass3.c | 4 +++
e2fsck/problem.c | 10 +++++++
e2fsck/problem.h | 6 ++++
e2fsck/rehash.c | 12 +++++++++
6 files changed, 101 insertions(+), 8 deletions(-)
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index d225d89..96a0cc9 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -483,6 +483,7 @@ extern void region_free(region_t region);
extern int region_allocate(region_t region, region_addr_t start, int n);
/* rehash.c */
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino);
errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino);
void e2fsck_rehash_directories(e2fsck_t ctx);
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 6b103fc..7b11405 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -748,7 +748,8 @@ static int check_dir_block(ext2_filsys fs,
struct problem_context pctx;
int dups_found = 0;
int ret;
- int csum_size = 0;
+ int dx_csum_size = 0, de_csum_size = 0;
+ int is_leaf = 1;
cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -761,8 +762,10 @@ static int check_dir_block(ext2_filsys fs,
return DIRENT_ABORT;
if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
- csum_size = sizeof(struct ext2_dx_tail);
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ dx_csum_size = sizeof(struct ext2_dx_tail);
+ de_csum_size = sizeof(struct ext2_dir_entry_tail);
+ }
/*
* Make sure the inode is still in use (could have been
@@ -806,11 +809,15 @@ static int check_dir_block(ext2_filsys fs,
if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
cd->pctx.errcode = 0; /* We'll handle this ourselves */
if (cd->pctx.errcode) {
+ char *buf2;
if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
ctx->flags |= E2F_FLAG_ABORT;
return DIRENT_ABORT;
}
- memset(buf, 0, fs->blocksize);
+ ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0,
+ EXT2_ROOT_INO, &buf2);
+ memcpy(buf, buf2, fs->blocksize);
+ ext2fs_free_mem(&buf2);
}
#ifdef ENABLE_HTREE
dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
@@ -855,13 +862,49 @@ static int check_dir_block(ext2_filsys fs,
(rec_len == fs->blocksize) &&
(dirent->name_len == 0) &&
(ext2fs_le16_to_cpu(limit->limit) ==
- ((fs->blocksize - (8 + csum_size)) /
+ ((fs->blocksize - (8 + dx_csum_size)) /
sizeof(struct ext2_dx_entry))))
dx_db->type = DX_DIRBLOCK_NODE;
+ is_leaf = 0;
}
out_htree:
#endif /* ENABLE_HTREE */
+ /* Verify checksum. */
+ if (is_leaf && de_csum_size) {
+ /* No space for csum? Rebuild dirs in pass 3A. */
+ if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
+ de_csum_size = 0;
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ goto skip_checksum;
+ if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM,
+ &cd->pctx))
+ goto skip_checksum;
+ if (!ctx->dirs_to_hash)
+ ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
+ if (ctx->dirs_to_hash)
+ ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+ goto skip_checksum;
+ }
+ if (!ext2fs_dirent_csum_verify(fs, ino,
+ (struct ext2_dir_entry *)buf)) {
+ char *buf2;
+ if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_CSUM_INVALID,
+ &cd->pctx))
+ goto skip_checksum;
+ ext2fs_new_dir_block(fs,
+ db->blockcnt == 0 ? ino : 0,
+ EXT2_ROOT_INO, &buf2);
+ memcpy(buf, buf2, fs->blocksize);
+ ext2fs_free_mem(&buf2);
+ dir_modified++;
+ }
+ }
+ /* htree nodes don't use fake dirents to store checksums */
+ if (!is_leaf)
+ de_csum_size = 0;
+
+skip_checksum:
dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
prev = 0;
do {
@@ -1128,7 +1171,7 @@ out_htree:
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
offset += rec_len;
dot_state++;
- } while (offset < fs->blocksize);
+ } while (offset < fs->blocksize - de_csum_size);
#if 0
printf("\n");
#endif
@@ -1145,16 +1188,33 @@ out_htree:
parse_int_node(fs, db, cd, dx_dir, buf);
}
#endif /* ENABLE_HTREE */
- if (offset != fs->blocksize) {
- cd->pctx.num = rec_len - fs->blocksize + offset;
+
+ if (offset != fs->blocksize - de_csum_size) {
+ cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
+ offset;
if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
dirent->rec_len = cd->pctx.num;
dir_modified++;
}
}
if (dir_modified) {
+ /* leaf block with no tail? Rehash dirs later. */
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ is_leaf &&
+ !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
+ if (!ctx->dirs_to_hash)
+ ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
+ if (ctx->dirs_to_hash)
+ ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+ }
+
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
0, ino);
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (cd->pctx.errcode) {
if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
&cd->pctx))
diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 86a509c..7ebbadc 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -658,8 +658,12 @@ static void fix_dotdot(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
clear_problem_context(&pctx);
pctx.ino = ino;
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_dir_iterate(fs, ino, DIRENT_FLAG_INCLUDE_EMPTY,
0, fix_dotdot_proc, &fp);
+ if (e2fsck_dir_will_be_rehashed(ctx, ino))
+ ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval || !fp.done) {
pctx.errcode = retval;
fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index d5c8796..5412dc8 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1388,6 +1388,16 @@ static struct e2fsck_problem problem_table[] = {
N_("@p @h %d: node fails checksum\n"),
PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+ /* leaf node fails checksum */
+ { PR_2_LEAF_NODE_CSUM_INVALID,
+ N_("@d @i %i, %B, offset %N: @d fails checksum\n"),
+ PROMPT_SALVAGE, 0 },
+
+ /* leaf node fails checksum */
+ { PR_2_LEAF_NODE_MISSING_CSUM,
+ N_("@d @i %i, %B, offset %N: @d has no checksum\n"),
+ PROMPT_FIX, PR_PREEN_OK },
+
/* Pass 3 errors */
/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 22c6b9b..573a7f5 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -830,6 +830,12 @@ struct problem_context {
/* htree node fails checksum */
#define PR_2_HTREE_NODE_CSUM_INVALID 0x02004A
+/* dir leaf node fails checksum */
+#define PR_2_LEAF_NODE_CSUM_INVALID 0x02004B
+
+/* no space in leaf for checksum */
+#define PR_2_LEAF_NODE_MISSING_CSUM 0x02004C
+
/*
* Pass 3 errors
*/
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index ff5e000..31f3856 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -52,6 +52,15 @@
#include "e2fsck.h"
#include "problem.h"
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino)
+{
+ if (ctx->options & E2F_OPT_COMPRESS_DIRS)
+ return 1;
+ if (!ctx->dirs_to_hash)
+ return 0;
+ return ext2fs_u32_list_test(ctx->dirs_to_hash, ino);
+}
+
struct fill_dir_struct {
char *buf;
struct ext2_inode *inode;
@@ -901,8 +910,11 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
if (!ext2fs_u32_list_iterate(iter, &ino))
break;
}
+#if 0
+ /* Why is lost_and_found never cleaned up or hashed? */
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 | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 197 insertions(+), 1 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 9edcfac..5e3db9d 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)
+ dcl = ext2fs_get_dx_countlimit(fs,
+ (struct ext2_dir_entry *)ctx->buf, &dcl_offset);
+ if (dcl) {
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ /* Ensure limit is the max size */
+ int max_entries = (fs->blocksize - dcl_offset) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(dcl->limit) != max_entries) {
+ changed = 1;
+ dcl->limit = ext2fs_cpu_to_le16(max_entries);
+ }
+ } else {
+ /* If htree block is full then rebuild the dir */
+ if (ext2fs_le16_to_cpu(dcl->count) ==
+ ext2fs_le16_to_cpu(dcl->limit)) {
+ request_dir_fsck_afterwards(fs);
+ return 0;
+ }
+ /*
+ * Ensure dcl->limit is small enough to leave room for
+ * the checksum tail.
+ */
+ int max_entries = (fs->blocksize - (dcl_offset +
+ sizeof(struct ext2_dx_tail))) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(dcl->limit) != max_entries)
+ dcl->limit = ext2fs_cpu_to_le16(max_entries);
+ /* Always rewrite checksum */
+ changed = 1;
+ }
+ } else {
+ unsigned int rec_len, name_size;
+ char *top = ctx->buf + fs->blocksize;
+ struct ext2_dir_entry *de = (struct ext2_dir_entry *)ctx->buf;
+ struct ext2_dir_entry *last_de = NULL, *penultimate_de = NULL;
+
+ /* Find last and penultimate dirent */
+ while ((char *)de < top) {
+ penultimate_de = last_de;
+ last_de = de;
+ ctx->errcode = ext2fs_get_rec_len(fs, de, &rec_len);
+ if (!ctx->errcode && !rec_len)
+ ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ de = (struct ext2_dir_entry *)(((void *)de) + rec_len);
+ }
+ ctx->errcode = ext2fs_get_rec_len(fs, last_de, &rec_len);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ name_size = last_de->name_len & 0xFF;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (!penultimate_de)
+ return 0;
+ if (last_de->inode ||
+ name_size ||
+ rec_len != sizeof(struct ext2_dir_entry_tail))
+ return 0;
+ /*
+ * The last dirent is unused and the right length to
+ * have stored a checksum. Erase it.
+ */
+ ctx->errcode = ext2fs_get_rec_len(fs, penultimate_de,
+ &rec_len);
+ if (!rec_len)
+ ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ ext2fs_set_rec_len(fs, rec_len +
+ sizeof(struct ext2_dir_entry_tail),
+ penultimate_de);
+ changed = 1;
+ } else {
+ int csum_size = sizeof(struct ext2_dir_entry_tail);
+ struct ext2_dir_entry_tail *t;
+
+ /*
+ * If the last dirent looks like the tail, just update
+ * the checksum.
+ */
+ if (!last_de->inode &&
+ rec_len == csum_size) {
+ t = (struct ext2_dir_entry_tail *)last_de;
+ t->reserved_name_len = EXT2_DIR_NAME_LEN_CSUM;
+ changed = 1;
+ goto out;
+ }
+ if (name_size & 3)
+ name_size = (name_size & ~3) + 4;
+ /* If there's not enough space for the tail, e2fsck */
+ if (rec_len <= (8 + name_size + csum_size)) {
+ request_dir_fsck_afterwards(fs);
+ return 0;
+ }
+ /* Shorten that last de and insert the tail */
+ ext2fs_set_rec_len(fs, rec_len - csum_size, last_de);
+ t = EXT2_DIRENT_TAIL(ctx->buf, fs->blocksize,
+ csum_size);
+ 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);
@@ -2293,7 +2487,9 @@ retry_open:
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
}
ext2fs_mark_super_dirty(fs);
- rewrite_checksums = 1;
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ rewrite_checksums = 1;
}
if (rewrite_checksums)
rewrite_metadata_checksums(fs);
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 | 5 ++-
e2fsck/super.c | 5 ++-
lib/ext2fs/csum.c | 75 ++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2_ext_attr.h | 4 ++
lib/ext2fs/ext2fs.h | 15 ++++++++-
lib/ext2fs/ext_attr.c | 55 +++++++++++++++++++++++++-------
lib/ext2fs/swapfs.c | 3 +-
10 files changed, 153 insertions(+), 26 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index e2fe53c..0423565 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1483,7 +1483,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;
@@ -1494,8 +1495,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);
@@ -1592,7 +1594,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 5ff92c2..0551fc0 100644
--- a/e2fsck/pass1b.c
+++ b/e2fsck/pass1b.c
@@ -640,9 +640,9 @@ static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
if (ext2fs_file_acl_block(&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(&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 7b11405..d664c8d 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -1283,8 +1283,9 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
if (ext2fs_file_acl_block(&inode) &&
(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
- pctx.errcode = ext2fs_adjust_ea_refcount2(fs, ext2fs_file_acl_block(&inode),
- block_buf, -1, &count);
+ pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
+ ext2fs_file_acl_block(&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 abf8081..605a96e 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -198,8 +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(inode)) {
- retval = ext2fs_adjust_ea_refcount2(fs, ext2fs_file_acl_block(inode),
- block_buf, -1, &count);
+ retval = ext2fs_adjust_ea_refcount3(fs,
+ ext2fs_file_acl_block(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 133cf84..aec5c87 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,81 @@
#define STATIC static
#endif
+static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr,
+ __u32 *crc)
+{
+ errcode_t retval = 0;
+ char *buf = (char *)hdr;
+ __u32 ncrc, old_crc = hdr->h_checksum;
+
+ hdr->h_checksum = 0;
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ ext2fs_swap_ext_attr(buf, (char *)hdr, fs->blocksize, 1);
+#endif
+
+ ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ if (hdr->h_refcount != 1) {
+ block = ext2fs_cpu_to_le64(block);
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&block,
+ sizeof(block));
+ } else {
+ inum = ext2fs_cpu_to_le32(inum);
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum,
+ sizeof(inum));
+ }
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, fs->blocksize);
+ hdr->h_checksum = old_crc;
+ *crc = ncrc;
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_free_mem(&buf);
+#endif
+
+ return retval;
+}
+
+int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr)
+{
+ __u32 calculated;
+ errcode_t retval;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 1;
+
+ retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &calculated);
+ if (retval)
+ return 0;
+
+ return 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 = crc;
+ return 0;
+}
+
void ext2fs_initialize_dirent_tail(ext2_filsys fs,
struct ext2_dir_entry_tail *t)
{
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index fa66b85..e210f8d 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -452,4 +452,7 @@ ec EXT2_ET_INODE_CORRUPT,
ec EXT2_ET_INODE_CSUM_NONZERO,
"Inode checksum should not be set"
+ec EXT2_ET_EXT_ATTR_BLOCK_CORRUPT,
+ "Extended attribute block fails checksum"
+
end
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/ext2fs.h b/lib/ext2fs/ext2fs.h
index 77a92af..9f0e8fa 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -929,10 +929,15 @@ 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, csum_size) \
((struct ext2_dir_entry_tail *)(((void *)(block)) + \
(blocksize) - (csum_size)))
-
extern void ext2fs_initialize_dirent_tail(ext2_filsys fs,
struct ext2_dir_entry_tail *t);
extern int ext2fs_dirent_has_tail(ext2_filsys fs,
@@ -1086,16 +1091,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..79d97d7 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -61,7 +61,8 @@ __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;
@@ -71,38 +72,57 @@ errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
#ifdef WORDS_BIGENDIAN
ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
#endif
+
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_ext_attr_block_csum_verify(fs, inum, block, buf))
+ return EXT2_ET_EXT_ATTR_BLOCK_CORRUPT;
+
return 0;
}
+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)
{
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);
+ retval = ext2fs_ext_attr_block_csum_set(fs, inum, block,
+ (struct ext2_ext_attr_header *)inbuf);
+ if (retval)
+ return retval;
+
+#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 = 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 +131,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 +150,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 +159,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 +169,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]);
}
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 2f4c232..1d81762 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1184,6 +1184,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) {
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 | 7 +++++++
e2fsck/problem.c | 5 +++++
e2fsck/problem.h | 3 +++
3 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 0423565..616e26d 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1594,7 +1594,9 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
* validate it
*/
pctx->blk = blk;
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
pctx->errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, pctx->ino);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
goto clear_extattr;
header = (struct ext2_ext_attr_header *) block_buf;
@@ -1607,6 +1609,11 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
goto clear_extattr;
}
+ if (!ext2fs_ext_attr_block_csum_verify(fs, pctx->ino, pctx->blk,
+ (struct ext2_ext_attr_header *)block_buf) &&
+ fix_problem(ctx, PR_1_EA_BLOCK_CSUM_INVALID, pctx))
+ goto clear_extattr;
+
if (header->h_blocks != 1) {
if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
goto clear_extattr;
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 5412dc8..76a13de 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -956,6 +956,11 @@ static struct e2fsck_problem problem_table[] = {
N_("The bad @b @i looks @n. "),
PROMPT_CLEAR, 0 },
+ /* Checksum failure reading extended attribute block */
+ { PR_1_EA_BLOCK_CSUM_INVALID,
+ N_("Checksum failure @a @b %b for @i %i. "),
+ PROMPT_CLEAR, 0 },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 573a7f5..fdb7f99 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -559,6 +559,9 @@ struct problem_context {
/* inode checksum should not be set */
#define PR_1_INODE_CSUM_NONZERO 0x010068
+/* ea block checksum invalid */
+#define PR_1_EA_BLOCK_CSUM_INVALID 0x010065
+
/*
* Pass 1b errors
*/
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 5e3db9d..b2c9e32 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(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/ext2_fs.h | 5 ++++-
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/openfs.c | 12 ++++++++----
7 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index d461275..0103497 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -148,6 +148,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 e2c32f4..9e4536e 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 e4d2caa..3b92eac 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -458,4 +458,7 @@ ec EXT2_ET_EXT_ATTR_BLOCK_CORRUPT,
ec EXT2_ET_SB_CSUM_INVALID,
"Superblock fails checksum"
+ec EXT2_ET_UNKNOWN_CSUM,
+ "Unknown checksum algorithm"
+
end
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 3d2ade0..d6cd6e0 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -553,6 +553,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
*/
@@ -638,7 +641,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 */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index b921f83..9311336 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -929,6 +929,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 334240c..2a672e0 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -203,10 +203,14 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
}
#endif
- 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;
}
if (fs->super->s_magic != EXT2_SUPER_MAGIC) {
When toggling the bg_use_meta_csum feature, we should rewrite the block groups
with the desired checksum.
Signed-off-by: Darrick J. Wong <[email protected]>
---
misc/tune2fs.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 708bc20..4f02905 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,7 +696,11 @@ static void rewrite_inodes(ext2_filsys fs)
static void rewrite_metadata_checksums(ext2_filsys fs)
{
+ int i;
+
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ for (i = 0; i < fs->group_desc_count; i++)
+ ext2fs_group_desc_csum_set(fs, i);
rewrite_inodes(fs);
ext2fs_read_bitmaps(fs);
ext2fs_mark_ib_dirty(fs);
@@ -709,6 +716,28 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
}
/*
+ * Rewrite just the block group checksums. Only call this function if
+ * you're _not_ calling rewrite_metadata_checksums; this function exists
+ * to handle the case that you're changing bg_use_meta_csum and NOT changing
+ * either gdt_csum or metadata_csum.
+ */
+static void rewrite_bg_checksums(ext2_filsys fs)
+{
+ int i;
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM) ||
+ !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return;
+
+ for (i = 0; i < fs->group_desc_count; i++)
+ ext2fs_group_desc_csum_set(fs, i);
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ ext2fs_mark_super_dirty(fs);
+}
+
+/*
* Update the feature set as provided by the user.
*/
static int update_feature_set(ext2_filsys fs, char *features)
@@ -880,6 +909,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))
@@ -2525,6 +2568,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 "
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 | 18 +++++++++++-------
lib/ext2fs/csum.c | 34 ++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2fs.h | 4 ++++
lib/ext2fs/openfs.c | 6 ++++++
5 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 1867be3..008ee91 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -246,14 +246,18 @@ 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;
+ retval = ext2fs_superblock_csum_set(fs, super_shadow);
+ if (retval)
+ return retval;
#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
return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE,
@@ -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
@@ -407,6 +408,9 @@ write_primary_superblock_only:
fs->super->s_block_group_nr = 0;
fs->super->s_state = fs_state;
fs->super->s_feature_incompat = feature_incompat;
+ retval = ext2fs_superblock_csum_set(fs, fs->super);
+ if (retval)
+ return retval;
#ifdef WORDS_BIGENDIAN
*super_shadow = *fs->super;
ext2fs_swap_super(super_shadow);
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index aec5c87..d89fa37 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,40 @@
#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);
+
+#ifdef WORDS_BIGENDIAN
+ struct ext2_super_block swabsb;
+ memcpy(&swabsb, sb, sizeof(struct ext2_super_block));
+ ext2fs_swap_super(&swabsb);
+ sb = &swabsb;
+#endif
+
+ return ext2fs_crc32c_le(~0, (unsigned char *)sb, offset);
+}
+
+int ext2fs_superblock_csum_verify(ext2_filsys fs, struct ext2_super_block *sb)
+{
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ (sb->s_checksum != ext2fs_superblock_csum(fs, sb)))
+ return 0;
+ return 1;
+}
+
+errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
+ struct ext2_super_block *sb)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ sb->s_checksum = ext2fs_superblock_csum(fs, sb);
+ 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 e210f8d..e4d2caa 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -455,4 +455,7 @@ ec EXT2_ET_INODE_CSUM_NONZERO,
ec EXT2_ET_EXT_ATTR_BLOCK_CORRUPT,
"Extended attribute block fails checksum"
+ec EXT2_ET_SB_CSUM_INVALID,
+ "Superblock fails checksum"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 9f0e8fa..b921f83 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -929,6 +929,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 0cefe3f..334240c 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -203,6 +203,12 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
}
#endif
+ 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->super->s_magic != EXT2_SUPER_MAGIC) {
retval = EXT2_ET_BAD_MAGIC;
goto cleanup;
Whenever we are calculating a checksum for a piece of metadata that is
associated with an inode, incorporate i_generation into that calculation so
that old metadata blocks cannot be re-associated after a delete/create cycle.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/csum.c | 45 +++++++++++++++++++++++++++++++++++++--------
lib/ext2fs/mkdir.c | 4 ++--
2 files changed, 39 insertions(+), 10 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index d89fa37..e2c32f4 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -71,7 +71,8 @@ static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum,
{
errcode_t retval = 0;
char *buf = (char *)hdr;
- __u32 ncrc, old_crc = hdr->h_checksum;
+ __u32 gen, ncrc, old_crc = hdr->h_checksum;
+ struct ext2_inode inode;
hdr->h_checksum = 0;
#ifdef WORDS_BIGENDIAN
@@ -88,14 +89,21 @@ static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum,
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&block,
sizeof(block));
} else {
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ goto out;
inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum,
sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen,
+ sizeof(gen));
}
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, fs->blocksize);
hdr->h_checksum = old_crc;
*crc = ncrc;
+out:
#ifdef WORDS_BIGENDIAN
ext2fs_free_mem(&buf);
#endif
@@ -190,7 +198,8 @@ static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum,
{
errcode_t retval = 0;
char *buf = (char *)dirent;
- __u32 ncrc;
+ __u32 ncrc, gen;
+ struct ext2_inode inode;
#ifdef WORDS_BIGENDIAN
retval = ext2fs_get_mem(fs->blocksize, &buf);
@@ -202,15 +211,20 @@ static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum,
goto out;
#endif
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ goto out;
inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen, sizeof(gen));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, size);
*crc = ncrc;
-#ifdef WORDS_BIGENDIAN
out:
+#ifdef WORDS_BIGENDIAN
ext2fs_free_mem(&buf);
#endif
@@ -270,7 +284,8 @@ static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
errcode_t retval = 0;
char *buf = (char *)dirent;
int size;
- __u32 ncrc;
+ __u32 ncrc, gen;
+ struct ext2_inode inode;
size = count_offset + (count * sizeof(struct ext2_dx_entry));
@@ -289,15 +304,20 @@ static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
goto out;
#endif
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ goto out;
inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen, sizeof(gen));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)buf, size);
*crc = ncrc;
-#ifdef WORDS_BIGENDIAN
out:
+#ifdef WORDS_BIGENDIAN
ext2fs_free_mem(&buf);
#endif
@@ -409,19 +429,26 @@ static errcode_t ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum,
__u32 *crc)
{
int size;
- __u32 ncrc;
- errcode_t retval = 0;
+ __u32 ncrc, gen;
+ errcode_t retval;
+ struct ext2_inode inode;
size = EXT3_EXTENT_TAIL_OFFSET(eh) + offsetof(struct ext3_extent_tail,
et_checksum);
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ goto out;
inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen, sizeof(gen));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)eh, size);
*crc = ncrc;
+out:
return retval;
}
@@ -566,7 +593,7 @@ static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
struct ext2_inode_large *inode,
__u32 *crc)
{
- __u32 ncrc;
+ __u32 ncrc, gen;
struct ext2_inode_large *desc = inode;
size_t size = fs->super->s_inode_size;
__u16 old_lo;
@@ -594,9 +621,11 @@ static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
#endif
inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode->i_generation);
ncrc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&inum, sizeof(inum));
+ ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)&gen, sizeof(gen));
ncrc = ext2fs_crc32c_le(ncrc, (unsigned char *)desc, size);
*crc = ncrc;
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index 0d1b7b8..e4c7d05 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -95,10 +95,10 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
/*
* Write out the inode and inode data block
*/
- retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
if (retval)
goto cleanup;
- retval = ext2fs_write_new_inode(fs, ino, &inode);
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
if (retval)
goto cleanup;
Verify the revoke block checksum when recovering the journal.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/recovery.c | 37 +++++++++++++++++++++++++++++++++++--
lib/ext2fs/kernel-jbd.h | 4 ++++
2 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index b669941..8979193 100644
--- a/e2fsck/recovery.c
+++ b/e2fsck/recovery.c
@@ -674,8 +674,17 @@ static int do_one_pass(journal_t *journal,
err = scan_revoke_records(journal, bh,
next_commit_ID, info);
brelse(bh);
- if (err)
- goto failed;
+ if (err) {
+ if (err != -EINVAL)
+ goto failed;
+ /*
+ * Ignoring corrupt revoke blocks is safe
+ * because at worst it results in unnecessary
+ * writes during recovery.
+ */
+ jbd_debug(3, "Skipping corrupt revoke "
+ "block.\n");
+ }
continue;
default:
@@ -715,6 +724,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 +759,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;
diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index 08479d3..187cb38 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -157,6 +157,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 */
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 6e9f399..45bab5c 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -2398,6 +2398,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);
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 b2c9e32..708bc20 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -700,6 +700,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);
}
/*
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 45bab5c..fc57372 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -865,7 +865,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|
@@ -2278,6 +2279,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 76a13de..f0b92ec 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 has invalid MMP checksum. "),
+ 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 fdb7f99..da6cd64 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 1d81762..423cc27 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1084,6 +1084,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;
}
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 | 35 +++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2_err.et.in | 3 +++
lib/ext2fs/ext2_fs.h | 3 ++-
lib/ext2fs/ext2fs.h | 3 +++
lib/ext2fs/mmp.c | 20 ++++++++++++++++++--
lib/ext2fs/swapfs.c | 1 +
8 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index d71de7d..693db62 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -2216,6 +2216,7 @@ void do_dump_mmp(int argc, char *argv[])
fprintf(stdout, "time: %lld -- %s", mmp_s->mmp_time, ctime(&t));
fprintf(stdout, "node_name: %s\n", mmp_s->mmp_nodename);
fprintf(stdout, "device_name: %s\n", mmp_s->mmp_bdevname);
+ 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 0103497..0daea66 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -753,6 +753,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 },
};
void do_set_mmp_value(int argc, char *argv[])
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 9694185..1429d9f 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -30,6 +30,41 @@
#define STATIC static
#endif
+static __u32 ext2fs_mmp_csum(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ int offset = offsetof(struct mmp_struct, mmp_checksum);
+ __u32 crc;
+
+#ifdef WORDS_BIGENDIAN
+ struct mmp_struct swabmmp;
+ memcpy(&swabmmp, mmp, sizeof(struct mmp_struct));
+ ext2fs_swap_mmp(&swabmmp);
+ mmp = &swabmmp;
+#endif
+ crc = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ return ext2fs_crc32c_le(crc, (unsigned char *)mmp, offset);
+}
+
+int ext2fs_mmp_csum_verify(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ (mmp->mmp_checksum != ext2fs_mmp_csum(fs, mmp)))
+ return 0;
+ return 1;
+}
+
+errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ return 0;
+
+ mmp->mmp_checksum = ext2fs_mmp_csum(fs, mmp);
+ 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 3b92eac..403bc61 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -461,4 +461,7 @@ ec EXT2_ET_SB_CSUM_INVALID,
ec EXT2_ET_UNKNOWN_CSUM,
"Unknown checksum algorithm"
+ec EXT2_ET_MMP_CSUM_INVALID,
+ "MMP: invalid checksum"
+
end
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 709fc2f..ef0a288 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -876,7 +876,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 d4d4ecf..ff7ee7b 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -931,6 +931,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);
@@ -1420,6 +1422,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 91f4fb2..ea2e913 100644
--- a/lib/ext2fs/mmp.c
+++ b/lib/ext2fs/mmp.c
@@ -103,6 +103,12 @@ errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf)
goto out;
}
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_mmp_csum_verify(fs, mmp_cmp)) {
+ retval = EXT2_ET_MMP_CSUM_INVALID;
+ goto out;
+ }
+
out:
return retval;
}
@@ -121,6 +127,10 @@ errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf)
fs->super->s_mmp_block > ext2fs_blocks_count(fs->super))
return EXT2_ET_MMP_BAD_BLOCK;
+ retval = ext2fs_mmp_csum_set(fs, mmp_s);
+ if (retval)
+ return retval;
+
#ifdef WORDS_BIGENDIAN
ext2fs_swap_mmp(mmp_s);
#endif
@@ -381,7 +391,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 +402,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 +423,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 ++++++++++++++++++++++++++++++++++++++-
lib/ext2fs/kernel-jbd.h | 5 +++++
2 files changed, 43 insertions(+), 1 deletions(-)
diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index 8979193..a192378 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;
diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index 187cb38..b79eed5 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -147,6 +147,11 @@ typedef struct journal_block_tag_s
#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
Change the block group algorithm to use the same algorithm as the rest of the
metadata_csum, if a certain incompat feature flag is set.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/e2p/feature.c | 2 ++
lib/ext2fs/csum.c | 62 ++++++++++++++++++++++++++++++++++----------------
lib/ext2fs/ext2_fs.h | 1 +
lib/ext2fs/ext2fs.h | 6 +++--
4 files changed, 49 insertions(+), 22 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 9e4536e..9694185 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -711,32 +711,54 @@ STATIC __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group)
desc = ext2fs_group_desc(fs, fs->group_desc, group);
- if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
- size_t offset = offsetof(struct ext2_group_desc, bg_checksum);
-
#ifdef WORDS_BIGENDIAN
- struct ext4_group_desc swabdesc;
+ struct ext4_group_desc swabdesc;
- /* Have to swab back to little-endian to do the checksum */
- memcpy(&swabdesc, desc, size);
- ext2fs_swap_group_desc2(fs,
- (struct ext2_group_desc *) &swabdesc);
- desc = (struct ext2_group_desc *) &swabdesc;
+ /* Have to swab back to little-endian to do the checksum */
+ memcpy(&swabdesc, desc, size);
+ ext2fs_swap_group_desc2(fs,
+ (struct ext2_group_desc *) &swabdesc);
+ desc = (struct ext2_group_desc *) &swabdesc;
- group = ext2fs_swab32(group);
+ group = ext2fs_swab32(group);
#endif
- crc = ext2fs_crc16(~0, fs->super->s_uuid,
- sizeof(fs->super->s_uuid));
- crc = ext2fs_crc16(crc, &group, sizeof(group));
- crc = ext2fs_crc16(crc, desc, offset);
- offset += sizeof(desc->bg_checksum); /* skip checksum */
- /* for checksum of struct ext4_group_desc do the rest...*/
- if (offset < size) {
- crc = ext2fs_crc16(crc, (char *)desc + offset,
- size - offset);
- }
+
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM)) {
+ /* new metadata csum code */
+ __u16 old_crc;
+ __u32 crc32;
+
+ old_crc = desc->bg_checksum;
+ desc->bg_checksum = 0;
+ crc32 = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ crc32 = ext2fs_crc32c_le(crc32, (unsigned char *)&group,
+ sizeof(group));
+ crc32 = ext2fs_crc32c_le(crc32, (unsigned char *)desc,
+ size);
+ desc->bg_checksum = old_crc;
+
+ crc = crc32 & 0xFFFF;
+ goto out;
}
+ /* old crc16 code */
+ size_t offset = offsetof(struct ext2_group_desc, bg_checksum);
+ crc = ext2fs_crc16(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+ crc = ext2fs_crc16(crc, &group, sizeof(group));
+ crc = ext2fs_crc16(crc, desc, offset);
+ offset += sizeof(desc->bg_checksum); /* skip checksum */
+ /* for checksum of struct ext4_group_desc do the rest...*/
+ if (offset < size) {
+ crc = ext2fs_crc16(crc, (char *)desc + offset,
+ size - offset);
+ }
+
+out:
return crc;
}
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index d6cd6e0..709fc2f 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -742,6 +742,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| \
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 9311336..d4d4ecf 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -567,7 +567,8 @@ typedef struct ext2_icount *ext2_icount_t;
EXT3_FEATURE_INCOMPAT_EXTENTS|\
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
EXT4_FEATURE_INCOMPAT_MMP|\
- EXT4_FEATURE_INCOMPAT_64BIT)
+ EXT4_FEATURE_INCOMPAT_64BIT|\
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM)
#else
#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
@@ -576,7 +577,8 @@ typedef struct ext2_icount *ext2_icount_t;
EXT3_FEATURE_INCOMPAT_EXTENTS|\
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
EXT4_FEATURE_INCOMPAT_MMP|\
- EXT4_FEATURE_INCOMPAT_64BIT)
+ EXT4_FEATURE_INCOMPAT_64BIT|\
+ EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM)
#endif
#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
Add the necessary structure fields, flags, and constants that are needed to
extend checksum coverage to all journal metadata blocks.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/e2p/feature.c | 2 ++
lib/ext2fs/kernel-jbd.h | 12 ++++++++----
misc/dumpe2fs.c | 29 +++++++++++++++++++++++++++++
3 files changed, 39 insertions(+), 4 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/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index 066c031..08479d3 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -114,6 +114,7 @@ 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
@@ -205,7 +206,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,18 +228,18 @@ typedef struct journal_superblock_s
#define JFS_FEATURE_COMPAT_CHECKSUM 0x00000001
-#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001
-
#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001
#define JFS_FEATURE_INCOMPAT_64BIT 0x00000002
#define JFS_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004
+#define JFS_FEATURE_INCOMPAT_CSUM_V2 0x00000008
/* Features known to this kernel version: */
#define JFS_KNOWN_COMPAT_FEATURES 0
#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)
#ifdef __KERNEL__
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 33c0933..a9cec88 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -313,6 +313,16 @@ static void list_bad_blocks(ext2_filsys fs, int dump)
ext2fs_badblocks_list_free(bb_list);
}
+static char *journal_checksum_type_str(__u8 type)
+{
+ switch (type) {
+ case JBD2_CRC32C_CHKSUM:
+ return "crc32c";
+ default:
+ return "unknown";
+ }
+}
+
static void print_inline_journal_information(ext2_filsys fs)
{
journal_superblock_t *jsb;
@@ -379,6 +389,15 @@ static void print_inline_journal_information(ext2_filsys fs)
(unsigned int)ntohl(jsb->s_maxlen),
(unsigned int)ntohl(jsb->s_sequence),
(unsigned int)ntohl(jsb->s_start));
+ if (jsb->s_feature_compat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM))
+ printf(_("Journal checksum type: crc32\n"));
+ if (jsb->s_feature_incompat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2))
+ printf(_("Journal checksum type: %s\n"
+ "Journal checksum: 0x%08x\n"),
+ journal_checksum_type_str(jsb->s_checksum_type),
+ ext2fs_be32_to_cpu(jsb->s_checksum));
}
static void print_journal_information(ext2_filsys fs)
@@ -404,6 +423,16 @@ static void print_journal_information(ext2_filsys fs)
exit(1);
}
+ if (jsb->s_feature_compat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM))
+ printf(_("Journal checksum type: crc32\n"));
+ if (jsb->s_feature_incompat &
+ ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2))
+ printf(_("Journal checksum type: %s\n"
+ "Journal checksum: 0x%08x\n"),
+ journal_checksum_type_str(jsb->s_checksum_type),
+ ext2fs_be32_to_cpu(jsb->s_checksum));
+
printf(_("\nJournal block size: %u\n"
"Journal length: %u\n"
"Journal first block: %u\n"
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);
}
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 | 31 +++++++++++++++++++++++++++++++
3 files changed, 63 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 beafacc..5cda381 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 c650582..a86150c 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -153,6 +153,7 @@ 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))
@@ -261,6 +262,36 @@ typedef struct journal_superblock_s
JFS_FEATURE_INCOMPAT_64BIT|\
JFS_FEATURE_INCOMPAT_CSUM_V2)
+#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>
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 4f02905..ad7ed95 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -705,6 +705,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,
When recovering a journal with checksum v2, verify the commit block checksum.
Signed-off-by: Darrick J. Wong <[email protected]>
---
e2fsck/recovery.c | 33 +++++++++++++++++++++++++++++++++
lib/ext2fs/kernel-jbd.h | 11 +++++++++++
2 files changed, 44 insertions(+), 0 deletions(-)
diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index a192378..beafacc 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;
diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index b79eed5..c650582 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -121,6 +121,17 @@ typedef struct journal_header_s
#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;