diff options
87 files changed, 3725 insertions, 1781 deletions
diff --git a/RELEASE-NOTES b/RELEASE-NOTES index ebd345a7..20470d7f 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,3 +1,23 @@ +E2fsprogs 1.43-WIP (September 22, 2012) +======================================= + +Add support for the ext4 metadata checksum feature. + +Check to make sure file system features which can not be supported by +HURD are not enabled if the file system is created to be +HURD-compatible. + + +Programmer's Notes +------------------ + +Reduce the use of libc functions in libext2fs that may not be present +in the boot loader environment, at least for those functions that are +needed by boot loadsers such as yaboot. + +Support for the MMP feature can now be disabled at compile time. + + E2fsprogs 1.42.7 (January 21, 2013) =================================== @@ -850,6 +850,9 @@ enable_fsck enable_e2initrd_helper enable_tls enable_uuidd +enable_mmp +enable_bmap_stats +enable_bmap_stats_ops enable_nls with_gnu_ld enable_rpath @@ -1505,6 +1508,9 @@ Optional Features: --enable-e2initrd-helper build e2initrd-helper program --disable-tls disable use of thread local support --disable-uuidd disable building the uuid daemon + --disable-mmp disable support mmp, Multi Mount Protection + --disable-bmap-stats disable collection of bitmap stats. + --enable-bmap-stats-ops enable collection of additional bitmap stats --disable-nls do not use Native Language Support --disable-rpath do not hardcode runtime library paths @@ -5575,6 +5581,77 @@ $as_echo "Building uuidd by default" >&6; } fi + +# Check whether --enable-mmp was given. +if test "${enable_mmp+set}" = set; then : + enableval=$enable_mmp; if test "$enableval" = "no" +then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Disabling mmp support" >&5 +$as_echo "Disabling mmp support" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling mmp support" >&5 +$as_echo "Enabling mmp support" >&6; } + $as_echo "#define CONFIG_MMP 1" >>confdefs.h + +fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling mmp support by default" >&5 +$as_echo "Enabling mmp support by default" >&6; } +$as_echo "#define CONFIG_MMP 1" >>confdefs.h + + +fi + + +# Check whether --enable-bmap-stats was given. +if test "${enable_bmap_stats+set}" = set; then : + enableval=$enable_bmap_stats; if test "$enableval" = "no" +then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Disabling bitmap statistics support" >&5 +$as_echo "Disabling bitmap statistics support" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling bitmap statistics support" >&5 +$as_echo "Enabling bitmap statistics support" >&6; } + $as_echo "#define ENABLE_BMAP_STATS 1" >>confdefs.h + +fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling bitmap statistics support by default" >&5 +$as_echo "Enabling bitmap statistics support by default" >&6; } +$as_echo "#define ENABLE_BMAP_STATS 1" >>confdefs.h + + +fi + + +# Check whether --enable-bmap-stats-ops was given. +if test "${enable_bmap_stats_ops+set}" = set; then : + enableval=$enable_bmap_stats_ops; if test "$enableval" = "no" +then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Disabling additional bitmap statistics" >&5 +$as_echo "Disabling additional bitmap statistics" >&6; } +else + if test "x${enable_bmap_stats}" = "xno"; then : + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Error --enable-bmap-stats-ops requires bmap-stats +See \`config.log' for more details" "$LINENO" 5; } +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Enabling additional bitmap statistics" >&5 +$as_echo "Enabling additional bitmap statistics" >&6; } + $as_echo "#define ENABLE_BMAP_STATS_OPS 1" >>confdefs.h + +fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Disabling additional bitmap statistics by default" >&5 +$as_echo "Disabling additional bitmap statistics by default" >&6; } + +fi + MAKEFILE_LIBRARY=$srcdir/lib/Makefile.library GETTEXT_PACKAGE=e2fsprogs diff --git a/configure.in b/configure.in index c3687bb8..181faeb3 100644 --- a/configure.in +++ b/configure.in @@ -761,6 +761,60 @@ AC_MSG_RESULT([Building uuidd by default]) ) AC_SUBST(UUIDD_CMT) dnl +dnl handle --disable-mmp +dnl +AH_TEMPLATE([CONFIG_MMP], [Define to 1 to enable mmp support]) +AC_ARG_ENABLE([mmp], +[ --disable-mmp disable support mmp, Multi Mount Protection], +if test "$enableval" = "no" +then + AC_MSG_RESULT([Disabling mmp support]) +else + AC_MSG_RESULT([Enabling mmp support]) + AC_DEFINE(CONFIG_MMP, 1) +fi +, +AC_MSG_RESULT([Enabling mmp support by default]) +AC_DEFINE(CONFIG_MMP, 1) +) +dnl +dnl handle --disable-bmap-stats +dnl +AH_TEMPLATE([ENABLE_BMAP_STATS], [Define to 1 to enable bitmap stats.]) +AC_ARG_ENABLE([bmap-stats], +[ --disable-bmap-stats disable collection of bitmap stats.], +if test "$enableval" = "no" +then + AC_MSG_RESULT([Disabling bitmap statistics support]) +else + AC_MSG_RESULT([Enabling bitmap statistics support]) + AC_DEFINE(ENABLE_BMAP_STATS, 1) +fi +, +AC_MSG_RESULT([Enabling bitmap statistics support by default]) +AC_DEFINE(ENABLE_BMAP_STATS, 1) +) +dnl +dnl handle --enable-bmap-stats-ops +dnl +AH_TEMPLATE([ENABLE_BMAP_STATS_OPS], [Define to 1 to enable bitmap stats.]) +AC_ARG_ENABLE([bmap-stats-ops], +[ --enable-bmap-stats-ops enable collection of additional bitmap stats], +if test "$enableval" = "no" +then + AC_MSG_RESULT([Disabling additional bitmap statistics]) +else + dnl There has to be a better way! + AS_IF([test "x${enable_bmap_stats}" = "xno"], + AC_MSG_FAILURE([Error --enable-bmap-stats-ops requires bmap-stats])) + + AC_MSG_RESULT([Enabling additional bitmap statistics]) + AC_DEFINE(ENABLE_BMAP_STATS_OPS, 1) +fi +, +AC_MSG_RESULT([Disabling additional bitmap statistics by default]) +) +dnl dnl dnl MAKEFILE_LIBRARY=$srcdir/lib/Makefile.library diff --git a/debian/changelog b/debian/changelog index 3d0463ec..bb075d5b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -53,6 +53,12 @@ e2fsprogs (1.42.7-1) unstable; urgency=low -- Theodore Y. Ts'o <tytso@mit.edu> Tue, 21 Jan 2013 21:52:58 -0500 +e2fsprogs (1.43~WIP-2012-09-22-1) unstable; urgency=low + + * Add metadata checksum feature + + -- Theodore Y. Ts'o <tytso@mit.edu> Sat, 22 Sep 2012 21:50:20 -0400 + e2fsprogs (1.42.6-1) unstable; urgency=low * New upstream version diff --git a/debian/e2fslibs.symbols b/debian/e2fslibs.symbols index b8eea529..6464a1bd 100644 --- a/debian/e2fslibs.symbols +++ b/debian/e2fslibs.symbols @@ -46,6 +46,7 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_add_journal_device@Base 1.37 ext2fs_add_journal_inode@Base 1.37 ext2fs_adjust_ea_refcount2@Base 1.42 + ext2fs_adjust_ea_refcount3@Base 1.43~WIP-2012-08-01 ext2fs_adjust_ea_refcount@Base 1.37 ext2fs_alloc_block2@Base 1.42 ext2fs_alloc_block@Base 1.37 @@ -87,6 +88,9 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_blkmap64_rbtree@Base 1.42.1 ext2fs_block_alloc_stats2@Base 1.42 ext2fs_block_alloc_stats@Base 1.37 + ext2fs_block_bitmap_checksum@Base 1.43~WIP-2012-08-01 + ext2fs_block_bitmap_csum_set@Base 1.43~WIP-2012-08-01 + ext2fs_block_bitmap_csum_verify@Base 1.43~WIP-2012-08-01 ext2fs_block_bitmap_loc@Base 1.42 ext2fs_block_bitmap_loc_set@Base 1.42 ext2fs_block_iterate2@Base 1.37 @@ -120,7 +124,9 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_copy_generic_bitmap@Base 1.41.0 ext2fs_copy_generic_bmap@Base 1.42 ext2fs_crc16@Base 1.41.1 - ext2fs_crc32c_be@Base 1.42 + ext2fs_crc32_be@Base 1.43~WIP-2012-08-01 +#Removed in e2fsprogs 1.43 +#ext2fs_crc32c_be@Base 1.42 ext2fs_crc32c_le@Base 1.42 ext2fs_create_icount2@Base 1.37 ext2fs_create_icount@Base 1.37 @@ -140,14 +146,22 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_default_journal_size@Base 1.40 ext2fs_descriptor_block_loc2@Base 1.42 ext2fs_descriptor_block_loc@Base 1.37 + ext2fs_dir_block_csum_set@Base 1.43~WIP-2012-08-01 + ext2fs_dir_block_csum_verify@Base 1.43~WIP-2012-08-01 ext2fs_dir_iterate2@Base 1.37 ext2fs_dir_iterate@Base 1.37 + ext2fs_dirent_csum_verify@Base 1.43~WIP-2012-08-01 + ext2fs_dirent_has_tail@Base 1.43~WIP-2012-08-01 ext2fs_dirhash@Base 1.37 ext2fs_div64_ceil@Base 1.42 ext2fs_div_ceil@Base 1.40 ext2fs_dup_handle@Base 1.37 ext2fs_expand_dir@Base 1.37 + ext2fs_ext_attr_block_csum_set@Base 1.43~WIP-2012-08-01 + ext2fs_ext_attr_block_csum_verify@Base 1.43~WIP-2012-08-01 ext2fs_ext_attr_hash_entry@Base 1.41.0 + ext2fs_extent_block_csum_set@Base 1.43~WIP-2012-08-01 + ext2fs_extent_block_csum_verify@Base 1.43~WIP-2012-08-01 ext2fs_extent_delete@Base 1.41.0 ext2fs_extent_fix_parents@Base 1.42.7 ext2fs_extent_free@Base 1.41.0 @@ -243,6 +257,7 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_get_device_size2@Base 1.41.4 ext2fs_get_device_size@Base 1.37 ext2fs_get_dio_alignment@Base 1.42.3 + ext2fs_get_dx_countlimit@Base 1.43~WIP-2012-08-01 ext2fs_get_free_blocks2@Base 1.42 ext2fs_get_free_blocks@Base 1.37 ext2fs_get_generic_bitmap_end@Base 1.41.0 @@ -293,12 +308,19 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_image_inode_write@Base 1.37 ext2fs_image_super_read@Base 1.37 ext2fs_image_super_write@Base 1.37 + ext2fs_init_csum_seed@Base 1.43~WIP-2012-08-01 ext2fs_init_dblist@Base 1.37 ext2fs_initialize@Base 1.37 + ext2fs_initialize_dirent_tail@Base 1.43~WIP-2012-08-01 ext2fs_inode_alloc_stats2@Base 1.37 ext2fs_inode_alloc_stats@Base 1.37 + ext2fs_inode_bitmap_checksum@Base 1.43~WIP-2012-08-01 + ext2fs_inode_bitmap_csum_set@Base 1.43~WIP-2012-08-01 + ext2fs_inode_bitmap_csum_verify@Base 1.43~WIP-2012-08-01 ext2fs_inode_bitmap_loc@Base 1.42 ext2fs_inode_bitmap_loc_set@Base 1.42 + ext2fs_inode_csum_set@Base 1.43~WIP-2012-08-01 + ext2fs_inode_csum_verify@Base 1.43~WIP-2012-08-01 ext2fs_inode_data_blocks2@Base 1.42 ext2fs_inode_data_blocks@Base 1.37 ext2fs_inode_has_valid_blocks@Base 1.37 @@ -330,11 +352,14 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_mem_is_zero@Base 1.42 ext2fs_mkdir@Base 1.37 ext2fs_mmp_clear@Base 1.42 + ext2fs_mmp_csum_set@Base 1.43~WIP-2012-08-01 + ext2fs_mmp_csum_verify@Base 1.43~WIP-2012-08-01 ext2fs_mmp_init@Base 1.42 ext2fs_mmp_new_seq@Base 1.42 ext2fs_mmp_read@Base 1.42 ext2fs_mmp_start@Base 1.42 ext2fs_mmp_stop@Base 1.42 + ext2fs_mmp_update2@Base 1.43~WIP-2012-08-01 ext2fs_mmp_update@Base 1.42 ext2fs_mmp_write@Base 1.42 ext2fs_namei@Base 1.37 @@ -346,6 +371,7 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_new_inode@Base 1.37 ext2fs_numeric_progress_close@Base 1.42 ext2fs_numeric_progress_init@Base 1.42 + ext2fs_numeric_progress_ops@Base 1.43~WIP-2012-08-01 ext2fs_numeric_progress_update@Base 1.42 ext2fs_open2@Base 1.37 ext2fs_open@Base 1.37 @@ -375,8 +401,10 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_read_block_bitmap@Base 1.37 ext2fs_read_dir_block2@Base 1.37 ext2fs_read_dir_block3@Base 1.42 + ext2fs_read_dir_block4@Base 1.43~WIP-2012-08-01 ext2fs_read_dir_block@Base 1.37 ext2fs_read_ext_attr2@Base 1.42 + ext2fs_read_ext_attr3@Base 1.43~WIP-2012-08-01 ext2fs_read_ext_attr@Base 1.37 ext2fs_read_ind_block@Base 1.37 ext2fs_read_inode@Base 1.37 @@ -413,6 +441,8 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_stat@Base 1.42 ext2fs_super_and_bgd_loc2@Base 1.42 ext2fs_super_and_bgd_loc@Base 1.37 + ext2fs_superblock_csum_set@Base 1.43~WIP-2012-08-01 + ext2fs_superblock_csum_verify@Base 1.43~WIP-2012-08-01 ext2fs_swab16@Base 1.37 ext2fs_swab32@Base 1.37 ext2fs_swab64@Base 1.40 @@ -509,6 +539,7 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_unmark_valid@Base 1.37 ext2fs_update_bb_inode@Base 1.37 ext2fs_update_dynamic_rev@Base 1.37 + ext2fs_verify_csum_type@Base 1.43~WIP-2012-08-01 ext2fs_warn_bitmap2@Base 1.37 ext2fs_warn_bitmap32@Base 1.42 ext2fs_warn_bitmap@Base 1.37 @@ -517,8 +548,10 @@ libext2fs.so.2 e2fslibs #MINVER# ext2fs_write_block_bitmap@Base 1.37 ext2fs_write_dir_block2@Base 1.37 ext2fs_write_dir_block3@Base 1.42 + ext2fs_write_dir_block4@Base 1.43~WIP-2012-08-01 ext2fs_write_dir_block@Base 1.37 ext2fs_write_ext_attr2@Base 1.42 + ext2fs_write_ext_attr3@Base 1.43~WIP-2012-08-01 ext2fs_write_ext_attr@Base 1.37 ext2fs_write_ind_block@Base 1.37 ext2fs_write_inode@Base 1.37 diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in index e563b0db..65a30d53 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 @@ -404,13 +409,16 @@ and .I \-b options. .TP -.I ls [-l] [-d] [-p] filespec +.I ls [-l] [-c] [-d] [-p] filespec Print a listing of the files in the directory .IR filespec . The .I \-l flag will list files using a more verbose format. The +.I \-c +flag causes directory block checksums (if present) to be displayed. +The .I \-d flag will list deleted entries in the directory. The diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c index 2a1525a8..969dbe00 100644 --- a/debugfs/debugfs.c +++ b/debugfs/debugfs.c @@ -356,8 +356,7 @@ void do_show_super_stats(int argc, char *argv[]) return; } - gdt_csum = EXT2_HAS_RO_COMPAT_FEATURE(current_fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + gdt_csum = ext2fs_has_group_desc_csum(current_fs); for (i = 0; i < current_fs->group_desc_count; i++) { fprintf(out, " Group %2d: block bitmap at %llu, " "inode bitmap at %llu, " @@ -802,6 +801,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); @@ -2234,6 +2246,7 @@ try_again: void do_dump_mmp(int argc EXT2FS_ATTR((unused)), char *argv[]) { +#if CONFIG_MMP struct ext2_super_block *sb; struct mmp_struct *mmp_s; time_t t; @@ -2277,6 +2290,11 @@ void do_dump_mmp(int argc EXT2FS_ATTR((unused)), char *argv[]) fprintf(stdout, "node_name: %s\n", mmp_s->mmp_nodename); fprintf(stdout, "device_name: %s\n", mmp_s->mmp_bdevname); fprintf(stdout, "magic: 0x%x\n", mmp_s->mmp_magic); + fprintf(stdout, "checksum: 0x%08x\n", mmp_s->mmp_checksum); +#else + fprintf(stdout, "MMP is unsupported, please recompile with " + "--enable-mmp\n"); +#endif } static int source_file(const char *cmd_file, int ss_idx) @@ -2341,9 +2359,9 @@ int main(int argc, char **argv) int catastrophic = 0; char *data_filename = 0; #ifdef READ_ONLY - const char *opt_string = "icR:f:b:s:Vd:D"; + const char *opt_string = "nicR:f:b:s:Vd:D"; #else - const char *opt_string = "iwcR:f:b:s:Vd:D"; + const char *opt_string = "niwcR:f:b:s:Vd:D"; #endif if (debug_prog_name == 0) @@ -2370,6 +2388,9 @@ int main(int argc, char **argv) case 'i': open_flags |= EXT2_FLAG_IMAGE_FILE; break; + case 'n': + open_flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; + break; #ifndef READ_ONLY case 'w': open_flags |= EXT2_FLAG_RW; diff --git a/debugfs/htree.c b/debugfs/htree.c index 24f8250c..d94dbeae 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, } fprintf(pager, "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", @@ -91,8 +96,23 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino, if (errcode) com_err("htree_dump_leaf_node", errcode, "while calculating hash"); - snprintf(tmp, EXT2_NAME_LEN + 64, "%u 0x%08x-%08x (%d) %s ", - dirent->inode, hash, minor_hash, rec_len, name); + if ((offset == fs->blocksize - csum_size) && + (dirent->inode == 0) && + (dirent->rec_len == csum_size) && + (dirent->name_len == EXT2_DIR_NAME_LEN_CSUM)) { + struct ext2_dir_entry_tail *t; + + t = (struct ext2_dir_entry_tail *) dirent; + + snprintf(tmp, EXT2_NAME_LEN + 64, + "leaf block checksum: 0x%08x ", + t->det_checksum); + } else { + snprintf(tmp, EXT2_NAME_LEN + 64, + "%u 0x%08x-%08x (%d) %s ", + dirent->inode, hash, minor_hash, + rec_len, name); + } thislen = strlen(tmp); if (col + thislen > 80) { fprintf(pager, "\n"); @@ -120,8 +140,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 +151,20 @@ static void htree_dump_int_node(ext2_filsys fs, ext2_ino_t ino, fprintf(pager, "Number of entries (count): %d\n", limit.count); fprintf(pager, "Number of entries (limit): %d\n", limit.limit); + remainder = fs->blocksize - (limit.limit * + sizeof(struct ext2_dx_entry)); + if (ent == (struct ext2_dx_entry *)(rootnode + 1)) + remainder -= sizeof(struct ext2_dx_root_info) + 24; + else + remainder -= 8; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && + remainder == sizeof(struct ext2_dx_tail)) { + tail = (struct ext2_dx_tail *)(ent + limit.limit); + fprintf(pager, "Checksum: 0x%08x\n", + ext2fs_le32_to_cpu(tail->dt_checksum)); + } + for (i=0; i < limit.count; i++) { hash = i ? ext2fs_le32_to_cpu(ent[i].hash) : 0; fprintf(pager, "Entry #%d: Hash 0x%08x%s, block %u\n", i, diff --git a/debugfs/logdump.c b/debugfs/logdump.c index 6e39b74c..4a09bdb9 100644 --- a/debugfs/logdump.c +++ b/debugfs/logdump.c @@ -501,7 +501,7 @@ static void dump_descriptor_block(FILE *out_file, break; tag_block = be32_to_cpu(tag->t_blocknr); - tag_flags = be32_to_cpu(tag->t_flags); + tag_flags = be16_to_cpu(tag->t_flags); if (!(tag_flags & JFS_FLAG_SAME_UUID)) offset += 16; diff --git a/debugfs/ls.c b/debugfs/ls.c index b4036de0..032d12d8 100644 --- a/debugfs/ls.c +++ b/debugfs/ls.c @@ -30,8 +30,7 @@ extern char *optarg; */ #define LONG_OPT 0x0001 -#define DELETED_OPT 0x0002 -#define PARSE_OPT 0x0004 +#define PARSE_OPT 0x0002 struct list_dir_struct { FILE *f; @@ -60,6 +59,7 @@ static int list_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), char lbr, rbr; int thislen; struct list_dir_struct *ls = (struct list_dir_struct *) private; + struct ext2_dir_entry_tail *t = (struct ext2_dir_entry_tail *) dirent; thislen = dirent->name_len & 0xFF; strncpy(name, dirent->name, thislen); @@ -99,8 +99,13 @@ static int list_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), strcpy(datestr, " "); memset(&inode, 0, sizeof(struct ext2_inode)); } - fprintf(ls->f, "%c%6u%c %6o (%d) %5d %5d ", lbr, ino, rbr, - inode.i_mode, dirent->name_len >> 8, + fprintf(ls->f, "%c%6u%c %6o ", lbr, ino, rbr, inode.i_mode); + if (entry == DIRENT_CHECKSUM) { + fprintf(ls->f, "(dirblock checksum: 0x%08x)\n", + t->det_checksum); + return 0; + } + fprintf(ls->f, "(%d) %5d %5d ", dirent->name_len >> 8, inode_uid(inode), inode_gid(inode)); if (LINUX_S_ISDIR(inode.i_mode)) fprintf(ls->f, "%5d", inode.i_size); @@ -108,8 +113,13 @@ static int list_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), fprintf(ls->f, "%5llu", EXT2_I_SIZE(&inode)); fprintf (ls->f, " %s %s\n", datestr, name); } else { - sprintf(tmp, "%c%u%c (%d) %s ", lbr, dirent->inode, rbr, - dirent->rec_len, name); + if (entry == DIRENT_CHECKSUM) + sprintf(tmp, "%c%u%c (dirblock checksum: 0x%08x) ", + lbr, dirent->inode, rbr, t->det_checksum); + else + sprintf(tmp, "%c%u%c (%d) %s ", + lbr, dirent->inode, rbr, + dirent->rec_len, name); thislen = strlen(tmp); if (ls->col + thislen > 80) { @@ -127,7 +137,7 @@ void do_list_dir(int argc, char *argv[]) ext2_ino_t inode; int retval; int c; - int flags; + int flags = DIRENT_FLAG_INCLUDE_EMPTY; struct list_dir_struct ls; ls.options = 0; @@ -135,13 +145,16 @@ void do_list_dir(int argc, char *argv[]) return; reset_getopt(); - while ((c = getopt (argc, argv, "dlp")) != EOF) { + while ((c = getopt (argc, argv, "cdlp")) != EOF) { switch (c) { + case 'c': + flags |= DIRENT_FLAG_INCLUDE_CSUM; + break; case 'l': ls.options |= LONG_OPT; break; case 'd': - ls.options |= DELETED_OPT; + flags |= DIRENT_FLAG_INCLUDE_REMOVED; break; case 'p': ls.options |= PARSE_OPT; @@ -166,9 +179,6 @@ void do_list_dir(int argc, char *argv[]) ls.f = open_pager(); ls.col = 0; - flags = DIRENT_FLAG_INCLUDE_EMPTY; - if (ls.options & DELETED_OPT) - flags |= DIRENT_FLAG_INCLUDE_REMOVED; retval = ext2fs_dir_iterate2(current_fs, inode, flags, 0, list_dir_proc, &ls); diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c index 5c86d744..3925f249 100644 --- a/debugfs/set_fields.c +++ b/debugfs/set_fields.c @@ -151,6 +151,7 @@ static struct field_set_info super_fields[] = { { "grp_quota_inum", &set_sb.s_grp_quota_inum, NULL, 4, parse_uint }, { "overhead_blocks", &set_sb.s_overhead_blocks, NULL, 4, parse_uint }, { "checksum", &set_sb.s_checksum, NULL, 4, parse_uint }, + { "checksum_type", &set_sb.s_checksum_type, NULL, 1, parse_uint }, { 0, 0, 0, 0 } }; @@ -255,6 +256,7 @@ static struct field_set_info mmp_fields[] = { { "bdevname", &set_mmp.mmp_bdevname, NULL, sizeof(set_mmp.mmp_bdevname), parse_string }, { "check_interval", &set_mmp.mmp_check_interval, NULL, 2, parse_uint }, + { "checksum", &set_mmp.mmp_checksum, NULL, 4, parse_uint }, }; static int check_suffix(const char *field) @@ -768,6 +770,7 @@ static errcode_t parse_mmp_clear(struct field_set_info *info, void do_set_mmp_value(int argc, char *argv[]) { +#ifdef CONFIG_MMP const char *usage = "<field> <value>\n" "\t\"set_mmp_value -l\" will list the names of " "MMP fields\n\twhich can be set."; @@ -822,5 +825,9 @@ void do_set_mmp_value(int argc, char *argv[]) &set_mmp); *mmp_s = set_mmp; } +#else + fprintf(stdout, "MMP is unsupported, please recompile with " + "--enable-mmp\n"); +#endif } diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in index 0c638e80..9395bf4f 100644 --- a/e2fsck/Makefile.in +++ b/e2fsck/Makefile.in @@ -64,7 +64,7 @@ COMPILE_ET=$(top_builddir)/lib/et/compile_et --build-tree # #MCHECK= -DMCHECK -OBJS= crc32.o dict.o unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o \ +OBJS= dict.o unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o \ pass3.o pass4.o pass5.o journal.o badblocks.o util.o dirinfo.o \ dx_dirinfo.o ehandler.o problem.o message.o quota.o recovery.o \ region.o revoke.o ea_refcount.o rehash.o profile.o prof_err.o \ @@ -78,12 +78,10 @@ PROFILED_OBJS= profiled/dict.o profiled/unix.o profiled/e2fsck.o \ profiled/message.o profiled/problem.o profiled/quota.o \ profiled/recovery.o profiled/region.o profiled/revoke.o \ profiled/ea_refcount.o profiled/rehash.o profiled/profile.o \ - profiled/crc32.o profiled/prof_err.o profiled/logfile.o \ + profiled/prof_err.o profiled/logfile.o \ profiled/sigcatcher.o SRCS= $(srcdir)/e2fsck.c \ - $(srcdir)/crc32.c \ - $(srcdir)/gen_crc32table.c \ $(srcdir)/dict.c \ $(srcdir)/super.c \ $(srcdir)/pass1.c \ @@ -134,15 +132,6 @@ e2fsck.profiled: $(OBJS) $(PROFILED_DEPLIBS) $(Q) $(LD) $(ALL_LDFLAGS) -g -pg -o e2fsck.profiled $(PROFILED_OBJS) \ $(PROFILED_LIBS) -gen_crc32table: $(srcdir)/gen_crc32table.c - $(E) " CC $@" - $(Q) $(BUILD_CC) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o gen_crc32table \ - $(srcdir)/gen_crc32table.c - -crc32table.h: gen_crc32table - $(E) " GEN32TABLE $@" - $(Q) ./gen_crc32table > crc32table.h - tst_sigcatcher: $(srcdir)/sigcatcher.c $(Q) $(CC) $(BUILD_LDFLAGS) $(ALL_CFLAGS) $(RDYNAMIC) \ $(srcdir)/sigcatcher.c -DDEBUG -o tst_sigcatcher @@ -153,10 +142,6 @@ tst_problem: $(srcdir)/problem.c $(srcdir)/problem.h $(LIBEXT2FS) \ $(srcdir)/problem.c -DUNITTEST $(LIBEXT2FS) $(LIBCOM_ERR) \ $(LIBINTL) -tst_crc32: $(srcdir)/crc32.c $(LIBEXT2FS) $(DEPLIBCOM_ERR) - $(Q) $(CC) $(BUILD_LDFLAGS) $(ALL_CFLAGS) -o tst_crc32 $(srcdir)/crc32.c \ - -DUNITTEST $(LIBEXT2FS) $(LIBCOM_ERR) - tst_refcount: ea_refcount.c $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_refcount $(srcdir)/ea_refcount.c \ @@ -172,10 +157,9 @@ tst_region: region.c $(DEPLIBCOM_ERR) $(Q) $(CC) -o tst_region $(srcdir)/region.c \ $(ALL_CFLAGS) -DTEST_PROGRAM $(LIBCOM_ERR) -check:: tst_refcount tst_region tst_crc32 tst_problem +check:: tst_refcount tst_region tst_problem LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_refcount LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_region - LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_crc32 LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_problem extend: extend.o @@ -272,9 +256,8 @@ uninstall: clean: $(RM) -f $(PROGS) \#* *\# *.s *.o *.a *~ core e2fsck.static \ e2fsck.shared e2fsck.profiled flushb e2fsck.8 \ - tst_problem tst_crc32 tst_region tst_refcount gen_crc32table \ - crc32table.h e2fsck.conf.5 prof_err.c prof_err.h \ - test_profile + tst_problem tst_region tst_refcount e2fsck.conf.5 \ + prof_err.c prof_err.h test_profile $(RM) -rf profiled mostlyclean: clean @@ -297,18 +280,6 @@ e2fsck.o: $(srcdir)/e2fsck.c $(top_builddir)/lib/config.h \ $(top_srcdir)/lib/quota/quotaio.h $(top_srcdir)/lib/quota/dqblk_v2.h \ $(top_srcdir)/lib/quota/quotaio_tree.h $(top_srcdir)/lib/../e2fsck/dict.h \ $(srcdir)/problem.h -crc32.o: $(srcdir)/crc32.c $(top_builddir)/lib/config.h \ - $(top_builddir)/lib/dirpaths.h $(srcdir)/e2fsck.h \ - $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ - $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \ - $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ - $(top_builddir)/lib/ext2fs/ext2_err.h \ - $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/quota/mkquota.h \ - $(top_srcdir)/lib/quota/quotaio.h $(top_srcdir)/lib/quota/dqblk_v2.h \ - $(top_srcdir)/lib/quota/quotaio_tree.h $(top_srcdir)/lib/../e2fsck/dict.h \ - $(srcdir)/crc32defs.h crc32table.h -gen_crc32table.o: $(srcdir)/gen_crc32table.c $(srcdir)/crc32defs.h dict.o: $(srcdir)/dict.c $(top_builddir)/lib/config.h \ $(top_builddir)/lib/dirpaths.h $(srcdir)/dict.h super.o: $(srcdir)/super.c $(top_builddir)/lib/config.h \ diff --git a/e2fsck/crc32.c b/e2fsck/crc32.c deleted file mode 100644 index 0497a380..00000000 --- a/e2fsck/crc32.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * crc32.c --- CRC32 function - * - * Copyright (C) 2008 Theodore Ts'o. - * - * %Begin-Header% - * This file may be redistributed under the terms of the GNU Public - * License. - * %End-Header% - */ - -/* - * Oct 15, 2000 Matt Domsch <Matt_Domsch@dell.com> - * Nicer crc32 functions/docs submitted by linux@horizon.com. Thanks! - * Code was from the public domain, copyright abandoned. Code was - * subsequently included in the kernel, thus was re-licensed under the - * GNU GPL v2. - * - * Oct 12, 2000 Matt Domsch <Matt_Domsch@dell.com> - * Same crc32 function was used in 5 other places in the kernel. - * I made one version, and deleted the others. - * There are various incantations of crc32(). Some use a seed of 0 or ~0. - * Some xor at the end with ~0. The generic crc32() function takes - * seed as an argument, and doesn't xor at the end. Then individual - * users can do whatever they need. - * drivers/net/smc9194.c uses seed ~0, doesn't xor with ~0. - * fs/jffs2 uses seed 0, doesn't xor with ~0. - * fs/partitions/efi.c uses seed ~0, xor's with ~0. - * - * This source code is licensed under the GNU General Public License, - * Version 2. See the file COPYING for more details. - */ - -#include "config.h" -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <ctype.h> - -#ifdef UNITTEST -#undef ENABLE_NLS -#endif -#include "e2fsck.h" - -#include "crc32defs.h" -#if CRC_LE_BITS == 8 -#define tole(x) __constant_cpu_to_le32(x) -#define tobe(x) __constant_cpu_to_be32(x) -#else -#define tole(x) (x) -#define tobe(x) (x) -#endif -#include "crc32table.h" - -#ifdef UNITTEST - -/** - * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 - * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for - * other uses, or the previous crc32 value if computing incrementally. - * @p: pointer to buffer over which CRC is run - * @len: length of buffer @p - */ -__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len); - -#if CRC_LE_BITS == 1 -/* - * In fact, the table-based code will work in this case, but it can be - * simplified by inlining the table in ?: form. - */ - -__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len) -{ - int i; - while (len--) { - crc ^= *p++; - for (i = 0; i < 8; i++) - crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); - } - return crc; -} -#else /* Table-based approach */ - -__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len) -{ -# if CRC_LE_BITS == 8 - const __u32 *b =(__u32 *)p; - const __u32 *tab = crc32table_le; - -# ifdef WORDS_BIGENDIAN -# define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8) -# else -# define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8) -# endif - - crc = __cpu_to_le32(crc); - /* Align it */ - if(unlikely(((long)b)&3 && len)){ - do { - __u8 *p = (__u8 *)b; - DO_CRC(*p++); - b = (void *)p; - } while ((--len) && ((long)b)&3 ); - } - if(likely(len >= 4)){ - /* load data 32 bits wide, xor data 32 bits wide. */ - size_t save_len = len & 3; - len = len >> 2; - --b; /* use pre increment below(*++b) for speed */ - do { - crc ^= *++b; - DO_CRC(0); - DO_CRC(0); - DO_CRC(0); - DO_CRC(0); - } while (--len); - b++; /* point to next byte(s) */ - len = save_len; - } - /* And the last few bytes */ - if(len){ - do { - __u8 *p = (__u8 *)b; - DO_CRC(*p++); - b = (void *)p; - } while (--len); - } - - return __le32_to_cpu(crc); -#undef ENDIAN_SHIFT -#undef DO_CRC - -# elif CRC_LE_BITS == 4 - while (len--) { - crc ^= *p++; - crc = (crc >> 4) ^ crc32table_le[crc & 15]; - crc = (crc >> 4) ^ crc32table_le[crc & 15]; - } - return crc; -# elif CRC_LE_BITS == 2 - while (len--) { - crc ^= *p++; - crc = (crc >> 2) ^ crc32table_le[crc & 3]; - crc = (crc >> 2) ^ crc32table_le[crc & 3]; - crc = (crc >> 2) ^ crc32table_le[crc & 3]; - crc = (crc >> 2) ^ crc32table_le[crc & 3]; - } - return crc; -# endif -} -#endif - -#endif /* UNITTEST */ - -/** - * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 - * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for - * other uses, or the previous crc32 value if computing incrementally. - * @p: pointer to buffer over which CRC is run - * @len: length of buffer @p - */ -__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len); - -#if CRC_BE_BITS == 1 -/* - * In fact, the table-based code will work in this case, but it can be - * simplified by inlining the table in ?: form. - */ - -__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len) -{ - int i; - while (len--) { - crc ^= *p++ << 24; - for (i = 0; i < 8; i++) - crc = - (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : - 0); - } - return crc; -} - -#else /* Table-based approach */ -__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len) -{ -# if CRC_BE_BITS == 8 - const __u32 *b =(const __u32 *)p; - const __u32 *tab = crc32table_be; - -# ifdef WORDS_BIGENDIAN -# define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8) -# else -# define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8) -# endif - - crc = __cpu_to_be32(crc); - /* Align it */ - if(unlikely(((long)b)&3 && len)){ - do { - const __u8 *q = (const __u8 *)b; - DO_CRC(*q++); - b = (const __u32 *)q; - } while ((--len) && ((long)b)&3 ); - } - if(likely(len >= 4)){ - /* load data 32 bits wide, xor data 32 bits wide. */ - size_t save_len = len & 3; - len = len >> 2; - --b; /* use pre increment below(*++b) for speed */ - do { - crc ^= *++b; - DO_CRC(0); - DO_CRC(0); - DO_CRC(0); - DO_CRC(0); - } while (--len); - b++; /* point to next byte(s) */ - len = save_len; - } - /* And the last few bytes */ - if(len){ - do { - const __u8 *q = (const __u8 *)b; - DO_CRC(*q++); - b = (const void *)q; - } while (--len); - } - return __be32_to_cpu(crc); -#undef ENDIAN_SHIFT -#undef DO_CRC - -# elif CRC_BE_BITS == 4 - while (len--) { - crc ^= *p++ << 24; - crc = (crc << 4) ^ crc32table_be[crc >> 28]; - crc = (crc << 4) ^ crc32table_be[crc >> 28]; - } - return crc; -# elif CRC_BE_BITS == 2 - while (len--) { - crc ^= *p++ << 24; - crc = (crc << 2) ^ crc32table_be[crc >> 30]; - crc = (crc << 2) ^ crc32table_be[crc >> 30]; - crc = (crc << 2) ^ crc32table_be[crc >> 30]; - crc = (crc << 2) ^ crc32table_be[crc >> 30]; - } - return crc; -# endif -} -#endif - -/* - * A brief CRC tutorial. - * - * A CRC is a long-division remainder. You add the CRC to the message, - * and the whole thing (message+CRC) is a multiple of the given - * CRC polynomial. To check the CRC, you can either check that the - * CRC matches the recomputed value, *or* you can check that the - * remainder computed on the message+CRC is 0. This latter approach - * is used by a lot of hardware implementations, and is why so many - * protocols put the end-of-frame flag after the CRC. - * - * It's actually the same long division you learned in school, except that - * - We're working in binary, so the digits are only 0 and 1, and - * - When dividing polynomials, there are no carries. Rather than add and - * subtract, we just xor. Thus, we tend to get a bit sloppy about - * the difference between adding and subtracting. - * - * A 32-bit CRC polynomial is actually 33 bits long. But since it's - * 33 bits long, bit 32 is always going to be set, so usually the CRC - * is written in hex with the most significant bit omitted. (If you're - * familiar with the IEEE 754 floating-point format, it's the same idea.) - * - * Note that a CRC is computed over a string of *bits*, so you have - * to decide on the endianness of the bits within each byte. To get - * the best error-detecting properties, this should correspond to the - * order they're actually sent. For example, standard RS-232 serial is - * little-endian; the most significant bit (sometimes used for parity) - * is sent last. And when appending a CRC word to a message, you should - * do it in the right order, matching the endianness. - * - * Just like with ordinary division, the remainder is always smaller than - * the divisor (the CRC polynomial) you're dividing by. Each step of the - * division, you take one more digit (bit) of the dividend and append it - * to the current remainder. Then you figure out the appropriate multiple - * of the divisor to subtract to being the remainder back into range. - * In binary, it's easy - it has to be either 0 or 1, and to make the - * XOR cancel, it's just a copy of bit 32 of the remainder. - * - * When computing a CRC, we don't care about the quotient, so we can - * throw the quotient bit away, but subtract the appropriate multiple of - * the polynomial from the remainder and we're back to where we started, - * ready to process the next bit. - * - * A big-endian CRC written this way would be coded like: - * for (i = 0; i < input_bits; i++) { - * multiple = remainder & 0x80000000 ? CRCPOLY : 0; - * remainder = (remainder << 1 | next_input_bit()) ^ multiple; - * } - * Notice how, to get at bit 32 of the shifted remainder, we look - * at bit 31 of the remainder *before* shifting it. - * - * But also notice how the next_input_bit() bits we're shifting into - * the remainder don't actually affect any decision-making until - * 32 bits later. Thus, the first 32 cycles of this are pretty boring. - * Also, to add the CRC to a message, we need a 32-bit-long hole for it at - * the end, so we have to add 32 extra cycles shifting in zeros at the - * end of every message, - * - * So the standard trick is to rearrage merging in the next_input_bit() - * until the moment it's needed. Then the first 32 cycles can be precomputed, - * and merging in the final 32 zero bits to make room for the CRC can be - * skipped entirely. - * This changes the code to: - * for (i = 0; i < input_bits; i++) { - * remainder ^= next_input_bit() << 31; - * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; - * remainder = (remainder << 1) ^ multiple; - * } - * With this optimization, the little-endian code is simpler: - * for (i = 0; i < input_bits; i++) { - * remainder ^= next_input_bit(); - * multiple = (remainder & 1) ? CRCPOLY : 0; - * remainder = (remainder >> 1) ^ multiple; - * } - * - * Note that the other details of endianness have been hidden in CRCPOLY - * (which must be bit-reversed) and next_input_bit(). - * - * However, as long as next_input_bit is returning the bits in a sensible - * order, we can actually do the merging 8 or more bits at a time rather - * than one bit at a time: - * for (i = 0; i < input_bytes; i++) { - * remainder ^= next_input_byte() << 24; - * for (j = 0; j < 8; j++) { - * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; - * remainder = (remainder << 1) ^ multiple; - * } - * } - * Or in little-endian: - * for (i = 0; i < input_bytes; i++) { - * remainder ^= next_input_byte(); - * for (j = 0; j < 8; j++) { - * multiple = (remainder & 1) ? CRCPOLY : 0; - * remainder = (remainder << 1) ^ multiple; - * } - * } - * If the input is a multiple of 32 bits, you can even XOR in a 32-bit - * word at a time and increase the inner loop count to 32. - * - * You can also mix and match the two loop styles, for example doing the - * bulk of a message byte-at-a-time and adding bit-at-a-time processing - * for any fractional bytes at the end. - * - * The only remaining optimization is to the byte-at-a-time table method. - * Here, rather than just shifting one bit of the remainder to decide - * in the correct multiple to subtract, we can shift a byte at a time. - * This produces a 40-bit (rather than a 33-bit) intermediate remainder, - * but again the multiple of the polynomial to subtract depends only on - * the high bits, the high 8 bits in this case. - * - * The multiple we need in that case is the low 32 bits of a 40-bit - * value whose high 8 bits are given, and which is a multiple of the - * generator polynomial. This is simply the CRC-32 of the given - * one-byte message. - * - * Two more details: normally, appending zero bits to a message which - * is already a multiple of a polynomial produces a larger multiple of that - * polynomial. To enable a CRC to detect this condition, it's common to - * invert the CRC before appending it. This makes the remainder of the - * message+crc come out not as zero, but some fixed non-zero value. - * - * The same problem applies to zero bits prepended to the message, and - * a similar solution is used. Instead of starting with a remainder of - * 0, an initial remainder of all ones is used. As long as you start - * the same way on decoding, it doesn't make a difference. - */ - -#ifdef UNITTEST - -#include <stdlib.h> -#include <stdio.h> - -const __u8 byte_rev_table[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, -}; - -static inline __u8 bitrev8(__u8 byte) -{ - return byte_rev_table[byte]; -} - -static inline __u16 bitrev16(__u16 x) -{ - return (bitrev8(x & 0xff) << 8) | bitrev8(x >> 8); -} - -/** - * bitrev32 - reverse the order of bits in a u32 value - * @x: value to be bit-reversed - */ -static __u32 bitrev32(__u32 x) -{ - return (bitrev16(x & 0xffff) << 16) | bitrev16(x >> 16); -} - -#if 0 /*Not used at present */ - -static void -buf_dump(char const *prefix, unsigned char const *buf, size_t len) -{ - fputs(prefix, stdout); - while (len--) - printf(" %02x", *buf++); - putchar('\n'); - -} -#endif - -static void bytereverse(unsigned char *buf, size_t len) -{ - while (len--) { - unsigned char x = bitrev8(*buf); - *buf++ = x; - } -} - -static void random_garbage(unsigned char *buf, size_t len) -{ - while (len--) - *buf++ = (unsigned char) random(); -} - -#if 0 /* Not used at present */ -static void store_le(__u32 x, unsigned char *buf) -{ - buf[0] = (unsigned char) x; - buf[1] = (unsigned char) (x >> 8); - buf[2] = (unsigned char) (x >> 16); - buf[3] = (unsigned char) (x >> 24); -} -#endif - -static void store_be(__u32 x, unsigned char *buf) -{ - buf[0] = (unsigned char) (x >> 24); - buf[1] = (unsigned char) (x >> 16); - buf[2] = (unsigned char) (x >> 8); - buf[3] = (unsigned char) x; -} - -/* - * This checks that CRC(buf + CRC(buf)) = 0, and that - * CRC commutes with bit-reversal. This has the side effect - * of bytewise bit-reversing the input buffer, and returns - * the CRC of the reversed buffer. - */ -static __u32 test_step(__u32 init, unsigned char *buf, size_t len) -{ - __u32 crc1, crc2; - size_t i; - - crc1 = crc32_be(init, buf, len); - store_be(crc1, buf + len); - crc2 = crc32_be(init, buf, len + 4); - if (crc2) - printf("\nCRC cancellation fail: 0x%08x should be 0\n", - crc2); - - for (i = 0; i <= len + 4; i++) { - crc2 = crc32_be(init, buf, i); - crc2 = crc32_be(crc2, buf + i, len + 4 - i); - if (crc2) - printf("\nCRC split fail: 0x%08x\n", crc2); - } - - /* Now swap it around for the other test */ - - bytereverse(buf, len + 4); - init = bitrev32(init); - crc2 = bitrev32(crc1); - if (crc1 != bitrev32(crc2)) - printf("\nBit reversal fail: 0x%08x -> 0x%08x -> 0x%08x\n", - crc1, crc2, bitrev32(crc2)); - crc1 = crc32_le(init, buf, len); - if (crc1 != crc2) - printf("\nCRC endianness fail: 0x%08x != 0x%08x\n", crc1, - crc2); - crc2 = crc32_le(init, buf, len + 4); - if (crc2) - printf("\nCRC cancellation fail: 0x%08x should be 0\n", - crc2); - - for (i = 0; i <= len + 4; i++) { - crc2 = crc32_le(init, buf, i); - crc2 = crc32_le(crc2, buf + i, len + 4 - i); - if (crc2) - printf("\nCRC split fail: 0x%08x\n", crc2); - } - - return crc1; -} - -#define SIZE 64 -#define INIT1 0 -#define INIT2 0 - -int main(int argc, char **argv) -{ - unsigned char buf1[SIZE + 4]; - unsigned char buf2[SIZE + 4]; - unsigned char buf3[SIZE + 4]; - int i, j; - __u32 crc1, crc2, crc3; - int exit_status = 0; - - for (i = 0; i <= SIZE; i++) { - printf("\rTesting length %d...", i); - fflush(stdout); - random_garbage(buf1, i); - random_garbage(buf2, i); - for (j = 0; j < i; j++) - buf3[j] = buf1[j] ^ buf2[j]; - - crc1 = test_step(INIT1, buf1, i); - crc2 = test_step(INIT2, buf2, i); - /* Now check that CRC(buf1 ^ buf2) = CRC(buf1) ^ CRC(buf2) */ - crc3 = test_step(INIT1 ^ INIT2, buf3, i); - if (crc3 != (crc1 ^ crc2)) { - printf("CRC XOR fail: 0x%08x != 0x%08x ^ 0x%08x\n", - crc3, crc1, crc2); - exit_status++; - } - } - printf("\nAll test complete. No failures expected.\n"); - return 0; -} - -#endif /* UNITTEST */ diff --git a/e2fsck/crc32defs.h b/e2fsck/crc32defs.h deleted file mode 100644 index 27414d2d..00000000 --- a/e2fsck/crc32defs.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * There are multiple 16-bit CRC polynomials in common use, but this is - * *the* standard CRC-32 polynomial, first popularized by Ethernet. - * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 - */ -#define CRCPOLY_LE 0xedb88320 -#define CRCPOLY_BE 0x04c11db7 - -/* How many bits at a time to use. Requires a table of 4<<CRC_xx_BITS bytes. */ -/* For less performance-sensitive, use 4 */ -#ifndef CRC_LE_BITS -# define CRC_LE_BITS 8 -#endif -#ifndef CRC_BE_BITS -# define CRC_BE_BITS 8 -#endif - -/* - * Little-endian CRC computation. Used with serial bit streams sent - * lsbit-first. Be sure to use cpu_to_le32() to append the computed CRC. - */ -#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1 -# error CRC_LE_BITS must be a power of 2 between 1 and 8 -#endif - -/* - * Big-endian CRC computation. Used with serial bit streams sent - * msbit-first. Be sure to use cpu_to_be32() to append the computed CRC. - */ -#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1 -# error CRC_BE_BITS must be a power of 2 between 1 and 8 -#endif - -#define ___constant_swab32(x) \ - ((__u32)( \ - (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ - (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ - (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ - (((__u32)(x) & (__u32)0xff000000UL) >> 24) )) - - -#ifdef WORDS_BIGENDIAN -#define __constant_cpu_to_le32(x) ___constant_swab32((x)) -#define __constant_cpu_to_be32(x) (x) -#define __be32_to_cpu(x) (x) -#define __cpu_to_be32(x) (x) -#define __cpu_to_le32(x) (ext2fs_swab32((x))) -#define __le32_to_cpu(x) (ext2fs_swab32((x))) -#else -#define __constant_cpu_to_le32(x) (x) -#define __constant_cpu_to_be32(x) ___constant_swab32((x)) -#define __be32_to_cpu(x) (ext2fs_swab32((x))) -#define __cpu_to_be32(x) (ext2fs_swab32((x))) -#define __cpu_to_le32(x) (x) -#define __le32_to_cpu(x) (x) -#endif - -#if (__GNUC__ >= 3) -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) -#else -#define likely(x) (x) -#define unlikely(x) (x) -#endif diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index 420b67f5..09a9d083 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -405,9 +405,6 @@ extern int e2fsck_run(e2fsck_t ctx); extern void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file, int replace_bad_blocks); -/* crc32.c */ -extern __u32 crc32_be(__u32 crc, unsigned char const *p, size_t len); - /* dirinfo.c */ extern void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent); extern void e2fsck_free_dir_info(e2fsck_t ctx); @@ -494,6 +491,8 @@ extern void region_free(region_t region); extern int region_allocate(region_t region, region_addr_t start, int n); /* rehash.c */ +void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino); +int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino); errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino); void e2fsck_rehash_directories(e2fsck_t ctx); diff --git a/e2fsck/gen_crc32table.c b/e2fsck/gen_crc32table.c deleted file mode 100644 index 2c1aa8ec..00000000 --- a/e2fsck/gen_crc32table.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * gen_crc32table.c --- Generate CRC32 tables. - * - * Copyright (C) 2008 Theodore Ts'o. - * - * %Begin-Header% - * This file may be redistributed under the terms of the GNU Public - * License. - * %End-Header% - */ - -#include <stdio.h> -#include "crc32defs.h" -#include <inttypes.h> - -#define ENTRIES_PER_LINE 4 - -#define LE_TABLE_SIZE (1 << CRC_LE_BITS) -#define BE_TABLE_SIZE (1 << CRC_BE_BITS) - -static uint32_t crc32table_le[LE_TABLE_SIZE]; -static uint32_t crc32table_be[BE_TABLE_SIZE]; - -/** - * crc32init_le() - allocate and initialize LE table data - * - * crc is the crc of the byte i; other entries are filled in based on the - * fact that crctable[i^j] = crctable[i] ^ crctable[j]. - * - */ -static void crc32init_le(void) -{ - unsigned i, j; - uint32_t crc = 1; - - crc32table_le[0] = 0; - - for (i = 1 << (CRC_LE_BITS - 1); i; i >>= 1) { - crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); - for (j = 0; j < LE_TABLE_SIZE; j += 2 * i) - crc32table_le[i + j] = crc ^ crc32table_le[j]; - } -} - -/** - * crc32init_be() - allocate and initialize BE table data - */ -static void crc32init_be(void) -{ - unsigned i, j; - uint32_t crc = 0x80000000; - - crc32table_be[0] = 0; - - for (i = 1; i < BE_TABLE_SIZE; i <<= 1) { - crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0); - for (j = 0; j < i; j++) - crc32table_be[i + j] = crc ^ crc32table_be[j]; - } -} - -static void output_table(uint32_t table[], int len, const char *trans) -{ - int i; - - for (i = 0; i < len - 1; i++) { - if (i % ENTRIES_PER_LINE == 0) - printf("\n"); - printf("%s(0x%8.8xL), ", trans, table[i]); - } - printf("%s(0x%8.8xL)\n", trans, table[len - 1]); -} - -#ifdef __GNUC__ -#define ATTR(x) __attribute__(x) -#else -#define ATTR(x) -#endif - -int main(int argc ATTR((unused)), char** argv ATTR((unused))) -{ - printf("/* this file is generated - do not edit */\n\n"); - - printf("#ifdef UNITTEST\n"); - if (CRC_LE_BITS > 1) { - crc32init_le(); - printf("static const __u32 crc32table_le[] = {"); - output_table(crc32table_le, LE_TABLE_SIZE, "tole"); - printf("};\n"); - } - printf("#endif /* UNITTEST */\n"); - - if (CRC_BE_BITS > 1) { - crc32init_be(); - printf("static const __u32 crc32table_be[] = {"); - output_table(crc32table_be, BE_TABLE_SIZE, "tobe"); - printf("};\n"); - } - - return 0; -} diff --git a/e2fsck/jfs_user.h b/e2fsck/jfs_user.h index bfc1bcde..3cccd3fa 100644 --- a/e2fsck/jfs_user.h +++ b/e2fsck/jfs_user.h @@ -115,17 +115,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/journal.c b/e2fsck/journal.c index 69771da6..fd6d6caa 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. @@ -571,6 +618,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. */ @@ -638,6 +694,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); @@ -678,6 +735,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); @@ -823,6 +881,7 @@ no_has_journal: 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); } diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 40957030..94df36d4 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -473,7 +473,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx, /* read the first block */ ehandler_operation(_("reading directory block")); - retval = ext2fs_read_dir_block3(ctx->fs, blk, buf, 0); + retval = ext2fs_read_dir_block4(ctx->fs, blk, buf, 0, pctx->ino); ehandler_operation(0); if (retval) return; @@ -540,6 +540,40 @@ extern void e2fsck_setup_tdb_icount(e2fsck_t ctx, int flags, *ret = 0; } +static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino, + e2fsck_t ctx, + struct problem_context *pctx) +{ + errcode_t retval; + struct ext2_inode_large inode; + + /* + * Reread inode. If we don't see checksum error, then this inode + * has been fixed elsewhere. + */ + retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode, + sizeof(inode)); + if (retval && retval != EXT2_ET_INODE_CSUM_INVALID) + return retval; + if (!retval) + return 0; + + /* + * Checksum still doesn't match. That implies that the inode passes + * all the sanity checks, so maybe the checksum is simply corrupt. + * See if the user will go for fixing that. + */ + if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx)) + return 0; + + retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode, + sizeof(inode)); + if (retval) + return retval; + + return 0; +} + void e2fsck_pass1(e2fsck_t ctx) { int i; @@ -561,6 +595,7 @@ void e2fsck_pass1(e2fsck_t ctx) int imagic_fs, extent_fs; int busted_fs_time = 0; int inode_size; + int failed_csum = 0; init_resource_track(&rtrack, ctx->fs->io); clear_problem_context(&pctx); @@ -691,7 +726,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); @@ -721,6 +757,9 @@ void e2fsck_pass1(e2fsck_t ctx) ext2fs_mark_block_bitmap2(ctx->block_found_map, fs->super->s_mmp_block); + /* Set up ctx->lost_and_found if possible */ + (void) e2fsck_get_lost_and_found(ctx, 0); + while (1) { if (ino % (fs->super->s_inodes_per_group * 4) == 1) { if (e2fsck_mmp_update(fs)) @@ -739,7 +778,8 @@ void e2fsck_pass1(e2fsck_t ctx) ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); continue; } - if (pctx.errcode) { + if (pctx.errcode && + pctx.errcode != EXT2_ET_INODE_CSUM_INVALID) { fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; @@ -749,6 +789,14 @@ void e2fsck_pass1(e2fsck_t ctx) pctx.ino = ino; pctx.inode = inode; ctx->stashed_ino = ino; + + /* Clear corrupt inode? */ + if (pctx.errcode == EXT2_ET_INODE_CSUM_INVALID) { + if (fix_problem(ctx, PR_1_INODE_CSUM_INVALID, &pctx)) + goto clear_inode; + failed_csum = 1; + } + if (inode->i_links_count) { pctx.errcode = ext2fs_icount_store(ctx->inode_link_info, ino, inode->i_links_count); @@ -1145,6 +1193,20 @@ void e2fsck_pass1(e2fsck_t ctx) } else check_blocks(ctx, &pctx, block_buf); + /* + * If the inode failed the checksum and the user didn't + * clear the inode, test the checksum again -- if it still + * fails, ask the user if the checksum should be corrected. + */ + if (failed_csum) { + pctx.errcode = recheck_bad_inode_checksum(fs, ino, ctx, + &pctx); + if (pctx.errcode) { + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; @@ -1236,6 +1298,12 @@ endit: ext2fs_free_mem(&block_buf); ext2fs_free_mem(&inode); + /* + * The l+f inode may have been cleared, so zap it now and + * later passes will recalculate it if necessary + */ + ctx->lost_and_found = 0; + print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io); } @@ -1465,7 +1533,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; @@ -1476,8 +1545,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); @@ -1502,6 +1572,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, struct ext2_ext_attr_entry *entry; int count; region_t region = 0; + int failed_csum = 0; blk = ext2fs_file_acl_block(fs, inode); if (blk == 0) @@ -1575,7 +1646,12 @@ 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 == EXT2_ET_EXT_ATTR_CSUM_INVALID) { + if (fix_problem(ctx, PR_1_EA_BLOCK_CSUM_INVALID, pctx)) + goto clear_extattr; + failed_csum = 1; + } if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx)) goto clear_extattr; header = (struct ext2_ext_attr_header *) block_buf; @@ -1657,6 +1733,18 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, } region_free(region); + /* + * We only get here if there was no other errors that were fixed. + * If there was a checksum fail, ask to correct it. + */ + if (failed_csum && + fix_problem(ctx, PR_1_EA_BLOCK_ONLY_CSUM_INVALID, pctx)) { + pctx->errcode = ext2fs_write_ext_attr3(fs, blk, block_buf, + pctx->ino); + if (pctx->errcode) + return 0; + } + count = header->h_refcount - 1; if (count) ea_refcount_store(ctx->refcount, blk, count); @@ -1770,6 +1858,7 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, int is_dir, is_leaf; errcode_t problem; struct ext2_extent_info info; + int failed_csum; pctx->errcode = ext2fs_extent_get_info(ehandle, &info); if (pctx->errcode) @@ -1777,11 +1866,25 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_FIRST_SIB, &extent); - while (!pctx->errcode && info.num_entries-- > 0) { + while ((pctx->errcode == 0 || + pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) && + info.num_entries-- > 0) { + failed_csum = 0; is_leaf = extent.e_flags & EXT2_EXTENT_FLAGS_LEAF; is_dir = LINUX_S_ISDIR(pctx->inode->i_mode); problem = 0; + /* Ask to clear a corrupt extent block */ + if (pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) { + pctx->blk = extent.e_pblk; + pctx->blk2 = extent.e_lblk; + pctx->num = extent.e_len; + problem = PR_1_EXTENT_CSUM_INVALID; + if (fix_problem(ctx, problem, pctx)) + goto fix_problem_now; + failed_csum = 1; + } + if (extent.e_pblk == 0 || extent.e_pblk < ctx->fs->super->s_first_data_block || extent.e_pblk >= ext2fs_blocks_count(ctx->fs->super)) @@ -1795,12 +1898,24 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, ext2fs_blocks_count(ctx->fs->super)) problem = PR_1_EXTENT_ENDS_BEYOND; + /* Corrupt but passes checks? Ask to fix checksum. */ + if (failed_csum) { + pctx->blk = extent.e_pblk; + pctx->blk2 = extent.e_lblk; + pctx->num = extent.e_len; + problem = 0; + if (fix_problem(ctx, PR_1_EXTENT_ONLY_CSUM_INVALID, + pctx)) + ext2fs_extent_replace(ehandle, 0, &extent); + } + if (problem) { report_problem: pctx->blk = extent.e_pblk; pctx->blk2 = extent.e_lblk; pctx->num = extent.e_len; if (fix_problem(ctx, problem, pctx)) { +fix_problem_now: e2fsck_read_bitmaps(ctx); pctx->errcode = ext2fs_extent_delete(ehandle, 0); @@ -1831,7 +1946,10 @@ report_problem: if (pctx->errcode) { pctx->str = "EXT2_EXTENT_DOWN"; problem = PR_1_EXTENT_HEADER_INVALID; - if (pctx->errcode == EXT2_ET_EXTENT_HEADER_BAD) + if (pctx->errcode == + EXT2_ET_EXTENT_HEADER_BAD || + pctx->errcode == + EXT2_ET_EXTENT_CSUM_INVALID) goto report_problem; return; } @@ -2165,9 +2283,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, } if (ctx->dirs_to_hash && pb.is_dir && + !(ctx->lost_and_found && ctx->lost_and_found == ino) && !(inode->i_flags & EXT2_INDEX_FL) && ((inode->i_size / fs->blocksize) >= 3)) - ext2fs_u32_list_add(ctx->dirs_to_hash, ino); + e2fsck_rehash_dir_later(ctx, ino); out: if (dirty_inode) diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c index 93fb630d..05cbd10c 100644 --- a/e2fsck/pass1b.c +++ b/e2fsck/pass1b.c @@ -653,9 +653,9 @@ static void delete_file(e2fsck_t ctx, ext2_ino_t ino, if (ext2fs_file_acl_block(fs, &inode) && (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { count = 1; - pctx.errcode = ext2fs_adjust_ea_refcount2(fs, + pctx.errcode = ext2fs_adjust_ea_refcount3(fs, ext2fs_file_acl_block(fs, &inode), - block_buf, -1, &count); + block_buf, -1, &count, ino); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 882950d6..e28af610 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -527,7 +527,7 @@ static void parse_int_node(ext2_filsys fs, struct ext2_db_entry2 *db, struct check_dir_struct *cd, struct dx_dir_info *dx_dir, - char *block_buf) + char *block_buf, int failed_csum) { struct ext2_dx_root_info *root; struct ext2_dx_entry *ent; @@ -538,6 +538,7 @@ static void parse_int_node(ext2_filsys fs, ext2_dirhash_t min_hash = 0xffffffff; ext2_dirhash_t max_hash = 0; ext2_dirhash_t hash = 0, prev_hash; + int csum_size = 0; if (db->blockcnt == 0) { root = (struct ext2_dx_root_info *) (block_buf + 24); @@ -552,9 +553,22 @@ static void parse_int_node(ext2_filsys fs, #endif ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length); + + if (failed_csum && + (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || + fix_problem(cd->ctx, PR_2_HTREE_ROOT_CSUM_INVALID, + &cd->pctx))) + goto clear_and_exit; } else { ent = (struct ext2_dx_entry *) (block_buf+8); + + if (failed_csum && + (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || + fix_problem(cd->ctx, PR_2_HTREE_NODE_CSUM_INVALID, + &cd->pctx))) + goto clear_and_exit; } + limit = (struct ext2_dx_countlimit *) ent; #ifdef DX_DEBUG @@ -565,8 +579,12 @@ static void parse_int_node(ext2_filsys fs, #endif count = ext2fs_le16_to_cpu(limit->count); - expect_limit = (fs->blocksize - ((char *) ent - block_buf)) / - sizeof(struct ext2_dx_entry); + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + csum_size = sizeof(struct ext2_dx_tail); + expect_limit = (fs->blocksize - + (csum_size + ((char *) ent - block_buf))) / + sizeof(struct ext2_dx_entry); if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) { cd->pctx.num = ext2fs_le16_to_cpu(limit->limit); if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx)) @@ -632,6 +650,7 @@ static void parse_int_node(ext2_filsys fs, clear_and_exit: clear_htree(cd->ctx, cd->pctx.ino); dx_dir->numblocks = 0; + e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino); } #endif /* ENABLE_HTREE */ @@ -734,6 +753,9 @@ static int check_dir_block(ext2_filsys fs, struct problem_context pctx; int dups_found = 0; int ret; + int dx_csum_size = 0, de_csum_size = 0; + int failed_csum = 0; + int is_leaf = 1; cd = (struct check_dir_struct *) priv_data; buf = cd->buf; @@ -745,6 +767,12 @@ static int check_dir_block(ext2_filsys fs, if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) return DIRENT_ABORT; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + dx_csum_size = sizeof(struct ext2_dx_tail); + de_csum_size = sizeof(struct ext2_dir_entry_tail); + } + /* * Make sure the inode is still in use (could have been * deleted in the duplicate/bad blocks pass. @@ -780,16 +808,24 @@ static int check_dir_block(ext2_filsys fs, #endif ehandler_operation(_("reading directory block")); - cd->pctx.errcode = ext2fs_read_dir_block3(fs, block_nr, buf, 0); + cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino); ehandler_operation(0); if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) cd->pctx.errcode = 0; /* We'll handle this ourselves */ + else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) { + cd->pctx.errcode = 0; /* We'll handle this ourselves */ + failed_csum = 1; + } if (cd->pctx.errcode) { + 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); @@ -834,13 +870,46 @@ static int check_dir_block(ext2_filsys fs, (rec_len == fs->blocksize) && (dirent->name_len == 0) && (ext2fs_le16_to_cpu(limit->limit) == - ((fs->blocksize-8) / + ((fs->blocksize - (8 + dx_csum_size)) / sizeof(struct ext2_dx_entry)))) dx_db->type = DX_DIRBLOCK_NODE; + is_leaf = 0; } out_htree: #endif /* ENABLE_HTREE */ + /* Verify checksum. */ + if (is_leaf && de_csum_size) { + /* No space for csum? Rebuild dirs in pass 3A. */ + if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) { + de_csum_size = 0; + if (e2fsck_dir_will_be_rehashed(ctx, ino)) + goto skip_checksum; + if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM, + &cd->pctx)) + goto skip_checksum; + e2fsck_rehash_dir_later(ctx, ino); + goto skip_checksum; + } + if (failed_csum) { + char *buf2; + if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_CSUM_INVALID, + &cd->pctx)) + goto skip_checksum; + ext2fs_new_dir_block(fs, + db->blockcnt == 0 ? ino : 0, + EXT2_ROOT_INO, &buf2); + memcpy(buf, buf2, fs->blocksize); + ext2fs_free_mem(&buf2); + dir_modified++; + failed_csum = 0; + } + } + /* htree nodes don't use fake dirents to store checksums */ + if (!is_leaf) + de_csum_size = 0; + +skip_checksum: dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); prev = 0; do { @@ -1088,10 +1157,7 @@ out_htree: pctx.ino = ino; pctx.dirent = dirent; fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); - if (!ctx->dirs_to_hash) - ext2fs_u32_list_create(&ctx->dirs_to_hash, 50); - if (ctx->dirs_to_hash) - ext2fs_u32_list_add(ctx->dirs_to_hash, ino); + e2fsck_rehash_dir_later(ctx, ino); dups_found++; } else dict_alloc_insert(&de_dict, dirent, dirent); @@ -1107,7 +1173,7 @@ out_htree: (void) ext2fs_get_rec_len(fs, dirent, &rec_len); offset += rec_len; dot_state++; - } while (offset < fs->blocksize); + } while (offset < fs->blocksize - de_csum_size); #if 0 printf("\n"); #endif @@ -1121,24 +1187,47 @@ out_htree: cd->pctx.dir = cd->pctx.ino; if ((dx_db->type == DX_DIRBLOCK_ROOT) || (dx_db->type == DX_DIRBLOCK_NODE)) - parse_int_node(fs, db, cd, dx_dir, buf); + parse_int_node(fs, db, cd, dx_dir, buf, failed_csum); } #endif /* ENABLE_HTREE */ - if (offset != fs->blocksize) { - cd->pctx.num = rec_len - fs->blocksize + offset; + + if (offset != fs->blocksize - de_csum_size) { + cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) + + offset; if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { dirent->rec_len = cd->pctx.num; dir_modified++; } } if (dir_modified) { - cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf); + /* leaf block with no tail? Rehash dirs later. */ + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && + is_leaf && + !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) + e2fsck_rehash_dir_later(ctx, ino); + +write_and_fix: + if (e2fsck_dir_will_be_rehashed(ctx, ino)) + ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; + cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf, + 0, ino); + if (e2fsck_dir_will_be_rehashed(ctx, ino)) + ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; if (cd->pctx.errcode) { if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx)) goto abort_free_dict; } ext2fs_mark_changed(fs); + } else if (is_leaf && failed_csum && !dir_modified) { + /* + * If a leaf node that fails csum makes it this far without + * alteration, ask the user if the checksum should be fixed. + */ + if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID, + &cd->pctx)) + goto write_and_fix; } dict_free_nodes(&de_dict); return 0; @@ -1201,9 +1290,9 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) if (ext2fs_file_acl_block(fs, &inode) && (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { - pctx.errcode = ext2fs_adjust_ea_refcount2(fs, - ext2fs_file_acl_block(fs, &inode), - block_buf, -1, &count); + pctx.errcode = ext2fs_adjust_ea_refcount3(fs, + ext2fs_file_acl_block(fs, &inode), + block_buf, -1, &count, ino); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; @@ -1452,7 +1541,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 565b8e33..a379e9b5 100644 --- a/e2fsck/pass3.c +++ b/e2fsck/pass3.c @@ -198,7 +198,8 @@ static void check_root(e2fsck_t ctx) return; } - pctx.errcode = ext2fs_write_dir_block(fs, blk, block); + pctx.errcode = ext2fs_write_dir_block4(fs, blk, block, 0, + EXT2_ROOT_INO); if (pctx.errcode) { pctx.str = "ext2fs_write_dir_block"; fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); @@ -374,7 +375,7 @@ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix) if (retval && !fix) return 0; if (!retval) { - if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, ino)) { + if (ext2fs_check_directory(fs, ino) == 0) { ctx->lost_and_found = ino; return ino; } @@ -444,7 +445,7 @@ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix) return 0; } - retval = ext2fs_write_dir_block(fs, blk, block); + retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino); ext2fs_free_mem(&block); if (retval) { pctx.errcode = retval; @@ -658,8 +659,12 @@ static void fix_dotdot(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent) clear_problem_context(&pctx); pctx.ino = ino; + if (e2fsck_dir_will_be_rehashed(ctx, ino)) + ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; retval = ext2fs_dir_iterate(fs, ino, DIRENT_FLAG_INCLUDE_EMPTY, 0, fix_dotdot_proc, &fp); + if (e2fsck_dir_will_be_rehashed(ctx, ino)) + ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; if (retval || !fp.done) { pctx.errcode = retval; fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR : @@ -685,6 +690,7 @@ struct expand_dir_struct { blk64_t last_block; errcode_t err; e2fsck_t ctx; + ext2_ino_t dir; }; static int expand_dir_proc(ext2_filsys fs, @@ -725,7 +731,8 @@ static int expand_dir_proc(ext2_filsys fs, return BLOCK_ABORT; } es->num--; - retval = ext2fs_write_dir_block(fs, new_blk, block); + retval = ext2fs_write_dir_block4(fs, new_blk, block, 0, + es->dir); } else { retval = ext2fs_get_mem(fs->blocksize, &block); if (retval) { @@ -778,6 +785,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/pass5.c b/e2fsck/pass5.c index fce0f6e3..86b5225d 100644 --- a/e2fsck/pass5.c +++ b/e2fsck/pass5.c @@ -27,6 +27,8 @@ 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); +static void check_block_bitmap_checksum(e2fsck_t ctx); void e2fsck_pass5(e2fsck_t ctx) { @@ -64,6 +66,9 @@ void e2fsck_pass5(e2fsck_t ctx) if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; + check_inode_bitmap_checksum(ctx); + check_block_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,126 @@ void e2fsck_pass5(e2fsck_t ctx) print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io); } +static void check_inode_bitmap_checksum(e2fsck_t ctx) +{ + struct problem_context pctx; + struct ext4_group_desc *gdp; + char *buf; + dgrp_t i; + int nbytes; + ext2_ino_t ino_itr; + errcode_t retval; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return; + + /* If bitmap is dirty from being fixed, checksum will be corrected */ + if (ext2fs_test_ib_dirty(ctx->fs)) + return; + + nbytes = (size_t)(EXT2_INODES_PER_GROUP(ctx->fs->super) / 8); + retval = ext2fs_get_memalign(ctx->fs->blocksize, ctx->fs->blocksize, + &buf); + if (retval) { + com_err(ctx->program_name, 0, + _("check_inode_bitmap_checksum: Memory allocation error")); + fatal_error(ctx, 0); + } + + clear_problem_context(&pctx); + for (i = 0; i < ctx->fs->group_desc_count; i++) { + if (ext2fs_bg_flags_test(ctx->fs, i, EXT2_BG_INODE_UNINIT)) + continue; + + ino_itr = 1 + (i * (nbytes << 3)); + gdp = (struct ext4_group_desc *)ext2fs_group_desc(ctx->fs, + ctx->fs->group_desc, i); + retval = ext2fs_get_inode_bitmap_range2(ctx->fs->inode_map, + ino_itr, nbytes << 3, + buf); + if (retval) + break; + + if (ext2fs_inode_bitmap_csum_verify(ctx->fs, i, buf, nbytes)) + continue; + pctx.group = i; + if (!fix_problem(ctx, PR_5_INODE_BITMAP_CSUM_INVALID, &pctx)) + continue; + + /* + * Fixing one checksum will rewrite all of them. The bitmap + * will be checked against the one we made during pass1 for + * discrepancies, and fixed if need be. + */ + ext2fs_mark_ib_dirty(ctx->fs); + break; + } + + ext2fs_free_mem(&buf); +} + +static void check_block_bitmap_checksum(e2fsck_t ctx) +{ + struct problem_context pctx; + struct ext4_group_desc *gdp; + char *buf; + dgrp_t i; + int nbytes; + blk64_t blk_itr; + errcode_t retval; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(ctx->fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return; + + /* If bitmap is dirty from being fixed, checksum will be corrected */ + if (ext2fs_test_bb_dirty(ctx->fs)) + return; + + nbytes = (size_t)(EXT2_CLUSTERS_PER_GROUP(ctx->fs->super) / 8); + retval = ext2fs_get_memalign(ctx->fs->blocksize, ctx->fs->blocksize, + &buf); + if (retval) { + com_err(ctx->program_name, 0, + _("check_block_bitmap_checksum: Memory allocation error")); + fatal_error(ctx, 0); + } + + clear_problem_context(&pctx); + for (i = 0; i < ctx->fs->group_desc_count; i++) { + if (ext2fs_bg_flags_test(ctx->fs, i, EXT2_BG_BLOCK_UNINIT)) + continue; + + blk_itr = EXT2FS_B2C(ctx->fs, + ctx->fs->super->s_first_data_block) + + (i * (nbytes << 3)); + gdp = (struct ext4_group_desc *)ext2fs_group_desc(ctx->fs, + ctx->fs->group_desc, i); + retval = ext2fs_get_block_bitmap_range2(ctx->fs->block_map, + blk_itr, nbytes << 3, + buf); + if (retval) + break; + + if (ext2fs_block_bitmap_csum_verify(ctx->fs, i, buf, nbytes)) + continue; + pctx.group = i; + if (!fix_problem(ctx, PR_5_BLOCK_BITMAP_CSUM_INVALID, &pctx)) + continue; + + /* + * Fixing one checksum will rewrite all of them. The bitmap + * will be checked against the one we made during pass1 for + * discrepancies, and fixed if need be. + */ + ext2fs_mark_bb_dirty(ctx->fs); + break; + } + + ext2fs_free_mem(&buf); +} + static void e2fsck_discard_blocks(e2fsck_t ctx, blk64_t start, blk64_t count) { @@ -253,8 +378,7 @@ static void check_block_bitmaps(e2fsck_t ctx) goto errout; } - csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + csum_flag = ext2fs_has_group_desc_csum(fs); redo_counts: had_problem = 0; save_problem = 0; @@ -582,8 +706,7 @@ static void check_inode_bitmaps(e2fsck_t ctx) goto errout; } - csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + csum_flag = ext2fs_has_group_desc_csum(fs); redo_counts: had_problem = 0; save_problem = 0; diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 05ef626f..76bc1d56 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -433,6 +433,20 @@ static struct e2fsck_problem problem_table[] = { N_("ext2fs_check_desc: %m\n"), PROMPT_NONE, 0 }, + /* + * metadata_csum implies uninit_bg; both feature bits cannot + * be set simultaneously. + */ + { PR_0_META_AND_GDT_CSUM_SET, + N_("@S metadata_csum supersedes uninit_bg; both feature " + "bits cannot be set simultaneously."), + PROMPT_FIX, PR_PREEN_OK | PR_NO_OK}, + + /* Superblock has invalid MMP checksum. */ + { PR_0_MMP_CSUM_INVALID, + N_("@S MMP block checksum does not match MMP block. "), + PROMPT_FIX, PR_PREEN_OK | PR_NO_OK}, + /* Pass 1 errors */ /* Pass 1: Checking inodes, blocks, and sizes */ @@ -946,6 +960,46 @@ static struct e2fsck_problem problem_table[] = { N_("@i %i has zero length extent\n\t(@n logical @b %c, physical @b %b)\n"), PROMPT_CLEAR, 0 }, + /* inode checksum does not match inode */ + { PR_1_INODE_CSUM_INVALID, + N_("@i %i checksum does not match @i. "), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* inode passes checks, but checksum does not match inode */ + { PR_1_INODE_ONLY_CSUM_INVALID, + N_("@i %i passes checks, but checksum does not match @i. "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Inode extent block checksum does not match extent */ + { PR_1_EXTENT_CSUM_INVALID, + N_("@i %i extent block checksum does not match extent\n\t(logical @b " + "%c, @n physical @b %b, len %N)\n"), + PROMPT_CLEAR, 0 }, + + /* + * Inode extent block passes checks, but checksum does not match + * extent + */ + { PR_1_EXTENT_ONLY_CSUM_INVALID, + N_("@i %i extent block passes checks, but checksum does not match " + "extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"), + PROMPT_FIX, 0 }, + + /* Extended attribute block checksum for inode does not match. */ + { PR_1_EA_BLOCK_CSUM_INVALID, + N_("Extended attribute @a @b %b checksum for @i %i does not " + "match. "), + PROMPT_CLEAR, 0 }, + + /* + * Extended attribute block passes checks, but checksum for inode does + * not match. + */ + { PR_1_EA_BLOCK_ONLY_CSUM_INVALID, + N_("Extended attribute @a @b %b passes checks, but checksum for " + "@i %i does not match. "), + PROMPT_FIX, 0 }, + /* * Interior extent node logical offset doesn't match first node below it */ @@ -1376,6 +1430,31 @@ static struct e2fsck_problem problem_table[] = { N_("i_file_acl_hi @F %N, @s zero.\n"), PROMPT_CLEAR, PR_PREEN_OK }, + /* htree root node fails checksum */ + { PR_2_HTREE_ROOT_CSUM_INVALID, + N_("@p @h %d: root node fails checksum\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* htree internal node fails checksum */ + { PR_2_HTREE_NODE_CSUM_INVALID, + N_("@p @h %d: internal node fails checksum\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* leaf node fails checksum */ + { PR_2_LEAF_NODE_CSUM_INVALID, + N_("@d @i %i, %B, offset %N: @d fails checksum\n"), + PROMPT_SALVAGE, PR_PREEN_OK }, + + /* leaf node has no checksum */ + { PR_2_LEAF_NODE_MISSING_CSUM, + N_("@d @i %i, %B, offset %N: @d has no checksum\n"), + PROMPT_FIX, PR_PREEN_OK }, + + /* leaf node passes checks but fails checksum */ + { PR_2_LEAF_NODE_ONLY_CSUM_INVALID, + N_("@d @i %i, %B, offset %N: @d passes checks but fails checksum\n"), + PROMPT_FIX, PR_PREEN_OK }, + /* Pass 3 errors */ /* Pass 3: Checking directory connectivity */ @@ -1692,6 +1771,16 @@ 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 }, + + /* 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 aed524dc..d2b6df42 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -250,6 +250,16 @@ struct problem_context { #define PR_0_CHECK_DESC_FAILED 0x000045 /* + * metadata_csum supersedes uninit_bg; both feature bits cannot be set + * simultaneously. + */ +#define PR_0_META_AND_GDT_CSUM_SET 0x000046 + +/* Superblock has invalid MMP checksum. */ +#define PR_0_MMP_CSUM_INVALID 0x000047 + + +/* * Pass 1 errors */ @@ -558,6 +568,24 @@ struct problem_context { /* Extent has zero length */ #define PR_1_EXTENT_LENGTH_ZERO 0x010066 +/* inode checksum does not match inode */ +#define PR_1_INODE_CSUM_INVALID 0x010067 + +/* inode passes checks, but checksum does not match inode */ +#define PR_1_INODE_ONLY_CSUM_INVALID 0x010068 + +/* extent block checksum does not match extent block */ +#define PR_1_EXTENT_CSUM_INVALID 0x010069 + +/* extent block passes checks, but checksum does not match extent block */ +#define PR_1_EXTENT_ONLY_CSUM_INVALID 0x01006A + +/* ea block checksum invalid */ +#define PR_1_EA_BLOCK_CSUM_INVALID 0x01006B + +/* ea block passes checks, but checksum invalid */ +#define PR_1_EA_BLOCK_ONLY_CSUM_INVALID 0x01006C + /* Index start doesn't match start of next extent down */ #define PR_1_EXTENT_INDEX_START_INVALID 0x01006D @@ -825,6 +853,21 @@ 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 + +/* dir leaf node fails checksum */ +#define PR_2_LEAF_NODE_CSUM_INVALID 0x02004B + +/* no space in leaf for checksum */ +#define PR_2_LEAF_NODE_MISSING_CSUM 0x02004C + +/* dir leaf node passes checks, but fails checksum */ +#define PR_2_LEAF_NODE_ONLY_CSUM_INVALID 0x02004D + /* * Pass 3 errors */ @@ -1023,6 +1066,12 @@ 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 + +/* Block bitmap checksum does not match */ +#define PR_5_BLOCK_BITMAP_CSUM_INVALID 0x05001B + /* * Post-Pass 5 errors */ diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c index bdfcd193..d9dc59d2 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) { @@ -193,10 +217,10 @@ static int count_tags(journal_t *journal, struct buffer_head *bh) nr++; tagp += tag_bytes; - if (!(tag->t_flags & cpu_to_be32(JFS_FLAG_SAME_UUID))) + if (!(tag->t_flags & cpu_to_be16(JFS_FLAG_SAME_UUID))) tagp += 16; - if (tag->t_flags & cpu_to_be32(JFS_FLAG_LAST_TAG)) + if (tag->t_flags & cpu_to_be16(JFS_FLAG_LAST_TAG)) break; } @@ -329,7 +353,8 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh, num_blks = count_tags(journal, bh); /* Calculate checksum of the descriptor block. */ - *crc32_sum = crc32_be(*crc32_sum, (void *)bh->b_data, bh->b_size); + *crc32_sum = ext2fs_crc32_be(*crc32_sum, (void *)bh->b_data, + bh->b_size); for (i = 0; i < num_blks; i++) { io_block = (*next_log_block)++; @@ -340,14 +365,54 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh, "%llu in log\n", err, io_block); return 1; } else { - *crc32_sum = crc32_be(*crc32_sum, (void *)obh->b_data, - obh->b_size); + *crc32_sum = ext2fs_crc32_be(*crc32_sum, + (void *)obh->b_data, + obh->b_size); } brelse(obh); } 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 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) & 0xffff; + provided = ext2fs_be16_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) { @@ -361,6 +426,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; /* * First thing is to establish what we expect to find in the log @@ -446,6 +512,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, @@ -476,11 +554,11 @@ 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 long io_block; tag = (journal_block_tag_t *) tagp; - flags = be32_to_cpu(tag->t_flags); + flags = be16_to_cpu(tag->t_flags); io_block = next_log_block++; wrap(journal, next_log_block); @@ -511,6 +589,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 %lld in log\n", + blocknr); + continue; + } + /* Find a buffer for the new * data being restored */ nbh = __getblk(journal->j_fs_dev, @@ -652,6 +743,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; @@ -708,6 +812,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. */ @@ -722,6 +847,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/e2fsck/rehash.c b/e2fsck/rehash.c index 1f0a0b8f..9cfb516f 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -52,6 +52,25 @@ #include "e2fsck.h" #include "problem.h" +/* Schedule a dir to be rebuilt during pass 3A. */ +void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino) +{ + if (!ctx->dirs_to_hash) + ext2fs_u32_list_create(&ctx->dirs_to_hash, 50); + if (ctx->dirs_to_hash) + ext2fs_u32_list_add(ctx->dirs_to_hash, ino); +} + +/* Ask if a dir will be rebuilt during pass 3A. */ +int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino) +{ + if (ctx->options & E2F_OPT_COMPRESS_DIRS) + return 1; + if (!ctx->dirs_to_hash) + return 0; + return ext2fs_u32_list_test(ctx->dirs_to_hash, ino); +} + struct fill_dir_struct { char *buf; struct ext2_inode *inode; @@ -62,6 +81,7 @@ struct fill_dir_struct { int dir_size; int compress; ino_t parent; + ext2_ino_t dir; }; struct hash_entry { @@ -106,7 +126,10 @@ static int fill_dir_block(ext2_filsys fs, dirent = (struct ext2_dir_entry *) dir; (void) ext2fs_set_rec_len(fs, fs->blocksize, dirent); } else { - fd->err = ext2fs_read_dir_block3(fs, *block_nr, dir, 0); + fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; + fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0, + fd->dir); + fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; if (fd->err) return BLOCK_ABORT; } @@ -397,7 +420,8 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs, static errcode_t copy_dir_entries(e2fsck_t ctx, struct fill_dir_struct *fd, - struct out_dir *outdir) + struct out_dir *outdir, + ext2_ino_t ino) { ext2_filsys fs = ctx->fs; errcode_t retval; @@ -408,6 +432,8 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, int i, left; ext2_dirhash_t prev_hash; int offset, slack; + int csum_size = 0; + struct ext2_dir_entry_tail *t; if (ctx->htree_slack_percentage == 255) { profile_get_uint(ctx->profile, "options", @@ -418,6 +444,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, ctx->htree_slack_percentage = 20; } + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + csum_size = sizeof(struct ext2_dir_entry_tail); + outdir->max = 0; retval = alloc_size_dir(fs, outdir, (fd->dir_size / fs->blocksize) + 2); @@ -432,9 +462,9 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, dirent = (struct ext2_dir_entry *) block_start; prev_rec_len = 0; rec_len = 0; - left = fs->blocksize; + left = fs->blocksize - csum_size; slack = fd->compress ? 12 : - (fs->blocksize * ctx->htree_slack_percentage)/100; + ((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100; if (slack < 12) slack = 12; for (i = 0; i < fd->num_array; i++) { @@ -449,12 +479,17 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, if (retval) return retval; } + if (csum_size) { + t = EXT2_DIRENT_TAIL(block_start, + fs->blocksize); + ext2fs_initialize_dirent_tail(fs, t); + } if ((retval = get_next_block(fs, outdir, &block_start))) return retval; offset = 0; } - left = fs->blocksize - offset; + left = (fs->blocksize - csum_size) - offset; dirent = (struct ext2_dir_entry *) (block_start + offset); if (offset == 0) { if (ent->hash == prev_hash) @@ -483,6 +518,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, } if (left) retval = ext2fs_set_rec_len(fs, rec_len + left, dirent); + if (csum_size) { + t = EXT2_DIRENT_TAIL(block_start, fs->blocksize); + ext2fs_initialize_dirent_tail(fs, t); + } return retval; } @@ -495,6 +534,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 +559,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 +576,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; @@ -628,6 +679,7 @@ struct write_dir_struct { errcode_t err; e2fsck_t ctx; int cleared; + ext2_ino_t dir; }; /* @@ -642,11 +694,23 @@ static int write_dir_block(ext2_filsys fs, { struct write_dir_struct *wd = (struct write_dir_struct *) priv_data; blk64_t blk; - char *dir; + char *dir, *buf = 0; if (*block_nr == 0) return 0; - if (blockcnt >= wd->outdir->num) { + if (blockcnt < 0) + return 0; + if (blockcnt < wd->outdir->num) + dir = wd->outdir->buf + (blockcnt * fs->blocksize); + else if (wd->ctx->lost_and_found == wd->dir) { + /* Don't release any extra directory blocks for lost+found */ + wd->err = ext2fs_new_dir_block(fs, 0, 0, &buf); + if (wd->err) + return BLOCK_ABORT; + dir = buf; + wd->outdir->num++; + } else { + /* We don't need this block, so release it */ e2fsck_read_bitmaps(wd->ctx); blk = *block_nr; ext2fs_unmark_block_bitmap2(wd->ctx->block_found_map, blk); @@ -655,11 +719,11 @@ static int write_dir_block(ext2_filsys fs, wd->cleared++; return BLOCK_CHANGED; } - if (blockcnt < 0) - 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 (buf) + ext2fs_free_mem(&buf); + if (wd->err) return BLOCK_ABORT; return 0; @@ -681,6 +745,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); @@ -733,6 +798,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; @@ -792,7 +858,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; @@ -836,7 +902,7 @@ void e2fsck_rehash_directories(e2fsck_t ctx) if (!ctx->dirs_to_hash && !all_dirs) return; - e2fsck_get_lost_and_found(ctx, 0); + (void) e2fsck_get_lost_and_found(ctx, 0); clear_problem_context(&pctx); @@ -864,8 +930,7 @@ void e2fsck_rehash_directories(e2fsck_t ctx) if (!ext2fs_u32_list_iterate(iter, &ino)) break; } - if (ino == ctx->lost_and_found) - continue; + pctx.dir = ino; if (first) { fix_problem(ctx, PR_3A_PASS_HEADER, &pctx); diff --git a/e2fsck/sigcatcher.c b/e2fsck/sigcatcher.c index faafb572..e3925f15 100644 --- a/e2fsck/sigcatcher.c +++ b/e2fsck/sigcatcher.c @@ -391,6 +391,7 @@ void sigcatcher_setup(void) sigaction(SIGILL, &sa, 0); sigaction(SIGBUS, &sa, 0); sigaction(SIGSEGV, &sa, 0); + sigaction(SIGABRT, &sa, 0); } diff --git a/e2fsck/super.c b/e2fsck/super.c index b9870f73..6f1ce3de 100644 --- a/e2fsck/super.c +++ b/e2fsck/super.c @@ -199,9 +199,9 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, ext2fs_iblk_sub_blocks(fs, inode, pb.truncated_blocks); if (ext2fs_file_acl_block(fs, inode)) { - retval = ext2fs_adjust_ea_refcount2(fs, - ext2fs_file_acl_block(fs, inode), - block_buf, -1, &count); + retval = ext2fs_adjust_ea_refcount3(fs, + ext2fs_file_acl_block(fs, inode), + block_buf, -1, &count, ino); if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) { retval = 0; count = 1; @@ -582,14 +582,26 @@ void check_super_block(e2fsck_t ctx) } } + /* Are metadata_csum and uninit_bg both set? */ + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && + EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && + fix_problem(ctx, PR_0_META_AND_GDT_CSUM_SET, &pctx)) { + fs->super->s_feature_ro_compat &= + ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM; + ext2fs_mark_super_dirty(fs); + for (i = 0; i < fs->group_desc_count; i++) + ext2fs_group_desc_csum_set(fs, i); + } + /* * Verify the group descriptors.... */ first_block = sb->s_first_data_block; last_block = ext2fs_blocks_count(sb)-1; - csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + csum_flag = ext2fs_has_group_desc_csum(fs); for (i = 0; i < fs->group_desc_count; i++) { pctx.group = i; @@ -717,6 +729,7 @@ void check_super_block(e2fsck_t ctx) if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) { if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) { uuid_generate(sb->s_uuid); + ext2fs_init_csum_seed(fs); fs->flags |= EXT2_FLAG_DIRTY; fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; } diff --git a/e2fsck/unix.c b/e2fsck/unix.c index 51eeb251..5b705ed6 100644 --- a/e2fsck/unix.c +++ b/e2fsck/unix.c @@ -1149,6 +1149,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; } @@ -1267,6 +1272,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 (retval) { @@ -1744,7 +1750,7 @@ no_journal: } if ((run_result & E2F_FLAG_CANCEL) == 0 && - sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM && + ext2fs_has_group_desc_csum(ctx->fs) && !(ctx->options & E2F_OPT_READONLY)) { retval = ext2fs_set_gdt_csum(ctx->fs); if (retval) { diff --git a/e2fsck/util.c b/e2fsck/util.c index 2b7d2ff8..9eaf5574 100644 --- a/e2fsck/util.c +++ b/e2fsck/util.c @@ -278,7 +278,9 @@ void e2fsck_read_bitmaps(e2fsck_t ctx) old_op = ehandler_operation(_("reading inode and block bitmaps")); e2fsck_set_bitmap_type(fs, EXT2FS_BMAP64_RBTREE, "fs_bitmaps", &save_type); + ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; retval = ext2fs_read_bitmaps(fs); + ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; fs->default_bitmap_type = save_type; ehandler_operation(old_op); if (retval) { diff --git a/lib/blkid/probe.h b/lib/blkid/probe.h index 37e80eff..d6809e10 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/config.h.in b/lib/config.h.in index e14eff4f..eebb75d0 100644 --- a/lib/config.h.in +++ b/lib/config.h.in @@ -12,6 +12,9 @@ /* Define to 1 if debugging ext3/4 journal code */ #undef CONFIG_JBD_DEBUG +/* Define to 1 to enable mmp support */ +#undef CONFIG_MMP + /* Define to 1 to enable quota support */ #undef CONFIG_QUOTA @@ -26,6 +29,12 @@ /* Define to 1 if using `alloca.c'. */ #undef C_ALLOCA +/* Define to 1 to enable bitmap stats. */ +#undef ENABLE_BMAP_STATS + +/* Define to 1 to enable bitmap stats. */ +#undef ENABLE_BMAP_STATS_OPS + /* Define to 1 if ext2 compression enabled */ #undef ENABLE_COMPRESSION diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c index 96912638..70815ef2 100644 --- a/lib/e2p/feature.c +++ b/lib/e2p/feature.c @@ -93,7 +93,7 @@ static struct feature feature_list[] = { "dirdata"}, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_LARGEDIR, "large_dir"}, - { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INLINEDATA, + { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INLINE_DATA, "inline_data"}, { 0, 0, 0 }, }; @@ -108,6 +108,8 @@ static struct feature jrnl_feature_list[] = { "journal_64bit" }, { 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/e2p/ls.c b/lib/e2p/ls.c index f05e16dc..d2e84eb3 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/e2p/pf.c b/lib/e2p/pf.c index e2f8ce50..f116ac3c 100644 --- a/lib/e2p/pf.c +++ b/lib/e2p/pf.c @@ -50,6 +50,7 @@ static struct flags_name flags_array[] = { { EXT4_EXTENTS_FL, "e", "Extents" }, { EXT4_HUGE_FILE_FL, "h", "Huge_file" }, { FS_NOCOW_FL, "C", "No_COW" }, + { EXT4_INLINE_DATA_FL, "N", "Inline_Data" }, { 0, NULL, NULL } }; diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index cc0fc7e7..dc7b0d17 100644 --- a/lib/ext2fs/Makefile.in +++ b/lib/ext2fs/Makefile.in @@ -50,6 +50,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \ freefs.o \ gen_bitmap.o \ gen_bitmap64.o \ + get_num_dirs.o \ get_pathname.o \ getsize.o \ getsectsize.o \ @@ -123,6 +124,7 @@ SRCS= ext2_err.c \ $(srcdir)/freefs.c \ $(srcdir)/gen_bitmap.c \ $(srcdir)/gen_bitmap64.c \ + $(srcdir)/get_num_dirs.c \ $(srcdir)/get_pathname.c \ $(srcdir)/getsize.c \ $(srcdir)/getsectsize.c \ @@ -700,6 +702,13 @@ gen_bitmap64.o: $(srcdir)/gen_bitmap64.c $(top_builddir)/lib/config.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h $(srcdir)/bmap64.h +get_num_dirs.o: $(srcdir)/get_num_dirs.c $(top_builddir)/lib/config.h \ + $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \ + $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ + $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ + $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ + $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ + $(srcdir)/bitops.h get_pathname.o: $(srcdir)/get_pathname.c $(top_builddir)/lib/config.h \ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ diff --git a/lib/ext2fs/alloc.c b/lib/ext2fs/alloc.c index 0c829edb..ce72ffe3 100644 --- a/lib/ext2fs/alloc.c +++ b/lib/ext2fs/alloc.c @@ -36,8 +36,7 @@ static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map, blk64_t blk, super_blk, old_desc_blk, new_desc_blk; int old_desc_blocks; - if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || + if (!ext2fs_has_group_desc_csum(fs) || !(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) return; @@ -83,8 +82,7 @@ static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map, { ext2_ino_t i, ino; - if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || + if (!ext2fs_has_group_desc_csum(fs) || !(ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT))) return; diff --git a/lib/ext2fs/alloc_stats.c b/lib/ext2fs/alloc_stats.c index adec3636..42290842 100644 --- a/lib/ext2fs/alloc_stats.c +++ b/lib/ext2fs/alloc_stats.c @@ -38,8 +38,7 @@ void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, /* We don't strictly need to be clearing the uninit flag if inuse < 0 * (i.e. freeing inodes) but it also means something is bad. */ ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + if (ext2fs_has_group_desc_csum(fs)) { ext2_ino_t first_unused_inode = fs->super->s_inodes_per_group - ext2fs_bg_itable_unused(fs, group) + group * fs->super->s_inodes_per_group + 1; diff --git a/lib/ext2fs/alloc_tables.c b/lib/ext2fs/alloc_tables.c index 9f3d4e04..5e6e556a 100644 --- a/lib/ext2fs/alloc_tables.c +++ b/lib/ext2fs/alloc_tables.c @@ -230,16 +230,19 @@ errcode_t ext2fs_allocate_tables(ext2_filsys fs) dgrp_t i; struct ext2fs_numeric_progress_struct progress; - ext2fs_numeric_progress_init(fs, &progress, NULL, - fs->group_desc_count); + if (fs->progress_ops && fs->progress_ops->init) + (fs->progress_ops->init)(fs, &progress, NULL, + fs->group_desc_count); for (i = 0; i < fs->group_desc_count; i++) { - ext2fs_numeric_progress_update(fs, &progress, i); + if (fs->progress_ops && fs->progress_ops->update) + (fs->progress_ops->update)(fs, &progress, i); retval = ext2fs_allocate_group_table(fs, i, fs->block_map); if (retval) return retval; } - ext2fs_numeric_progress_close(fs, &progress, NULL); + if (fs->progress_ops && fs->progress_ops->close) + (fs->progress_ops->close)(fs, &progress, NULL); return 0; } diff --git a/lib/ext2fs/blkmap64_ba.c b/lib/ext2fs/blkmap64_ba.c index 8eddde9a..73180b0f 100644 --- a/lib/ext2fs/blkmap64_ba.c +++ b/lib/ext2fs/blkmap64_ba.c @@ -310,12 +310,16 @@ static void ba_clear_bmap(ext2fs_generic_bitmap bitmap) (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); } +#ifdef ENABLE_BMAP_STATS static void ba_print_stats(ext2fs_generic_bitmap bitmap) { fprintf(stderr, "%16llu Bytes used by bitarray\n", ((bitmap->real_end - bitmap->start) >> 3) + 1 + sizeof(struct ext2fs_ba_private_struct)); } +#else +static void ba_print_stats(ext2fs_generic_bitmap bitmap) {} +#endif /* Find the first zero bit between start and end, inclusive. */ static errcode_t ba_find_first_zero(ext2fs_generic_bitmap bitmap, diff --git a/lib/ext2fs/blkmap64_rb.c b/lib/ext2fs/blkmap64_rb.c index 9a8938a4..702187fa 100644 --- a/lib/ext2fs/blkmap64_rb.c +++ b/lib/ext2fs/blkmap64_rb.c @@ -41,7 +41,7 @@ struct ext2fs_rb_private { struct bmap_rb_extent *wcursor; struct bmap_rb_extent *rcursor; struct bmap_rb_extent *rcursor_next; -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS __u64 mark_hit; __u64 test_hit; #endif @@ -135,10 +135,8 @@ static void rb_get_new_extent(struct bmap_rb_extent **ext, __u64 start, retval = ext2fs_get_mem(sizeof (struct bmap_rb_extent), &new_ext); - if (retval) { - perror("ext2fs_get_mem"); - exit(1); - } + if (retval) + abort(); new_ext->start = start; new_ext->count = count; @@ -172,7 +170,7 @@ static errcode_t rb_alloc_private_data (ext2fs_generic_bitmap bitmap) bp->rcursor_next = NULL; bp->wcursor = NULL; -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS bp->test_hit = 0; bp->mark_hit = 0; #endif @@ -320,7 +318,7 @@ rb_test_bit(struct ext2fs_rb_private *bp, __u64 bit) goto search_tree; if (bit >= rcursor->start && bit < rcursor->start + rcursor->count) { -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS bp->test_hit++; #endif return 1; @@ -386,7 +384,7 @@ static int rb_insert_extent(__u64 start, __u64 count, if (ext) { if (start >= ext->start && start <= (ext->start + ext->count)) { -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS bp->mark_hit++; #endif goto got_extent; @@ -791,7 +789,7 @@ static void rb_clear_bmap(ext2fs_generic_bitmap bitmap) bp->wcursor = NULL; } -#ifdef BMAP_STATS +#ifdef ENABLE_BMAP_STATS static void rb_print_stats(ext2fs_generic_bitmap bitmap) { struct ext2fs_rb_private *bp; @@ -802,7 +800,7 @@ static void rb_print_stats(ext2fs_generic_bitmap bitmap) __u64 min_size = ULONG_MAX; __u64 size = 0, avg_size = 0; double eff; -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS __u64 mark_all, test_all; double m_hit = 0.0, t_hit = 0.0; #endif @@ -827,7 +825,7 @@ static void rb_print_stats(ext2fs_generic_bitmap bitmap) min_size = 0; eff = (double)((count * sizeof(struct bmap_rb_extent)) << 3) / (bitmap->real_end - bitmap->start); -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS mark_all = bitmap->stats.mark_count + bitmap->stats.mark_ext_count; test_all = bitmap->stats.test_count + bitmap->stats.test_ext_count; if (mark_all) diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c index 33da7d6f..b3eef6c4 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) @@ -230,6 +245,21 @@ void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk) } /* + * Return the inode bitmap checksum of a group + */ +__u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + __u32 csum; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + csum = gdp->bg_inode_bitmap_csum_lo; + if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) + csum |= ((__u32)gdp->bg_inode_bitmap_csum_hi << 16); + return csum; +} + +/* * Return the inode bitmap block of a group */ blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group) diff --git a/lib/ext2fs/bmap64.h b/lib/ext2fs/bmap64.h index f44d379c..c5384c95 100644 --- a/lib/ext2fs/bmap64.h +++ b/lib/ext2fs/bmap64.h @@ -13,7 +13,7 @@ struct ext2_bmap_statistics { int type; struct timeval created; -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS unsigned long copy_count; unsigned long resize_count; unsigned long mark_count; @@ -33,7 +33,7 @@ struct ext2_bmap_statistics { unsigned long mark_seq; unsigned long test_seq; -#endif /* BMAP_STATS_OPS */ +#endif /* ENABLE_BMAP_STATS_OPS */ }; @@ -48,7 +48,7 @@ struct ext2fs_struct_generic_bitmap { char *description; void *private; errcode_t base_error_code; -#ifdef BMAP_STATS +#ifdef ENABLE_BMAP_STATS struct ext2_bmap_statistics stats; #endif }; diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c index 973c2a23..22cecb61 100644 --- a/lib/ext2fs/closefs.c +++ b/lib/ext2fs/closefs.c @@ -246,15 +246,19 @@ static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, blk_t group_block, struct ext2_super_block *super_shadow) { + errcode_t retval; dgrp_t sgrp = group; if (sgrp > ((1 << 16) - 1)) sgrp = (1 << 16) - 1; + + super_shadow->s_block_group_nr = sgrp; #ifdef WORDS_BIGENDIAN - super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); -#else - fs->super->s_block_group_nr = sgrp; + ext2fs_swap_super(super_shadow); #endif + retval = ext2fs_superblock_csum_set(fs, super_shadow); + if (retval) + return retval; return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE, super_shadow); @@ -288,6 +292,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); @@ -297,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); @@ -317,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 @@ -340,14 +358,16 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags) else old_desc_blocks = fs->desc_blocks; - ext2fs_numeric_progress_init(fs, &progress, NULL, - fs->group_desc_count); + if (fs->progress_ops && fs->progress_ops->init) + (fs->progress_ops->init)(fs, &progress, NULL, + fs->group_desc_count); for (i = 0; i < fs->group_desc_count; i++) { blk64_t super_blk, old_desc_blk, new_desc_blk; - ext2fs_numeric_progress_update(fs, &progress, i); + if (fs->progress_ops && fs->progress_ops->update) + (fs->progress_ops->update)(fs, &progress, i); ext2fs_super_and_bgd_loc2(fs, i, &super_blk, &old_desc_blk, &new_desc_blk, 0); @@ -376,19 +396,8 @@ 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; - } + if (fs->progress_ops && fs->progress_ops->close) + (fs->progress_ops->close)(fs, &progress, NULL); write_primary_superblock_only: /* @@ -407,6 +416,10 @@ write_primary_superblock_only: ext2fs_swap_super(super_shadow); #endif + retval = ext2fs_superblock_csum_set(fs, super_shadow); + if (retval) + return retval; + if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC)) retval = io_channel_flush(fs->io); retval = write_primary_superblock(fs, super_shadow); diff --git a/lib/ext2fs/crc32c.c b/lib/ext2fs/crc32c.c index b54fc279..4e72eda9 100644 --- a/lib/ext2fs/crc32c.c +++ b/lib/ext2fs/crc32c.c @@ -32,7 +32,6 @@ #include <stdint.h> #include <stdlib.h> #include <stdio.h> -#define __force #define min(x, y) ((x) > (y) ? (y) : (x)) #define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) #define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1) @@ -58,400 +57,189 @@ #endif #if CRC_LE_BITS > 8 -# define tole(x) (__force uint32_t) __constant_cpu_to_le32(x) +# define tole(x) __constant_cpu_to_le32(x) #else # define tole(x) (x) #endif #if CRC_BE_BITS > 8 -# define tobe(x) (__force uint32_t) __constant_cpu_to_be32(x) +# define tobe(x) __constant_cpu_to_be32(x) #else # define tobe(x) (x) #endif #include "crc32c_table.h" -#if CRC_LE_BITS == 32 -/* slice by 4 algorithm */ -static uint32_t crc32c_le_body(uint32_t crc, uint8_t const *buf, size_t len) -{ - const uint8_t *p8; - const uint32_t *p32; - size_t init_bytes; - size_t words; - size_t end_bytes; - size_t i; - uint32_t q; - uint8_t i0, i1, i2, i3; - - crc = (__force uint32_t) __cpu_to_le32(crc); - - /* unroll loop into 'init_bytes' odd bytes followed by - * 'words' aligned 4 byte words followed by - * 'end_bytes' odd bytes at the end */ - p8 = buf; - p32 = (uint32_t *)PTR_ALIGN(p8, 4); - init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len); - words = (len - init_bytes) >> 2; - end_bytes = (len - init_bytes) & 3; - - for (i = 0; i < init_bytes; i++) { -#ifndef WORDS_BIGENDIAN - i0 = *p8++ ^ crc; - crc = t0_le[i0] ^ (crc >> 8); -#else - i0 = *p8++ ^ (crc >> 24); - crc = t0_le[i0] ^ (crc << 8); -#endif - } - - /* using pre-increment below slightly faster */ - p32--; - - for (i = 0; i < words; i++) { -#ifndef WORDS_BIGENDIAN - q = *++p32 ^ crc; - i3 = q; - i2 = q >> 8; - i1 = q >> 16; - i0 = q >> 24; - crc = t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0]; -#else - q = *++p32 ^ crc; - i3 = q >> 24; - i2 = q >> 16; - i1 = q >> 8; - i0 = q; - crc = t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0]; -#endif - } +#if CRC_LE_BITS > 8 || CRC_BE_BITS > 8 - p8 = (uint8_t *)(++p32); - - for (i = 0; i < end_bytes; i++) { -#ifndef WORDS_BIGENDIAN - i0 = *p8++ ^ crc; - crc = t0_le[i0] ^ (crc >> 8); -#else - i0 = *p8++ ^ (crc >> 24); - crc = t0_le[i0] ^ (crc << 8); -#endif - } - - return __le32_to_cpu((__force __le32)crc); -} -#endif - -#if CRC_BE_BITS == 32 -static uint32_t crc32c_be_body(uint32_t crc, uint8_t const *buf, size_t len) +/* implements slicing-by-4 or slicing-by-8 algorithm */ +static inline uint32_t +crc32_body(uint32_t crc, unsigned char const *buf, size_t len, + const uint32_t (*tab)[256]) { - const uint8_t *p8; - const uint32_t *p32; - size_t init_bytes; - size_t words; - size_t end_bytes; - size_t i; - uint32_t q; - uint8_t i0, i1, i2, i3; - - crc = (__force uint32_t) __cpu_to_be32(crc); - - p8 = buf; - p32 = (uint32_t *)PTR_ALIGN(p8, 4); - init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len); - words = (len - init_bytes) >> 2; - end_bytes = (len - init_bytes) & 3; - - for (i = 0; i < init_bytes; i++) { -#ifndef WORDS_BIGENDIAN - i0 = *p8++ ^ crc; - crc = t0_be[i0] ^ (crc >> 8); -#else - i0 = *p8++ ^ (crc >> 24); - crc = t0_be[i0] ^ (crc << 8); -#endif - } - - p32--; - - for (i = 0; i < words; i++) { -#ifndef WORDS_BIGENDIAN - q = *++p32 ^ crc; - i3 = q; - i2 = q >> 8; - i1 = q >> 16; - i0 = q >> 24; - crc = t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0]; -#else - q = *++p32 ^ crc; - i3 = q >> 24; - i2 = q >> 16; - i1 = q >> 8; - i0 = q; - crc = t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0]; -#endif - } - - p8 = (uint8_t *)(++p32); - - for (i = 0; i < end_bytes; i++) { -#ifndef WORDS_BIGENDIAN - i0 = *p8++ ^ crc; - crc = t0_be[i0] ^ (crc >> 8); -#else - i0 = *p8++ ^ (crc >> 24); - crc = t0_be[i0] ^ (crc << 8); -#endif - } - - return __be32_to_cpu((__force __be32)crc); -} -#endif - -#if CRC_LE_BITS == 64 -/* slice by 8 algorithm */ -static uint32_t crc32c_le_body(uint32_t crc, uint8_t const *buf, size_t len) -{ - const uint8_t *p8; - const uint32_t *p32; - size_t init_bytes; - size_t words; - size_t end_bytes; - size_t i; - uint32_t q; - uint8_t i0, i1, i2, i3; - - crc = (__force uint32_t) __cpu_to_le32(crc); - - p8 = buf; - p32 = (uint32_t *)PTR_ALIGN(p8, 8); - init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len); - words = (len - init_bytes) >> 3; - end_bytes = (len - init_bytes) & 7; - - for (i = 0; i < init_bytes; i++) { -#ifndef WORDS_BIGENDIAN - i0 = *p8++ ^ crc; - crc = t0_le[i0] ^ (crc >> 8); -#else - i0 = *p8++ ^ (crc >> 24); - crc = t0_le[i0] ^ (crc << 8); -#endif - } - - p32--; - - for (i = 0; i < words; i++) { -#ifndef WORDS_BIGENDIAN - q = *++p32 ^ crc; - i3 = q; - i2 = q >> 8; - i1 = q >> 16; - i0 = q >> 24; - crc = t7_le[i3] ^ t6_le[i2] ^ t5_le[i1] ^ t4_le[i0]; - - q = *++p32; - i3 = q; - i2 = q >> 8; - i1 = q >> 16; - i0 = q >> 24; - crc ^= t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0]; -#else - q = *++p32 ^ crc; - i3 = q >> 24; - i2 = q >> 16; - i1 = q >> 8; - i0 = q; - crc = t7_le[i3] ^ t6_le[i2] ^ t5_le[i1] ^ t4_le[i0]; - - q = *++p32; - i3 = q >> 24; - i2 = q >> 16; - i1 = q >> 8; - i0 = q; - crc ^= t3_le[i3] ^ t2_le[i2] ^ t1_le[i1] ^ t0_le[i0]; -#endif - } - - p8 = (uint8_t *)(++p32); - - for (i = 0; i < end_bytes; i++) { -#ifndef WORDS_BIGENDIAN - i0 = *p8++ ^ crc; - crc = t0_le[i0] ^ (crc >> 8); -#else - i0 = *p8++ ^ (crc >> 24); - crc = t0_le[i0] ^ (crc << 8); -#endif - } - - return __le32_to_cpu(crc); -} -#endif - -#if CRC_BE_BITS == 64 -static uint32_t crc32c_be_body(uint32_t crc, uint8_t const *buf, size_t len) -{ - const uint8_t *p8; - const uint32_t *p32; - size_t init_bytes; - size_t words; - size_t end_bytes; - size_t i; +# ifndef WORDS_BIGENDIAN +# define DO_CRC(x) (crc = t0[(crc ^ (x)) & 255] ^ (crc >> 8)) +# define DO_CRC4 (t3[(q) & 255] ^ t2[(q >> 8) & 255] ^ \ + t1[(q >> 16) & 255] ^ t0[(q >> 24) & 255]) +# define DO_CRC8 (t7[(q) & 255] ^ t6[(q >> 8) & 255] ^ \ + t5[(q >> 16) & 255] ^ t4[(q >> 24) & 255]) +# else +# define DO_CRC(x) (crc = t0[((crc >> 24) ^ (x)) & 255] ^ (crc << 8)) +# define DO_CRC4 (t0[(q) & 255] ^ t1[(q >> 8) & 255] ^ \ + t2[(q >> 16) & 255] ^ t3[(q >> 24) & 255]) +# define DO_CRC8 (t4[(q) & 255] ^ t5[(q >> 8) & 255] ^ \ + t6[(q >> 16) & 255] ^ t7[(q >> 24) & 255]) +# endif + const uint32_t *b; + size_t rem_len; + const uint32_t *t0 = tab[0], *t1 = tab[1], *t2 = tab[2], *t3 = tab[3]; + const uint32_t *t4 = tab[4], *t5 = tab[5], *t6 = tab[6], *t7 = tab[7]; uint32_t q; - uint8_t i0, i1, i2, i3; - - crc = (__force uint32_t) __cpu_to_be32(crc); - p8 = buf; - p32 = (uint32_t *)PTR_ALIGN(p8, 8); - init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len); - words = (len - init_bytes) >> 3; - end_bytes = (len - init_bytes) & 7; - - for (i = 0; i < init_bytes; i++) { -#ifndef WORDS_BIGENDIAN - i0 = *p8++ ^ crc; - crc = t0_be[i0] ^ (crc >> 8); -#else - i0 = *p8++ ^ (crc >> 24); - crc = t0_be[i0] ^ (crc << 8); -#endif + /* Align it */ + if (unlikely((long)buf & 3 && len)) { + do { + DO_CRC(*buf++); + } while ((--len) && ((long)buf)&3); } - p32--; - - for (i = 0; i < words; i++) { -#ifndef WORDS_BIGENDIAN - q = *++p32 ^ crc; - i3 = q; - i2 = q >> 8; - i1 = q >> 16; - i0 = q >> 24; - crc = t7_be[i3] ^ t6_be[i2] ^ t5_be[i1] ^ t4_be[i0]; - - q = *++p32; - i3 = q; - i2 = q >> 8; - i1 = q >> 16; - i0 = q >> 24; - crc ^= t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0]; -#else - q = *++p32 ^ crc; - i3 = q >> 24; - i2 = q >> 16; - i1 = q >> 8; - i0 = q; - crc = t7_be[i3] ^ t6_be[i2] ^ t5_be[i1] ^ t4_be[i0]; +# if CRC_LE_BITS == 32 + rem_len = len & 3; + len = len >> 2; +# else + rem_len = len & 7; + len = len >> 3; +# endif - q = *++p32; - i3 = q >> 24; - i2 = q >> 16; - i1 = q >> 8; - i0 = q; - crc ^= t3_be[i3] ^ t2_be[i2] ^ t1_be[i1] ^ t0_be[i0]; -#endif + b = (const uint32_t *)buf; + for (--b; len; --len) { + q = crc ^ *++b; /* use pre increment for speed */ +# if CRC_LE_BITS == 32 + crc = DO_CRC4; +# else + crc = DO_CRC8; + q = *++b; + crc ^= DO_CRC4; +# endif } - - p8 = (uint8_t *)(++p32); - - for (i = 0; i < end_bytes; i++) { -#ifndef WORDS_BIGENDIAN - i0 = *p8++ ^ crc; - crc = t0_be[i0] ^ (crc >> 8); -#else - i0 = *p8++ ^ (crc >> 24); - crc = t0_be[i0] ^ (crc << 8); -#endif + len = rem_len; + /* And the last few bytes */ + if (len) { + uint8_t *p = (uint8_t *)(b + 1) - 1; + do { + DO_CRC(*++p); /* use pre increment for speed */ + } while (--len); } - - return __be32_to_cpu(crc); + return crc; +#undef DO_CRC +#undef DO_CRC4 +#undef DO_CRC8 } #endif /** - * crc32c_le() - Calculate bitwise little-endian CRC32c. - * @crc: seed value for computation. ~0 for ext4, sometimes 0 for - * other uses, or the previous crc32c value if computing incrementally. + * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 + * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for + * other uses, or the previous crc32 value if computing incrementally. * @p: pointer to buffer over which CRC is run * @len: length of buffer @p */ -uint32_t ext2fs_crc32c_le(uint32_t crc, unsigned char const *p, size_t len) +static inline uint32_t crc32_le_generic(uint32_t crc, unsigned char const *p, + size_t len, const uint32_t (*tab)[256], + uint32_t polynomial) { #if CRC_LE_BITS == 1 int i; while (len--) { crc ^= *p++; for (i = 0; i < 8; i++) - crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0); } # elif CRC_LE_BITS == 2 while (len--) { crc ^= *p++; - crc = (crc >> 2) ^ t0_le[crc & 0x03]; - crc = (crc >> 2) ^ t0_le[crc & 0x03]; - crc = (crc >> 2) ^ t0_le[crc & 0x03]; - crc = (crc >> 2) ^ t0_le[crc & 0x03]; + crc = (crc >> 2) ^ tab[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; } # elif CRC_LE_BITS == 4 while (len--) { crc ^= *p++; - crc = (crc >> 4) ^ t0_le[crc & 0x0f]; - crc = (crc >> 4) ^ t0_le[crc & 0x0f]; + crc = (crc >> 4) ^ tab[0][crc & 15]; + crc = (crc >> 4) ^ tab[0][crc & 15]; } # elif CRC_LE_BITS == 8 + /* aka Sarwate algorithm */ while (len--) { crc ^= *p++; - crc = (crc >> 8) ^ t0_le[crc & 0xff]; + crc = (crc >> 8) ^ tab[0][crc & 255]; } # else - crc = crc32c_le_body(crc, p, len); -# endif + crc = __cpu_to_le32(crc); + crc = crc32_body(crc, p, len, tab); + crc = __le32_to_cpu(crc); +#endif return crc; } +uint32_t ext2fs_crc32c_le(uint32_t crc, unsigned char const *p, size_t len) +{ + return crc32_le_generic(crc, p, len, crc32ctable_le, CRC32C_POLY_LE); +} + /** - * crc32c_be() - Calculate bitwise big-endian CRC32c. - * @crc: seed value for computation. ~0 for ext4, sometimes 0 for - * other uses, or the previous crc32c value if computing incrementally. + * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 + * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for + * other uses, or the previous crc32 value if computing incrementally. * @p: pointer to buffer over which CRC is run * @len: length of buffer @p */ -uint32_t ext2fs_crc32c_be(uint32_t crc, unsigned char const *p, size_t len) +static inline uint32_t crc32_be_generic(uint32_t crc, unsigned char const *p, + size_t len, const uint32_t (*tab)[256], + uint32_t polynomial) { #if CRC_BE_BITS == 1 int i; while (len--) { crc ^= *p++ << 24; for (i = 0; i < 8; i++) - crc = (crc << 1) ^ - ((crc & 0x80000000) ? CRCPOLY_BE : 0); + crc = + (crc << 1) ^ ((crc & 0x80000000) ? polynomial : + 0); } # elif CRC_BE_BITS == 2 while (len--) { crc ^= *p++ << 24; - crc = (crc << 2) ^ t0_be[crc >> 30]; - crc = (crc << 2) ^ t0_be[crc >> 30]; - crc = (crc << 2) ^ t0_be[crc >> 30]; - crc = (crc << 2) ^ t0_be[crc >> 30]; + crc = (crc << 2) ^ tab[0][crc >> 30]; + crc = (crc << 2) ^ tab[0][crc >> 30]; + crc = (crc << 2) ^ tab[0][crc >> 30]; + crc = (crc << 2) ^ tab[0][crc >> 30]; } # elif CRC_BE_BITS == 4 while (len--) { crc ^= *p++ << 24; - crc = (crc << 4) ^ t0_be[crc >> 28]; - crc = (crc << 4) ^ t0_be[crc >> 28]; + crc = (crc << 4) ^ tab[0][crc >> 28]; + crc = (crc << 4) ^ tab[0][crc >> 28]; } # elif CRC_BE_BITS == 8 while (len--) { crc ^= *p++ << 24; - crc = (crc << 8) ^ t0_be[crc >> 24]; + crc = (crc << 8) ^ tab[0][crc >> 24]; } # else - crc = crc32c_be_body(crc, p, len); + crc = __cpu_to_be32(crc); + crc = crc32_body(crc, p, len, tab); + crc = __be32_to_cpu(crc); # endif return crc; } +uint32_t ext2fs_crc32_be(uint32_t crc, unsigned char const *p, size_t len) +{ + return crc32_be_generic(crc, p, len, crc32table_be, CRCPOLY_BE); +} + #ifdef UNITTEST static uint8_t test_buf[] = { 0xd9, 0xd7, 0x6a, 0x13, 0x3a, 0xb1, 0x05, 0x48, @@ -972,137 +760,137 @@ static struct crc_test { uint32_t crc; /* random starting crc */ uint32_t start; /* random offset in buf */ uint32_t length; /* random length of test */ - uint32_t crc_le; /* expected crc32_le result */ - uint32_t crc_be; /* expected crc32_be result */ + uint32_t crc32c_le; /* expected crc32c_le result */ + uint32_t crc32_be; /* expected crc32_be result */ } test[] = { - {0xffffffff, 0x00000000, 0x00001000, 0x13934bef, 0x14f3b75f}, - {0xfe7328ea, 0x00000763, 0x00000717, 0xed2c0d70, 0x57531214}, - {0x4c40684e, 0x00000721, 0x0000011e, 0xd7f46ccc, 0xedf12ec3}, - {0x6b487f90, 0x00000264, 0x000007bc, 0x759e9939, 0x9af8e387}, - {0x9f5810db, 0x00000afa, 0x00000255, 0x2685197f, 0x716de6ed}, - {0xb15c4755, 0x00000d5b, 0x000002a4, 0xd8fadcb5, 0xfc34ae3f}, - {0x06518253, 0x00000ffb, 0x00000004, 0xabee2433, 0xfa30ac9e}, - {0xd9e71c55, 0x00000a2a, 0x00000259, 0x96682af2, 0xe5907ea3}, - {0x0c1ae843, 0x00000ce4, 0x0000031b, 0x7b637c43, 0xe7f71b04}, - {0xec3cd517, 0x000002ff, 0x00000566, 0x5d719a77, 0xed16e045}, - {0x77828e95, 0x0000067f, 0x0000038f, 0x43ee5b6c, 0x35999927}, - {0xec87b4e3, 0x00000d1c, 0x000002e3, 0x2ddd2eee, 0x9452d3f8}, - {0x412158bb, 0x00000eee, 0x00000111, 0x67b38ba2, 0x177976d0}, - {0x2e52de3e, 0x00000c4a, 0x000003b5, 0xbcc5d61d, 0xf60fee71}, - {0x6ddaae8b, 0x00000d99, 0x00000266, 0x8b535544, 0x1dab8596}, - {0x049b6cb1, 0x000009c5, 0x000000b0, 0xfc22cabc, 0x47ebc954}, - {0x77d4b954, 0x0000028a, 0x000007fa, 0x71d00923, 0x905585ef}, - {0x5e192355, 0x00000ac1, 0x000001fa, 0xb966b81a, 0x33c12903}, - {0x7d80b71d, 0x00000213, 0x000001e0, 0x2bba371a, 0x5f4bd8d9}, - {0x01f6f1e4, 0x000001d6, 0x00000395, 0xb7e8a647, 0x2a7943a1}, - {0x1dfabb13, 0x00000e14, 0x000001eb, 0x53917fba, 0x8dee1e5d}, - {0xb00a4449, 0x00000bf6, 0x00000409, 0xedecb577, 0x628e087d}, - {0x7ecd3981, 0x0000083f, 0x0000016b, 0xefef62b9, 0xda4f94e6}, - {0xf8f330d2, 0x000004be, 0x00000757, 0x9357c9f3, 0x8e2d5c2f}, - {0x03c38af2, 0x00000d23, 0x000002dc, 0x360fa8c0, 0x6294c0d6}, - {0x687bb79b, 0x00000f3d, 0x000000c2, 0x448d3be2, 0x08f48f3a}, - {0x6710f550, 0x000009e9, 0x00000603, 0xdbfd1998, 0xc950ac29}, - {0x873171d1, 0x00000787, 0x000004d5, 0xab7f1b62, 0xe66896ab}, - {0x373b1314, 0x00000f0f, 0x000000f0, 0x184098ab, 0x4038e674}, - {0x90fad9cd, 0x00000ead, 0x00000152, 0x23ce52ff, 0x9eff3974}, - {0x19676fe7, 0x0000007d, 0x0000070d, 0xf8a76f1e, 0xfbc5c8a9}, - {0x89facd45, 0x000005f3, 0x00000473, 0x4331a006, 0xb8f0f0cc}, - {0x6f173747, 0x00000fc3, 0x0000003c, 0xb012f08e, 0x5126e378}, - {0x4b44a106, 0x0000075a, 0x0000008b, 0xf6f7ac38, 0xf9b1781b}, - {0xb620ad06, 0x00000774, 0x0000017e, 0xd34558e6, 0xb175edd3}, - {0x976f21e9, 0x000008d7, 0x0000034a, 0xe533aa3a, 0x1e4367b9}, - {0x687628c0, 0x000006c5, 0x0000061b, 0x3a840b15, 0xfb5989a0}, - {0xe24ac108, 0x00000cd0, 0x0000032f, 0x51010ae8, 0xcdd8f182}, - {0x361c44a3, 0x00000304, 0x00000719, 0xfd7bd481, 0x12de540f}, - {0xd93ff95e, 0x00000db7, 0x0000008e, 0xcfbbc304, 0x42eecd5a}, - {0xed752d12, 0x00000883, 0x00000091, 0x65a6c868, 0x9ebfa578}, - {0xb4ff4b54, 0x000003d3, 0x000001c1, 0xf82597e7, 0xa8ad2b19}, - {0x111b520f, 0x00000708, 0x000000eb, 0xc3e109f3, 0x323ace17}, - {0x62c806f2, 0x00000ba3, 0x0000045c, 0x874d3a72, 0xaf1a1360}, - {0x40d97470, 0x000005e1, 0x0000058d, 0x87a9684f, 0x524244a8}, - {0x4312179c, 0x00000056, 0x0000070e, 0x809a00f5, 0xf9e940b0}, - {0x13d5f84c, 0x00000a2d, 0x00000104, 0xf3d27578, 0x5d33341c}, - {0x1f302cb2, 0x00000151, 0x00000014, 0x1e162693, 0x53c3cfc3}, - {0xe491db24, 0x00000600, 0x000006f6, 0x7ff09615, 0xa300ecf7}, - {0xf9a98069, 0x000002ba, 0x000002ad, 0x01af7387, 0x31c0911e}, - {0xe9c477ad, 0x0000015f, 0x00000778, 0x6facf9a0, 0x1993b688}, - {0x353f32b2, 0x0000087c, 0x00000783, 0x6cc964ea, 0x418db561}, - {0x78e1b24f, 0x00000650, 0x000006a8, 0xb3bb7c27, 0xf2aad006}, - {0x61aa400e, 0x00000049, 0x00000254, 0xb8cd1681, 0x79150b15}, - {0xb84b10b0, 0x00000f73, 0x0000008c, 0x406a6450, 0x0c705222}, - {0x9fa99c9c, 0x00000a7c, 0x000004d7, 0xfb3d21b4, 0xe4e789df}, - {0x3fc9ebe3, 0x00000cd9, 0x000000d6, 0x43803f9c, 0x5a152be5}, - {0x529879cd, 0x000002f2, 0x00000595, 0x78b4c6a6, 0xf7236ec4}, - {0x3a933019, 0x00000516, 0x00000266, 0xdcb45436, 0x2c7935f5}, - {0x887b4977, 0x00000227, 0x0000038d, 0xc5f7c3d9, 0x0d6d7df6}, - {0x770745de, 0x000008c6, 0x00000739, 0xf69145e8, 0x47d5efc9}, - {0x28be3b47, 0x00000c46, 0x0000032b, 0x764c028f, 0x1eb70d64}, - {0x5013a050, 0x00000cf6, 0x00000309, 0xea8fe164, 0x186affa4}, - {0x2ec4c9ba, 0x000006e8, 0x0000078d, 0xa35557a9, 0xb41f49ec}, - {0xa9f950c9, 0x00000d33, 0x000002cc, 0x41ea8618, 0xab8dfae3}, - {0x5b520229, 0x000007b2, 0x00000484, 0x44569f1f, 0x607a8052}, - {0xd8dcbbfc, 0x0000002f, 0x0000048c, 0xdb88ab8b, 0xf1c411f1}, - {0x25529792, 0x00000d1d, 0x000002e2, 0x20cda404, 0x32683a2d}, - {0x9f3f6d71, 0x00000238, 0x0000079a, 0x0720443e, 0x4b8ba2ff}, - {0x64121215, 0x000007ff, 0x0000038f, 0x6aacff2c, 0x3b84233b}, - {0xfb6cdde0, 0x00000ef8, 0x00000107, 0xbd43a0f1, 0x926624d0}, - {0x221c9d6f, 0x000007b6, 0x0000014f, 0xb67f834b, 0x2bdedda4}, - {0x030e1de4, 0x00000836, 0x000004b4, 0x0d67d26a, 0x75a73b73}, - {0xb56fa6cf, 0x00000c07, 0x000003f8, 0x60601ac1, 0x10a43f35}, - {0xb55c89f5, 0x0000098e, 0x000001d4, 0x2400efbe, 0x006e28eb}, - {0x5e90b6d5, 0x0000070b, 0x000003ea, 0x3bb5d6ea, 0xb175fa6b}, - {0x2a7045ae, 0x00000961, 0x00000633, 0xfca89e4b, 0x962cd6d2}, - {0x8b374ea9, 0x000006ba, 0x00000780, 0xbce036ed, 0x4dc8279b}, - {0x8bd90bc9, 0x00000562, 0x00000369, 0xcb26a24b, 0x50dee743}, - {0x5b1b1762, 0x000000fd, 0x0000051a, 0x33cdda07, 0xee75ff7b}, - {0xa4153555, 0x0000058f, 0x000005c7, 0xbe50eeca, 0xe73fffcc}, - {0x0be1f931, 0x00000651, 0x00000672, 0x95a25753, 0x4ad6270f}, - {0xb7e78618, 0x00000a7f, 0x000002bb, 0xe06bcc1c, 0x1a35ee59}, - {0x4a9bc41b, 0x00000e51, 0x000001ae, 0x709e8d2c, 0x75080ca8}, - {0xfc359d13, 0x00000440, 0x000002f8, 0x0a58451f, 0x6fa3cfbf}, - {0x5aa48619, 0x000006d1, 0x00000284, 0x928ead83, 0xbd600efc}, - {0xa609afa8, 0x0000053e, 0x00000272, 0xb048c141, 0x184f80bb}, - {0x3f108afb, 0x00000949, 0x00000150, 0x9a6bb5bc, 0x0ea02be1}, - {0x79bec2d3, 0x000008ed, 0x00000712, 0x32692d57, 0x2eb13289}, - {0x9429e067, 0x00000bc3, 0x0000043c, 0x5295ceff, 0x8a9014a7}, - {0xae58b96a, 0x0000082d, 0x000007d2, 0xc2a681ba, 0x6af94089}, - {0x95df24be, 0x00000985, 0x000004c1, 0x3a287765, 0x379fcb42}, - {0x5e94976f, 0x00000596, 0x000004ed, 0xff00c489, 0x991fc1f5}, - {0xf5e5f1de, 0x00000d31, 0x000002ce, 0x35f28e91, 0x543def1a}, - {0xa2c219cf, 0x00000a3c, 0x00000374, 0x707d21eb, 0xa6d28bc1}, - {0xf21b6ceb, 0x00000919, 0x00000135, 0x0847fb8b, 0x224468c2}, - {0xaa988728, 0x00000787, 0x00000771, 0x885aeaa4, 0x814db00b}, - {0xaa5dfaac, 0x000003e5, 0x0000051b, 0x52c48ab7, 0x725bef8a}, - {0x0a053968, 0x00000d2a, 0x000002d5, 0x7a90256d, 0xc53b9402}, - {0x1421dc20, 0x00000eef, 0x00000110, 0x97d6da24, 0x10846935}, - {0xb47c2166, 0x00000a6a, 0x00000209, 0xcfd6cc52, 0x46e2797e}, - {0x77dd1955, 0x000000de, 0x00000266, 0xba74bcaa, 0x4fa3fe9c}, - {0x68a03cc2, 0x0000082f, 0x000007b0, 0x752bd5d8, 0x4f760c63}, - {0x0226b0a3, 0x00000a5f, 0x000005a0, 0x82de4970, 0x8ee1310e}, - {0x637bf3b1, 0x00000d93, 0x0000026c, 0x5c7115cb, 0x9f6a0ced}, - {0x3b120edf, 0x00000c13, 0x000003ec, 0x80d7d20f, 0x241657d5}, - {0xe2456780, 0x000002eb, 0x00000641, 0xc0a5d289, 0x74df96b4}, - {0x9b2e7125, 0x00000c0c, 0x000003f3, 0xcc15f57e, 0x03e290bf}, - {0x153033ef, 0x00000787, 0x000006b6, 0x3cde443b, 0x7bf1d121}, - {0x18458b3f, 0x0000066c, 0x00000561, 0x9a2bd8c6, 0x9d564bef}, - {0x4ff9d4b9, 0x00000c8f, 0x0000033a, 0xd0ee6d6d, 0xee00aa0b}, - {0xdf84b5d9, 0x00000802, 0x0000029a, 0xdab0d74a, 0xd0cb63dc}, - {0x81ee15df, 0x000003ce, 0x00000725, 0x9942e2de, 0xe48fb26b}, - {0x5c768e04, 0x00000afd, 0x00000160, 0x36110831, 0x8dc74483}, - {0xe5e18094, 0x00000b4b, 0x000000a0, 0xffa3e4a7, 0xc0145e1b}, - {0xed7263b6, 0x00000d0d, 0x000002f2, 0xb0006a35, 0x5468ae3a}, - {0x5bfde7d7, 0x000006fb, 0x00000554, 0xa4193b76, 0xb73d34b2}, - {0x67f4a743, 0x00000b85, 0x0000047a, 0xf05c8d8f, 0x4f843e49}, - {0xf13bdf22, 0x00000ff7, 0x00000008, 0x816351eb, 0x41f537f6}, - {0x08ecc608, 0x00000d5d, 0x00000098, 0x90492772, 0xf5172204}, - {0x296f52ba, 0x000004f9, 0x00000788, 0x5e5a4896, 0xe01d5b46}, - {0xbe4624c2, 0x00000427, 0x000004ef, 0xcd267b94, 0x7b9069f4}, - {0x906f7c7c, 0x00000a05, 0x0000003f, 0x03fcfc33, 0x7b6ff563}, - {0x8f7b323e, 0x00000458, 0x000004c7, 0xcd4969c8, 0xd4c22ada}, - {0x88d6593d, 0x00000597, 0x000005b5, 0xf199cd3b, 0x5c3e8ca2}, - {0x978a7768, 0x00000268, 0x000001d3, 0xb28c95bd, 0x49a2cc67}, - {0x857a621e, 0x000007a7, 0x000003a8, 0xf4bf84ab, 0xde26f369}, - {0xb0e121ef, 0x000005be, 0x00000644, 0x28747c14, 0x61d4dc6b}, + {0xffffffff, 0x00000000, 0x00001000, 0x13934bef, 0xd8ddcdc3}, + {0xfe7328ea, 0x00000763, 0x00000717, 0xed2c0d70, 0xc863aef8}, + {0x4c40684e, 0x00000721, 0x0000011e, 0xd7f46ccc, 0x173a11c4}, + {0x6b487f90, 0x00000264, 0x000007bc, 0x759e9939, 0xd6307c56}, + {0x9f5810db, 0x00000afa, 0x00000255, 0x2685197f, 0x2e5c9201}, + {0xb15c4755, 0x00000d5b, 0x000002a4, 0xd8fadcb5, 0xf682c4be}, + {0x06518253, 0x00000ffb, 0x00000004, 0xabee2433, 0x3d8abdf9}, + {0xd9e71c55, 0x00000a2a, 0x00000259, 0x96682af2, 0x47b4d26c}, + {0x0c1ae843, 0x00000ce4, 0x0000031b, 0x7b637c43, 0x62b47e8b}, + {0xec3cd517, 0x000002ff, 0x00000566, 0x5d719a77, 0xff5bc5b7}, + {0x77828e95, 0x0000067f, 0x0000038f, 0x43ee5b6c, 0x1a0cfacd}, + {0xec87b4e3, 0x00000d1c, 0x000002e3, 0x2ddd2eee, 0x275118a7}, + {0x412158bb, 0x00000eee, 0x00000111, 0x67b38ba2, 0xa74ecff5}, + {0x2e52de3e, 0x00000c4a, 0x000003b5, 0xbcc5d61d, 0xbd800707}, + {0x6ddaae8b, 0x00000d99, 0x00000266, 0x8b535544, 0xecbde1a1}, + {0x049b6cb1, 0x000009c5, 0x000000b0, 0xfc22cabc, 0xfb78eb9f}, + {0x77d4b954, 0x0000028a, 0x000007fa, 0x71d00923, 0x8c116f85}, + {0x5e192355, 0x00000ac1, 0x000001fa, 0xb966b81a, 0x5aa17bbe}, + {0x7d80b71d, 0x00000213, 0x000001e0, 0x2bba371a, 0xb5906aa6}, + {0x01f6f1e4, 0x000001d6, 0x00000395, 0xb7e8a647, 0x3ad112b1}, + {0x1dfabb13, 0x00000e14, 0x000001eb, 0x53917fba, 0xbaee0339}, + {0xb00a4449, 0x00000bf6, 0x00000409, 0xedecb577, 0x6f3a3979}, + {0x7ecd3981, 0x0000083f, 0x0000016b, 0xefef62b9, 0xe3e52eed}, + {0xf8f330d2, 0x000004be, 0x00000757, 0x9357c9f3, 0x0835bc1b}, + {0x03c38af2, 0x00000d23, 0x000002dc, 0x360fa8c0, 0x2ca885e6}, + {0x687bb79b, 0x00000f3d, 0x000000c2, 0x448d3be2, 0x79be2f78}, + {0x6710f550, 0x000009e9, 0x00000603, 0xdbfd1998, 0x1d25f627}, + {0x873171d1, 0x00000787, 0x000004d5, 0xab7f1b62, 0xa76a5656}, + {0x373b1314, 0x00000f0f, 0x000000f0, 0x184098ab, 0xba273974}, + {0x90fad9cd, 0x00000ead, 0x00000152, 0x23ce52ff, 0xb7bc958c}, + {0x19676fe7, 0x0000007d, 0x0000070d, 0xf8a76f1e, 0xf882b644}, + {0x89facd45, 0x000005f3, 0x00000473, 0x4331a006, 0xe9dc1396}, + {0x6f173747, 0x00000fc3, 0x0000003c, 0xb012f08e, 0xc6b888ee}, + {0x4b44a106, 0x0000075a, 0x0000008b, 0xf6f7ac38, 0x60cd2b74}, + {0xb620ad06, 0x00000774, 0x0000017e, 0xd34558e6, 0x3a0a615b}, + {0x976f21e9, 0x000008d7, 0x0000034a, 0xe533aa3a, 0xa99e60be}, + {0x687628c0, 0x000006c5, 0x0000061b, 0x3a840b15, 0x9bfcaef2}, + {0xe24ac108, 0x00000cd0, 0x0000032f, 0x51010ae8, 0x20958672}, + {0x361c44a3, 0x00000304, 0x00000719, 0xfd7bd481, 0xd70ff2b2}, + {0xd93ff95e, 0x00000db7, 0x0000008e, 0xcfbbc304, 0xad716acd}, + {0xed752d12, 0x00000883, 0x00000091, 0x65a6c868, 0x95c71c7b}, + {0xb4ff4b54, 0x000003d3, 0x000001c1, 0xf82597e7, 0x44b7f99b}, + {0x111b520f, 0x00000708, 0x000000eb, 0xc3e109f3, 0x71bc01ee}, + {0x62c806f2, 0x00000ba3, 0x0000045c, 0x874d3a72, 0xc539b753}, + {0x40d97470, 0x000005e1, 0x0000058d, 0x87a9684f, 0xea6073a5}, + {0x4312179c, 0x00000056, 0x0000070e, 0x809a00f5, 0x209aea3b}, + {0x13d5f84c, 0x00000a2d, 0x00000104, 0xf3d27578, 0xe087a8b6}, + {0x1f302cb2, 0x00000151, 0x00000014, 0x1e162693, 0x95e4b90e}, + {0xe491db24, 0x00000600, 0x000006f6, 0x7ff09615, 0x77611523}, + {0xf9a98069, 0x000002ba, 0x000002ad, 0x01af7387, 0xea925faa}, + {0xe9c477ad, 0x0000015f, 0x00000778, 0x6facf9a0, 0x1130f736}, + {0x353f32b2, 0x0000087c, 0x00000783, 0x6cc964ea, 0x32459994}, + {0x78e1b24f, 0x00000650, 0x000006a8, 0xb3bb7c27, 0x5a632f78}, + {0x61aa400e, 0x00000049, 0x00000254, 0xb8cd1681, 0xdf2652d5}, + {0xb84b10b0, 0x00000f73, 0x0000008c, 0x406a6450, 0x3619d31b}, + {0x9fa99c9c, 0x00000a7c, 0x000004d7, 0xfb3d21b4, 0xea31c743}, + {0x3fc9ebe3, 0x00000cd9, 0x000000d6, 0x43803f9c, 0x1f76a809}, + {0x529879cd, 0x000002f2, 0x00000595, 0x78b4c6a6, 0x63b9b93f}, + {0x3a933019, 0x00000516, 0x00000266, 0xdcb45436, 0x8f99c98c}, + {0x887b4977, 0x00000227, 0x0000038d, 0xc5f7c3d9, 0xaf5e3091}, + {0x770745de, 0x000008c6, 0x00000739, 0xf69145e8, 0x53d0dce1}, + {0x28be3b47, 0x00000c46, 0x0000032b, 0x764c028f, 0x106d0905}, + {0x5013a050, 0x00000cf6, 0x00000309, 0xea8fe164, 0x62180b57}, + {0x2ec4c9ba, 0x000006e8, 0x0000078d, 0xa35557a9, 0xf44430a4}, + {0xa9f950c9, 0x00000d33, 0x000002cc, 0x41ea8618, 0x587b4eb3}, + {0x5b520229, 0x000007b2, 0x00000484, 0x44569f1f, 0x92406c32}, + {0xd8dcbbfc, 0x0000002f, 0x0000048c, 0xdb88ab8b, 0x13bfe70e}, + {0x25529792, 0x00000d1d, 0x000002e2, 0x20cda404, 0x19d3b4e4}, + {0x9f3f6d71, 0x00000238, 0x0000079a, 0x0720443e, 0x3c107021}, + {0x64121215, 0x000007ff, 0x0000038f, 0x6aacff2c, 0xb82fdc3e}, + {0xfb6cdde0, 0x00000ef8, 0x00000107, 0xbd43a0f1, 0xab0d3c1d}, + {0x221c9d6f, 0x000007b6, 0x0000014f, 0xb67f834b, 0x1371ad05}, + {0x030e1de4, 0x00000836, 0x000004b4, 0x0d67d26a, 0xe2e72df1}, + {0xb56fa6cf, 0x00000c07, 0x000003f8, 0x60601ac1, 0x039de73e}, + {0xb55c89f5, 0x0000098e, 0x000001d4, 0x2400efbe, 0xfe39a2bb}, + {0x5e90b6d5, 0x0000070b, 0x000003ea, 0x3bb5d6ea, 0xf0f794a0}, + {0x2a7045ae, 0x00000961, 0x00000633, 0xfca89e4b, 0xe66ce41c}, + {0x8b374ea9, 0x000006ba, 0x00000780, 0xbce036ed, 0x4cb28ef7}, + {0x8bd90bc9, 0x00000562, 0x00000369, 0xcb26a24b, 0x40236d1d}, + {0x5b1b1762, 0x000000fd, 0x0000051a, 0x33cdda07, 0xc32e420a}, + {0xa4153555, 0x0000058f, 0x000005c7, 0xbe50eeca, 0x83a67f35}, + {0x0be1f931, 0x00000651, 0x00000672, 0x95a25753, 0x88f1aac1}, + {0xb7e78618, 0x00000a7f, 0x000002bb, 0xe06bcc1c, 0x74274f66}, + {0x4a9bc41b, 0x00000e51, 0x000001ae, 0x709e8d2c, 0x54eff534}, + {0xfc359d13, 0x00000440, 0x000002f8, 0x0a58451f, 0x55e9363f}, + {0x5aa48619, 0x000006d1, 0x00000284, 0x928ead83, 0x31041c06}, + {0xa609afa8, 0x0000053e, 0x00000272, 0xb048c141, 0x4704efba}, + {0x3f108afb, 0x00000949, 0x00000150, 0x9a6bb5bc, 0x4e4430c8}, + {0x79bec2d3, 0x000008ed, 0x00000712, 0x32692d57, 0x11d52a7b}, + {0x9429e067, 0x00000bc3, 0x0000043c, 0x5295ceff, 0x04640f4d}, + {0xae58b96a, 0x0000082d, 0x000007d2, 0xc2a681ba, 0xf7ca4a2c}, + {0x95df24be, 0x00000985, 0x000004c1, 0x3a287765, 0x2c4af003}, + {0x5e94976f, 0x00000596, 0x000004ed, 0xff00c489, 0x5ae11687}, + {0xf5e5f1de, 0x00000d31, 0x000002ce, 0x35f28e91, 0x30d47957}, + {0xa2c219cf, 0x00000a3c, 0x00000374, 0x707d21eb, 0x2a14a255}, + {0xf21b6ceb, 0x00000919, 0x00000135, 0x0847fb8b, 0xcb8d3b93}, + {0xaa988728, 0x00000787, 0x00000771, 0x885aeaa4, 0x6531b509}, + {0xaa5dfaac, 0x000003e5, 0x0000051b, 0x52c48ab7, 0xe43cc5e9}, + {0x0a053968, 0x00000d2a, 0x000002d5, 0x7a90256d, 0x8004765c}, + {0x1421dc20, 0x00000eef, 0x00000110, 0x97d6da24, 0x1378f6ff}, + {0xb47c2166, 0x00000a6a, 0x00000209, 0xcfd6cc52, 0x676e14a5}, + {0x77dd1955, 0x000000de, 0x00000266, 0xba74bcaa, 0xc71b429c}, + {0x68a03cc2, 0x0000082f, 0x000007b0, 0x752bd5d8, 0x19ed14aa}, + {0x0226b0a3, 0x00000a5f, 0x000005a0, 0x82de4970, 0xf654d3ed}, + {0x637bf3b1, 0x00000d93, 0x0000026c, 0x5c7115cb, 0x3cccb57e}, + {0x3b120edf, 0x00000c13, 0x000003ec, 0x80d7d20f, 0x92132798}, + {0xe2456780, 0x000002eb, 0x00000641, 0xc0a5d289, 0x6160c87a}, + {0x9b2e7125, 0x00000c0c, 0x000003f3, 0xcc15f57e, 0x6f00f637}, + {0x153033ef, 0x00000787, 0x000006b6, 0x3cde443b, 0xb46caa6e}, + {0x18458b3f, 0x0000066c, 0x00000561, 0x9a2bd8c6, 0xb6c29121}, + {0x4ff9d4b9, 0x00000c8f, 0x0000033a, 0xd0ee6d6d, 0xc81cf380}, + {0xdf84b5d9, 0x00000802, 0x0000029a, 0xdab0d74a, 0xb2464559}, + {0x81ee15df, 0x000003ce, 0x00000725, 0x9942e2de, 0x4ccf571b}, + {0x5c768e04, 0x00000afd, 0x00000160, 0x36110831, 0xae0b305a}, + {0xe5e18094, 0x00000b4b, 0x000000a0, 0xffa3e4a7, 0x6c8a4f09}, + {0xed7263b6, 0x00000d0d, 0x000002f2, 0xb0006a35, 0x7e04af8c}, + {0x5bfde7d7, 0x000006fb, 0x00000554, 0xa4193b76, 0xb3a91d12}, + {0x67f4a743, 0x00000b85, 0x0000047a, 0xf05c8d8f, 0xfb472fdf}, + {0xf13bdf22, 0x00000ff7, 0x00000008, 0x816351eb, 0xf347f235}, + {0x08ecc608, 0x00000d5d, 0x00000098, 0x90492772, 0x0b7f1521}, + {0x296f52ba, 0x000004f9, 0x00000788, 0x5e5a4896, 0x1cc67088}, + {0xbe4624c2, 0x00000427, 0x000004ef, 0xcd267b94, 0x550caefd}, + {0x906f7c7c, 0x00000a05, 0x0000003f, 0x03fcfc33, 0x9ed82a02}, + {0x8f7b323e, 0x00000458, 0x000004c7, 0xcd4969c8, 0x633c38a8}, + {0x88d6593d, 0x00000597, 0x000005b5, 0xf199cd3b, 0x0491452f}, + {0x978a7768, 0x00000268, 0x000001d3, 0xb28c95bd, 0x1a42fe61}, + {0x857a621e, 0x000007a7, 0x000003a8, 0xf4bf84ab, 0xcd0694c6}, + {0xb0e121ef, 0x000005be, 0x00000644, 0x28747c14, 0xf0510c72}, {0, 0, 0, 0, 0}, }; @@ -1114,15 +902,15 @@ static int test_crc32c(void) while (t->length) { uint32_t be, le; le = ext2fs_crc32c_le(t->crc, test_buf + t->start, t->length); - be = ext2fs_crc32c_be(t->crc, test_buf + t->start, t->length); - if (le != t->crc_le) { + be = ext2fs_crc32_be(t->crc, test_buf + t->start, t->length); + if (le != t->crc32c_le) { printf("Test %d LE fails, %x != %x\n", - (int) (t - test), le, t->crc_le); + (int) (t - test), le, t->crc32c_le); failures++; } - if (be != t->crc_be) { + if (be != t->crc32_be) { printf("Test %d BE fails, %x != %x\n", - (int) (t - test), be, t->crc_be); + (int) (t - test), be, t->crc32_be); failures++; } t++; diff --git a/lib/ext2fs/crc32c_defs.h b/lib/ext2fs/crc32c_defs.h index 023f2c01..3f9a09e5 100644 --- a/lib/ext2fs/crc32c_defs.h +++ b/lib/ext2fs/crc32c_defs.h @@ -1,10 +1,18 @@ /* + * There are multiple 16-bit CRC polynomials in common use, but this is + * *the* standard CRC-32 polynomial, first popularized by Ethernet. + * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 + */ +#define CRCPOLY_LE 0xedb88320 +#define CRCPOLY_BE 0x04c11db7 + +/* * This is the CRC32c polynomial, as outlined by Castagnoli. * x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+x^11+x^10+x^9+ * x^8+x^6+x^0 */ -#define CRCPOLY_LE 0x82F63B78 -#define CRCPOLY_BE 0x1EDC6F41 +#define CRC32C_POLY_LE 0x82F63B78 +#define CRC32C_POLY_BE 0x1EDC6F41 /* How many bits at a time to use. Valid values are 1, 2, 4, 8, 32 and 64. */ /* For less performance-sensitive, use 4 */ diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c index 5ee34457..ebd544fc 100644 --- a/lib/ext2fs/csum.c +++ b/lib/ext2fs/csum.c @@ -30,6 +30,693 @@ #define STATIC static #endif +static __u32 ext2fs_mmp_csum(ext2_filsys fs, struct mmp_struct *mmp) +{ + int offset = offsetof(struct mmp_struct, mmp_checksum); + + return ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)mmp, offset); +} + +int ext2fs_mmp_csum_verify(ext2_filsys fs, struct mmp_struct *mmp) +{ + __u32 calculated; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + + calculated = ext2fs_mmp_csum(fs, mmp); + + return ext2fs_le32_to_cpu(mmp->mmp_checksum) == calculated; +} + +errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp) +{ + __u32 crc; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + crc = ext2fs_mmp_csum(fs, mmp); + mmp->mmp_checksum = ext2fs_cpu_to_le32(crc); + + return 0; +} + +int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb) +{ + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + 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); + + return ext2fs_crc32c_le(~0, (unsigned char *)sb, offset); +} + +int ext2fs_superblock_csum_verify(ext2_filsys fs, struct ext2_super_block *sb) +{ + __u32 calculated; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + + calculated = ext2fs_superblock_csum(fs, sb); + + return ext2fs_le32_to_cpu(sb->s_checksum) == calculated; +} + +errcode_t ext2fs_superblock_csum_set(ext2_filsys fs, + struct ext2_super_block *sb) +{ + __u32 crc; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + crc = ext2fs_superblock_csum(fs, sb); + sb->s_checksum = ext2fs_cpu_to_le32(crc); + + return 0; +} + +static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum, + blk64_t block, + struct ext2_ext_attr_header *hdr, + __u32 *crc) +{ + char *buf = (char *)hdr; + __u32 old_crc = hdr->h_checksum; + + hdr->h_checksum = 0; + block = ext2fs_cpu_to_le64(block); + *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&block, + sizeof(block)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, fs->blocksize); + hdr->h_checksum = old_crc; + + return 0; +} + +int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, + blk64_t block, + struct ext2_ext_attr_header *hdr) +{ + __u32 calculated; + errcode_t retval; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + + retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &calculated); + if (retval) + return 0; + + return ext2fs_le32_to_cpu(hdr->h_checksum) == calculated; +} + +errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs, ext2_ino_t inum, + blk64_t block, + struct ext2_ext_attr_header *hdr) +{ + errcode_t retval; + __u32 crc; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &crc); + if (retval) + return retval; + hdr->h_checksum = ext2fs_cpu_to_le32(crc); + return 0; +} + +static __u16 do_nothing16(__u16 x) +{ + return x; +} + +static __u16 disk_to_host16(__u16 x) +{ + return ext2fs_le16_to_cpu(x); +} + +static errcode_t __get_dx_countlimit(ext2_filsys fs, + struct ext2_dir_entry *dirent, + struct ext2_dx_countlimit **cc, + int *offset, + int need_swab) +{ + struct ext2_dir_entry *dp; + struct ext2_dx_root_info *root; + struct ext2_dx_countlimit *c; + int count_offset, max_sane_entries; + unsigned int rec_len; + __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16); + + rec_len = translate(dirent->rec_len); + + if (rec_len == fs->blocksize && translate(dirent->name_len) == 0) + count_offset = 8; + else if (rec_len == 12) { + dp = (struct ext2_dir_entry *)(((void *)dirent) + rec_len); + rec_len = translate(dp->rec_len); + if (rec_len != fs->blocksize - 12) + return EXT2_ET_DB_NOT_FOUND; + root = (struct ext2_dx_root_info *)(((void *)dp + 12)); + if (root->reserved_zero || + root->info_length != sizeof(struct ext2_dx_root_info)) + return EXT2_ET_DB_NOT_FOUND; + count_offset = 32; + } else + return EXT2_ET_DB_NOT_FOUND; + + c = (struct ext2_dx_countlimit *)(((void *)dirent) + count_offset); + max_sane_entries = (fs->blocksize - count_offset) / + sizeof(struct ext2_dx_entry); + if (ext2fs_le16_to_cpu(c->limit) > max_sane_entries || + ext2fs_le16_to_cpu(c->count) > max_sane_entries) + return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; + + if (offset) + *offset = count_offset; + if (cc) + *cc = c; + + return 0; +} + +errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs, + struct ext2_dir_entry *dirent, + struct ext2_dx_countlimit **cc, + int *offset) +{ + return __get_dx_countlimit(fs, dirent, cc, offset, 0); +} + +void ext2fs_initialize_dirent_tail(ext2_filsys fs, + struct ext2_dir_entry_tail *t) +{ + memset(t, 0, sizeof(struct ext2_dir_entry_tail)); + ext2fs_set_rec_len(fs, sizeof(struct ext2_dir_entry_tail), + (struct ext2_dir_entry *)t); + t->det_reserved_name_len = EXT2_DIR_NAME_LEN_CSUM; +} + +static errcode_t __get_dirent_tail(ext2_filsys fs, + struct ext2_dir_entry *dirent, + struct ext2_dir_entry_tail **tt, + int need_swab) +{ + struct ext2_dir_entry *d; + void *top; + struct ext2_dir_entry_tail *t; + unsigned int rec_len; + errcode_t retval = 0; + __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16); + + d = dirent; + top = EXT2_DIRENT_TAIL(dirent, fs->blocksize); + + rec_len = translate(d->rec_len); + while (rec_len && !(rec_len & 0x3)) { + d = (struct ext2_dir_entry *)(((void *)d) + rec_len); + if ((void *)d >= top) + break; + rec_len = translate(d->rec_len); + } + + if (d != top) + return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; + + t = (struct ext2_dir_entry_tail *)d; + if (t->det_reserved_zero1 || + translate(t->det_rec_len) != sizeof(struct ext2_dir_entry_tail) || + translate(t->det_reserved_name_len) != EXT2_DIR_NAME_LEN_CSUM) + return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; + + if (tt) + *tt = t; + return retval; +} + +int ext2fs_dirent_has_tail(ext2_filsys fs, struct ext2_dir_entry *dirent) +{ + return __get_dirent_tail(fs, dirent, NULL, 0) == 0; +} + +static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent, __u32 *crc, + int size) +{ + errcode_t retval; + char *buf = (char *)dirent; + __u32 gen; + struct ext2_inode inode; + + retval = ext2fs_read_inode(fs, inum, &inode); + if (retval) + return retval; + + inum = ext2fs_cpu_to_le32(inum); + gen = ext2fs_cpu_to_le32(inode.i_generation); + *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum, + sizeof(inum)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size); + + return 0; +} + +int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent) +{ + errcode_t retval; + __u32 calculated; + struct ext2_dir_entry_tail *t; + + retval = __get_dirent_tail(fs, dirent, &t, 1); + if (retval) + return 1; + + /* + * The checksum field is overlaid with the dirent->name field + * so the swapfs.c functions won't change the endianness. + */ + retval = ext2fs_dirent_csum(fs, inum, dirent, &calculated, + (void *)t - (void *)dirent); + if (retval) + return 0; + return ext2fs_le32_to_cpu(t->det_checksum) == calculated; +} + +static errcode_t ext2fs_dirent_csum_set(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent) +{ + errcode_t retval; + __u32 crc; + struct ext2_dir_entry_tail *t; + + retval = __get_dirent_tail(fs, dirent, &t, 1); + if (retval) + return retval; + + /* swapfs.c functions don't change the checksum endianness */ + retval = ext2fs_dirent_csum(fs, inum, dirent, &crc, + (void *)t - (void *)dirent); + if (retval) + return retval; + t->det_checksum = ext2fs_cpu_to_le32(crc); + return 0; +} + +static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent, + __u32 *crc, int count_offset, int count, + struct ext2_dx_tail *t) +{ + errcode_t retval; + char *buf = (char *)dirent; + int size; + __u32 old_csum, gen; + struct ext2_inode inode; + + size = count_offset + (count * sizeof(struct ext2_dx_entry)); + old_csum = t->dt_checksum; + t->dt_checksum = 0; + + retval = ext2fs_read_inode(fs, inum, &inode); + if (retval) + return retval; + + inum = ext2fs_cpu_to_le32(inum); + gen = ext2fs_cpu_to_le32(inode.i_generation); + *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum, + sizeof(inum)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)t, + sizeof(struct ext2_dx_tail)); + t->dt_checksum = old_csum; + + return 0; +} + +static int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent) +{ + __u32 calculated; + errcode_t retval; + struct ext2_dx_countlimit *c; + struct ext2_dx_tail *t; + int count_offset, limit, count; + + retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1); + if (retval) + return 1; + limit = ext2fs_le16_to_cpu(c->limit); + count = ext2fs_le16_to_cpu(c->count); + if (count_offset + (limit * sizeof(struct ext2_dx_entry)) > + fs->blocksize - sizeof(struct ext2_dx_tail)) + return 0; + /* htree structs are accessed in LE order */ + t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit); + retval = ext2fs_dx_csum(fs, inum, dirent, &calculated, count_offset, + count, t); + if (retval) + return 0; + + return ext2fs_le32_to_cpu(t->dt_checksum) == calculated; +} + +static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent) +{ + __u32 crc; + errcode_t retval = 0; + struct ext2_dx_countlimit *c; + struct ext2_dx_tail *t; + int count_offset, limit, count; + + retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1); + if (retval) + return retval; + limit = ext2fs_le16_to_cpu(c->limit); + count = ext2fs_le16_to_cpu(c->count); + if (count_offset + (limit * sizeof(struct ext2_dx_entry)) > + fs->blocksize - sizeof(struct ext2_dx_tail)) + return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; + t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit); + + /* htree structs are accessed in LE order */ + retval = ext2fs_dx_csum(fs, inum, dirent, &crc, count_offset, count, t); + if (retval) + return retval; + t->dt_checksum = ext2fs_cpu_to_le32(crc); + return retval; +} + +int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent) +{ + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + + if (__get_dirent_tail(fs, dirent, NULL, 1) == 0) + return ext2fs_dirent_csum_verify(fs, inum, dirent); + if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0) + return ext2fs_dx_csum_verify(fs, inum, dirent); + + return 0; +} + +errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent) +{ + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + if (__get_dirent_tail(fs, dirent, NULL, 1) == 0) + return ext2fs_dirent_csum_set(fs, inum, dirent); + if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0) + return ext2fs_dx_csum_set(fs, inum, dirent); + + if (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) + return 0; + return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; +} + +#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \ + (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max))) + +static struct ext3_extent_tail *get_extent_tail(struct ext3_extent_header *h) +{ + return (struct ext3_extent_tail *)(((void *)h) + + EXT3_EXTENT_TAIL_OFFSET(h)); +} + +static errcode_t ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum, + struct ext3_extent_header *eh, + __u32 *crc) +{ + int size; + __u32 gen; + errcode_t retval; + struct ext2_inode inode; + + size = EXT3_EXTENT_TAIL_OFFSET(eh) + offsetof(struct ext3_extent_tail, + et_checksum); + + retval = ext2fs_read_inode(fs, inum, &inode); + if (retval) + return retval; + inum = ext2fs_cpu_to_le32(inum); + gen = ext2fs_cpu_to_le32(inode.i_generation); + *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum, + sizeof(inum)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)eh, size); + + return 0; +} + +int ext2fs_extent_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, + struct ext3_extent_header *eh) +{ + errcode_t retval; + __u32 provided, calculated; + struct ext3_extent_tail *t = get_extent_tail(eh); + + /* + * The extent tree structures are accessed in LE order, so we must + * swap the checksum bytes here. + */ + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + + provided = ext2fs_le32_to_cpu(t->et_checksum); + retval = ext2fs_extent_block_csum(fs, inum, eh, &calculated); + if (retval) + return 0; + + return provided == calculated; +} + +errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs, ext2_ino_t inum, + struct ext3_extent_header *eh) +{ + errcode_t retval; + __u32 crc; + struct ext3_extent_tail *t = get_extent_tail(eh); + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + /* + * The extent tree structures are accessed in LE order, so we must + * swap the checksum bytes here. + */ + retval = ext2fs_extent_block_csum(fs, inum, eh, &crc); + if (retval) + return retval; + t->et_checksum = ext2fs_cpu_to_le32(crc); + return retval; +} + +int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group, + char *bitmap, int size) +{ + struct ext4_group_desc *gdp = (struct ext4_group_desc *) + ext2fs_group_desc(fs, fs->group_desc, group); + __u32 provided, calculated; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + provided = gdp->bg_inode_bitmap_csum_lo; + calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, + size); + if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) + provided |= (__u32)gdp->bg_inode_bitmap_csum_hi << 16; + else + calculated &= 0xFFFF; + + return provided == calculated; +} + +errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group, + char *bitmap, int size) +{ + __u32 crc; + struct ext4_group_desc *gdp = (struct ext4_group_desc *) + ext2fs_group_desc(fs, fs->group_desc, group); + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size); + gdp->bg_inode_bitmap_csum_lo = crc & 0xFFFF; + if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) + gdp->bg_inode_bitmap_csum_hi = crc >> 16; + + return 0; +} + +int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group, + char *bitmap, int size) +{ + struct ext4_group_desc *gdp = (struct ext4_group_desc *) + ext2fs_group_desc(fs, fs->group_desc, group); + __u32 provided, calculated; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + provided = gdp->bg_block_bitmap_csum_lo; + calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, + size); + if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) + provided |= (__u32)gdp->bg_block_bitmap_csum_hi << 16; + else + calculated &= 0xFFFF; + + return provided == calculated; +} + +errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group, + char *bitmap, int size) +{ + __u32 crc; + struct ext4_group_desc *gdp = (struct ext4_group_desc *) + ext2fs_group_desc(fs, fs->group_desc, group); + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size); + gdp->bg_block_bitmap_csum_lo = crc & 0xFFFF; + if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) + gdp->bg_block_bitmap_csum_hi = crc >> 16; + + return 0; +} + +static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum, + struct ext2_inode_large *inode, + __u32 *crc, int has_hi) +{ + __u32 gen; + struct ext2_inode_large *desc = inode; + size_t size = fs->super->s_inode_size; + __u16 old_lo; + __u16 old_hi = 0; + + old_lo = inode->i_checksum_lo; + inode->i_checksum_lo = 0; + if (has_hi) { + old_hi = inode->i_checksum_hi; + inode->i_checksum_hi = 0; + } + + inum = ext2fs_cpu_to_le32(inum); + gen = inode->i_generation; + *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum, + sizeof(inum)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)desc, size); + + inode->i_checksum_lo = old_lo; + if (has_hi) + inode->i_checksum_hi = old_hi; + return 0; +} + +int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum, + struct ext2_inode_large *inode) +{ + errcode_t retval; + __u32 provided, calculated; + int i, has_hi; + char *cp; + + if (fs->super->s_creator_os != EXT2_OS_LINUX || + !EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + + has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE && + inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END); + + provided = ext2fs_le16_to_cpu(inode->i_checksum_lo); + retval = ext2fs_inode_csum(fs, inum, inode, &calculated, has_hi); + if (retval) + return 0; + if (has_hi) { + __u32 hi = ext2fs_le16_to_cpu(inode->i_checksum_hi); + provided |= hi << 16; + } else + calculated &= 0xFFFF; + + if (provided == calculated) + return 1; + + /* + * If the checksum didn't match, it's possible it was due to + * the inode being all zero's. It's unlikely this is the + * case, but it can happen. So check for it here. (We only + * check the base inode since that's good enough, and it's not + * worth the bother to figure out how much of the extended + * inode, if any, is present.) + */ + for (cp = (char *) inode, i = 0; + i < sizeof(struct ext2_inode); + cp++, i++) + if (*cp) + return 0; + return 1; /* Inode must have been all zero's */ +} + +errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum, + struct ext2_inode_large *inode) +{ + errcode_t retval; + __u32 crc; + int has_hi; + + if (fs->super->s_creator_os != EXT2_OS_LINUX || + !EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE && + inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END); + + retval = ext2fs_inode_csum(fs, inum, inode, &crc, has_hi); + if (retval) + return retval; + inode->i_checksum_lo = ext2fs_cpu_to_le16(crc & 0xFFFF); + if (has_hi) + inode->i_checksum_hi = ext2fs_cpu_to_le16(crc >> 16); + return 0; +} + __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) { __u16 crc = 0; @@ -40,45 +727,62 @@ __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) if (size < EXT2_MIN_DESC_SIZE) size = EXT2_MIN_DESC_SIZE; if (size > sizeof(struct ext4_group_desc)) { - printf("%s: illegal s_desc_size(%zd)\n", __func__, size); + /* This should never happen, but cap it for safety's sake */ size = sizeof(struct ext4_group_desc); } desc = ext2fs_group_desc(fs, fs->group_desc, group); - if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { - size_t offset = offsetof(struct ext2_group_desc, bg_checksum); - #ifdef WORDS_BIGENDIAN - struct ext4_group_desc swabdesc; + struct ext4_group_desc swabdesc; - /* Have to swab back to little-endian to do the checksum */ - memcpy(&swabdesc, desc, size); - ext2fs_swap_group_desc2(fs, - (struct ext2_group_desc *) &swabdesc); - desc = (struct ext2_group_desc *) &swabdesc; + /* Have to swab back to little-endian to do the checksum */ + memcpy(&swabdesc, desc, size); + ext2fs_swap_group_desc2(fs, + (struct ext2_group_desc *) &swabdesc); + desc = (struct ext2_group_desc *) &swabdesc; - group = ext2fs_swab32(group); + group = ext2fs_swab32(group); #endif - crc = ext2fs_crc16(~0, fs->super->s_uuid, - sizeof(fs->super->s_uuid)); - crc = ext2fs_crc16(crc, &group, sizeof(group)); - crc = ext2fs_crc16(crc, desc, offset); - offset += sizeof(desc->bg_checksum); /* skip checksum */ - /* for checksum of struct ext4_group_desc do the rest...*/ - if (offset < size) { - crc = ext2fs_crc16(crc, (char *)desc + offset, - size - offset); - } + + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + /* new metadata csum code */ + __u16 old_crc; + __u32 crc32; + + old_crc = desc->bg_checksum; + desc->bg_checksum = 0; + crc32 = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&group, + sizeof(group)); + crc32 = ext2fs_crc32c_le(crc32, (unsigned char *)desc, + size); + desc->bg_checksum = old_crc; + + crc = crc32 & 0xFFFF; + goto out; } + /* old crc16 code */ + size_t offset = offsetof(struct ext2_group_desc, bg_checksum); + crc = ext2fs_crc16(~0, fs->super->s_uuid, + sizeof(fs->super->s_uuid)); + crc = ext2fs_crc16(crc, &group, sizeof(group)); + crc = ext2fs_crc16(crc, desc, offset); + offset += sizeof(desc->bg_checksum); /* skip checksum */ + /* for checksum of struct ext4_group_desc do the rest...*/ + if (offset < size) { + crc = ext2fs_crc16(crc, (char *)desc + offset, + size - offset); + } + +out: return crc; } int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) { - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && + if (ext2fs_has_group_desc_csum(fs) && (ext2fs_bg_checksum(fs, group) != ext2fs_group_desc_csum(fs, group))) return 0; @@ -88,8 +792,7 @@ int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group) { - if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + if (!ext2fs_has_group_desc_csum(fs)) return; /* ext2fs_bg_checksum_set() sets the actual checksum field but @@ -123,8 +826,7 @@ errcode_t ext2fs_set_gdt_csum(ext2_filsys fs) if (!fs->inode_map) return EXT2_ET_NO_INODE_BITMAP; - if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + if (!ext2fs_has_group_desc_csum(fs)) return 0; for (i = 0; i < fs->group_desc_count; i++) { diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c index 4cb7ca18..35036157 100644 --- a/lib/ext2fs/dblist.c +++ b/lib/ext2fs/dblist.c @@ -25,34 +25,6 @@ static EXT2_QSORT_TYPE dir_block_cmp2(const void *a, const void *b); static EXT2_QSORT_TYPE (*sortfunc32)(const void *a, const void *b); /* - * Returns the number of directories in the filesystem as reported by - * the group descriptors. Of course, the group descriptors could be - * wrong! - */ -errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs) -{ - dgrp_t i; - ext2_ino_t num_dirs, max_dirs; - - EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); - - num_dirs = 0; - max_dirs = fs->super->s_inodes_per_group; - for (i = 0; i < fs->group_desc_count; i++) { - if (ext2fs_bg_used_dirs_count(fs, i) > max_dirs) - num_dirs += max_dirs / 8; - else - num_dirs += ext2fs_bg_used_dirs_count(fs, i); - } - if (num_dirs > fs->super->s_inodes_count) - num_dirs = fs->super->s_inodes_count; - - *ret_num_dirs = num_dirs; - - return 0; -} - -/* * helper function for making a new directory block list (for * initialize and copy). */ diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c index 5125d199..1a4bf5cf 100644 --- a/lib/ext2fs/dir_iterate.c +++ b/lib/ext2fs/dir_iterate.c @@ -192,16 +192,22 @@ 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; + 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) { dirent = (struct ext2_dir_entry *) (ctx->buf + offset); if (ext2fs_get_rec_len(fs, dirent, &rec_len)) @@ -213,9 +219,16 @@ int ext2fs_process_dir_block(ext2_filsys fs, ctx->errcode = EXT2_ET_DIR_CORRUPTED; return BLOCK_ABORT; } - if (!dirent->inode && - !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) - goto next; + if (!dirent->inode) { + if ((offset == fs->blocksize - csum_size) && + (dirent->rec_len == csum_size) && + (dirent->name_len == EXT2_DIR_NAME_LEN_CSUM)) { + if (!(ctx->flags & DIRENT_FLAG_INCLUDE_CSUM)) + goto next; + entry = DIRENT_CHECKSUM; + } else if (!(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) + goto next; + } ret = (ctx->func)(ctx->dir, (next_real_entry > offset) ? @@ -259,8 +272,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 cb3a104c..54b27772 100644 --- a/lib/ext2fs/dirblock.c +++ b/lib/ext2fs/dirblock.c @@ -20,45 +20,36 @@ #include "ext2_fs.h" #include "ext2fs.h" -errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, - void *buf, int flags EXT2FS_ATTR((unused))) +errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block, + void *buf, int flags EXT2FS_ATTR((unused)), + ext2_ino_t ino) { errcode_t retval; - char *p, *end; - struct ext2_dir_entry *dirent; - unsigned int name_len, rec_len; - + int corrupt = 0; retval = io_channel_read_blk64(fs->io, block, 1, buf); if (retval) return retval; - p = (char *) buf; - end = (char *) buf + fs->blocksize; - while (p < end-8) { - dirent = (struct ext2_dir_entry *) p; -#ifdef WORDS_BIGENDIAN - dirent->inode = ext2fs_swab32(dirent->inode); - dirent->rec_len = ext2fs_swab16(dirent->rec_len); - dirent->name_len = ext2fs_swab16(dirent->name_len); -#endif - name_len = dirent->name_len; + if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_dir_block_csum_verify(fs, ino, + (struct ext2_dir_entry *)buf)) + corrupt = 1; + #ifdef WORDS_BIGENDIAN - if (flags & EXT2_DIRBLOCK_V2_STRUCT) - dirent->name_len = ext2fs_swab16(dirent->name_len); + retval = ext2fs_dirent_swab_in(fs, buf, flags); #endif - if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0) - return retval; - if ((rec_len < 8) || (rec_len % 4)) { - rec_len = 8; - retval = EXT2_ET_DIR_CORRUPTED; - } else if (((name_len & 0xFF) + 8) > rec_len) - retval = EXT2_ET_DIR_CORRUPTED; - p += rec_len; - } + if (!retval && corrupt) + retval = EXT2_ET_DIR_CSUM_INVALID; return retval; } +errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, + void *buf, int flags EXT2FS_ATTR((unused))) +{ + return ext2fs_read_dir_block4(fs, block, buf, flags, 0); +} + errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags EXT2FS_ATTR((unused))) { @@ -72,45 +63,40 @@ errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, } -errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block, - void *inbuf, int flags EXT2FS_ATTR((unused))) +errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block, + void *inbuf, int flags EXT2FS_ATTR((unused)), + ext2_ino_t ino) { -#ifdef WORDS_BIGENDIAN errcode_t retval; - char *p, *end; - char *buf = 0; - unsigned int rec_len; - struct ext2_dir_entry *dirent; + char *buf = inbuf; +#ifdef WORDS_BIGENDIAN retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; memcpy(buf, inbuf, fs->blocksize); - p = buf; - end = buf + fs->blocksize; - while (p < end) { - dirent = (struct ext2_dir_entry *) p; - if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0) - return retval; - if ((rec_len < 8) || - (rec_len % 4)) { - ext2fs_free_mem(&buf); - return (EXT2_ET_DIR_CORRUPTED); - } - p += rec_len; - dirent->inode = ext2fs_swab32(dirent->inode); - dirent->rec_len = ext2fs_swab16(dirent->rec_len); - dirent->name_len = ext2fs_swab16(dirent->name_len); - - if (flags & EXT2_DIRBLOCK_V2_STRUCT) - dirent->name_len = ext2fs_swab16(dirent->name_len); - } + retval = ext2fs_dirent_swab_out(fs, buf, flags); + if (retval) + return retval; +#endif + retval = ext2fs_dir_block_csum_set(fs, ino, + (struct ext2_dir_entry *)buf); + if (retval) + goto out; + retval = io_channel_write_blk64(fs->io, block, 1, buf); + +out: +#ifdef WORDS_BIGENDIAN ext2fs_free_mem(&buf); - return retval; -#else - return io_channel_write_blk64(fs->io, block, 1, (char *) inbuf); #endif + return retval; +} + +errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block, + void *inbuf, int flags EXT2FS_ATTR((unused))) +{ + return ext2fs_write_dir_block4(fs, block, inbuf, flags, 0); } errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c index 41c40882..22558d66 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_ext_attr.h b/lib/ext2fs/ext2_ext_attr.h index ed548d12..bbb0aaa9 100644 --- a/lib/ext2fs/ext2_ext_attr.h +++ b/lib/ext2fs/ext2_ext_attr.h @@ -20,7 +20,9 @@ struct ext2_ext_attr_header { __u32 h_refcount; /* reference count */ __u32 h_blocks; /* number of disk blocks used */ __u32 h_hash; /* hash value of all attributes */ - __u32 h_reserved[4]; /* zero right now */ + __u32 h_checksum; /* crc32c(uuid+id+xattrs) */ + /* id = inum if refcount = 1, else blknum */ + __u32 h_reserved[3]; /* zero right now */ }; struct ext2_ext_attr_entry { diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index fb3f7cc3..0c0bbcbf 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -192,6 +192,13 @@ struct ext4_group_desc __u32 bg_reserved; }; +#define EXT4_BG_INODE_BITMAP_CSUM_HI_END \ + (offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \ + sizeof(__u16)) +#define 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 */ #define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */ @@ -235,6 +242,13 @@ struct ext2_dx_countlimit { __u16 count; }; +/* + * This goes at the end of each htree block. + */ +struct ext2_dx_tail { + __u32 dt_reserved; + __u32 dt_checksum; /* crc32c(uuid+inum+dxblock) */ +}; /* * Macro-instructions used to manage group descriptors @@ -305,6 +319,7 @@ struct ext2_dx_countlimit { #define EXT4_SNAPFILE_FL 0x01000000 /* Inode is a snapshot */ #define EXT4_SNAPFILE_DELETED_FL 0x04000000 /* Snapshot is being deleted */ #define EXT4_SNAPFILE_SHRUNK_FL 0x08000000 /* Snapshot shrink has completed */ +#define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data */ #define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ #define EXT2_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ @@ -454,8 +469,14 @@ struct ext2_inode_large { __u32 i_version_hi; /* high 32 bits for 64-bit version */ }; +#define EXT4_INODE_CSUM_HI_EXTRA_END \ + (offsetof(struct ext2_inode_large, i_checksum_hi) + sizeof(__u16) - \ + EXT2_GOOD_OLD_INODE_SIZE) + #define i_dir_acl i_size_high +#define i_checksum_lo osd2.linux2.l_i_checksum_lo + #if defined(__KERNEL__) || defined(__linux__) #define i_reserved1 osd1.linux1.l_i_reserved1 #define i_frag osd2.linux2.l_i_frag @@ -535,6 +556,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 */ @@ -620,7 +644,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 */ @@ -708,6 +732,11 @@ struct ext2_super_block { #define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080 #define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100 #define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200 +/* + * METADATA_CSUM implies GDT_CSUM. When METADATA_CSUM is set, group + * descriptor checksums use the same algorithm as all other data + * structures' checksums. + */ #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 #define EXT4_FEATURE_RO_COMPAT_REPLICA 0x0800 @@ -724,7 +753,7 @@ struct ext2_super_block { #define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 /* 0x2000 was EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM but this was never used */ #define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */ -#define EXT4_FEATURE_INCOMPAT_INLINEDATA 0x8000 /* data in inode */ +#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */ #define EXT2_FEATURE_COMPAT_SUPP 0 #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ @@ -784,6 +813,17 @@ struct ext2_dir_entry_2 { }; /* + * This is a bogus directory entry at the end of each leaf block that + * records checksums. + */ +struct ext2_dir_entry_tail { + __u32 det_reserved_zero1; /* Pretend to be unused */ + __u16 det_rec_len; /* 12 */ + __u16 det_reserved_name_len; /* 0xDE00, fake namelen/filetype */ + __u32 det_checksum; /* crc32c(uuid+inode+dirent) */ +}; + +/* * Ext2 directory file types. Only the low 3 bits are used. The * other bits are reserved for now. */ @@ -799,6 +839,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 @@ -839,7 +887,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 93c8360c..cd594730 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -185,6 +185,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 @@ -267,6 +268,12 @@ struct struct_ext2_filsys { * Time at which e2fsck last updated the MMP block. */ long mmp_last_written; + + /* progress operation functions */ + struct ext2fs_progress_ops *progress_ops; + + /* Precomputed FS UUID checksum for seeding other checksums */ + __u32 csum_seed; }; #if EXT2_FLAT_INCLUDES @@ -424,11 +431,13 @@ struct ext2_extent_info { #define DIRENT_FLAG_INCLUDE_EMPTY 1 #define DIRENT_FLAG_INCLUDE_REMOVED 2 +#define DIRENT_FLAG_INCLUDE_CSUM 4 #define DIRENT_DOT_FILE 1 #define DIRENT_DOT_DOT_FILE 2 #define DIRENT_OTHER_FILE 3 #define DIRENT_DELETED_FILE 4 +#define DIRENT_CHECKSUM 5 /* * Inode scan definitions @@ -554,26 +563,33 @@ typedef struct ext2_icount *ext2_icount_t; environment at configure time. */ #warning "Compression support is experimental" #endif -#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ - EXT2_FEATURE_INCOMPAT_COMPRESSION|\ - EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ - EXT2_FEATURE_INCOMPAT_META_BG|\ - EXT3_FEATURE_INCOMPAT_RECOVER|\ - EXT3_FEATURE_INCOMPAT_EXTENTS|\ - EXT4_FEATURE_INCOMPAT_FLEX_BG|\ - EXT4_FEATURE_INCOMPAT_MMP|\ - EXT4_FEATURE_INCOMPAT_64BIT) +#define EXT2_LIB_INCOMPAT_COMPRESSION EXT2_FEATURE_INCOMPAT_COMPRESSION +#else +#define EXT2_LIB_INCOMPAT_COMPRESSION (0) +#endif + +#ifdef CONFIG_MMP +#define EXT4_LIB_INCOMPAT_MMP EXT4_FEATURE_INCOMPAT_MMP +#else +#define EXT4_LIB_INCOMPAT_MMP (0) +#endif + +#ifdef CONFIG_QUOTA +#define EXT4_LIB_RO_COMPAT_QUOTA EXT4_FEATURE_RO_COMPAT_QUOTA #else +#define EXT4_LIB_RO_COMPAT_QUOTA (0) +#endif + #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ + EXT2_LIB_INCOMPAT_COMPRESSION|\ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ EXT2_FEATURE_INCOMPAT_META_BG|\ EXT3_FEATURE_INCOMPAT_RECOVER|\ EXT3_FEATURE_INCOMPAT_EXTENTS|\ EXT4_FEATURE_INCOMPAT_FLEX_BG|\ - EXT4_FEATURE_INCOMPAT_MMP|\ + EXT4_LIB_INCOMPAT_MMP|\ EXT4_FEATURE_INCOMPAT_64BIT) -#endif -#ifdef CONFIG_QUOTA + #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\ EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\ @@ -581,16 +597,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) -#else -#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ - EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\ - EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\ - EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\ - EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\ - EXT4_FEATURE_RO_COMPAT_GDT_CSUM|\ - EXT4_FEATURE_RO_COMPAT_BIGALLOC) -#endif + EXT4_LIB_RO_COMPAT_QUOTA|\ + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) /* * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed @@ -625,6 +633,12 @@ typedef struct stat ext2fs_struct_stat; /* * function prototypes */ +static inline int ext2fs_has_group_desc_csum(ext2_filsys fs) +{ + return EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM | + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM); +} /* alloc.c */ extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode, @@ -792,6 +806,8 @@ 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 __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); @@ -819,9 +835,11 @@ 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); +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); @@ -927,18 +945,63 @@ extern int ext2fs_super_and_bgd_loc(ext2_filsys fs, extern void ext2fs_update_dynamic_rev(ext2_filsys fs); /* crc32c.c */ -extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len); +extern __u32 ext2fs_crc32_be(__u32 crc, unsigned char const *p, size_t len); extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len); /* csum.c */ +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); +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); +extern int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, + blk64_t block, + struct ext2_ext_attr_header *hdr); +#define EXT2_DIRENT_TAIL(block, blocksize) \ + ((struct ext2_dir_entry_tail *)(((void *)(block)) + \ + (blocksize) - sizeof(struct ext2_dir_entry_tail))) + +extern void ext2fs_initialize_dirent_tail(ext2_filsys fs, + struct ext2_dir_entry_tail *t); +extern int ext2fs_dirent_has_tail(ext2_filsys fs, + struct ext2_dir_entry *dirent); +extern int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent); +extern errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent); +extern errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs, + struct ext2_dir_entry *dirent, + struct ext2_dx_countlimit **cc, + int *offset); +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, + 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, + 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, + 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); extern __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group); /* dblist.c */ - -extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs); extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist); extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt); @@ -993,12 +1056,16 @@ extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags); extern errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, void *buf, int flags); +extern errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block, + void *buf, int flags, ext2_ino_t ino); extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags); extern errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block, void *buf, int flags); +extern errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block, + void *buf, int flags, ext2_ino_t ino); /* dirhash.c */ extern errcode_t ext2fs_dirhash(int version, const char *name, int len, @@ -1049,16 +1116,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); @@ -1164,10 +1239,6 @@ extern errcode_t ext2fs_find_first_zero_generic_bitmap(ext2fs_generic_bitmap bit __u32 *out); /* gen_bitmap64.c */ - -/* Generate and print bitmap usage statistics */ -#define BMAP_STATS - void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap); errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, int type, __u64 start, __u64 end, @@ -1196,6 +1267,9 @@ errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bmap, errcode_t ext2fs_convert_subcluster_bitmap(ext2_filsys fs, ext2fs_block_bitmap *bitmap); +/* get_num_dirs.c */ +extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs); + /* getsize.c */ extern errcode_t ext2fs_get_device_size(const char *file, int blocksize, blk_t *retblocks); @@ -1258,6 +1332,7 @@ extern errcode_t ext2fs_get_memalign(unsigned long size, unsigned long align, void *ptr); /* inode.c */ +extern void ext2fs_free_inode_cache(struct ext2_inode_cache *icache); extern errcode_t ext2fs_flush_icache(ext2_filsys fs); extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, @@ -1385,6 +1460,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); @@ -1409,6 +1485,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, @@ -1448,6 +1526,7 @@ extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, /* inline functions */ #ifdef NO_INLINE_FUNCS +extern void ext2fs_init_csum_seed(ext2_filsys fs); extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr); extern errcode_t ext2fs_get_memzero(unsigned long size, void *ptr); extern errcode_t ext2fs_get_array(unsigned long count, @@ -1498,6 +1577,16 @@ extern __u64 ext2fs_div64_ceil(__u64 a, __u64 b); #endif /* __STDC_VERSION__ >= 199901L */ #endif +_INLINE_ void ext2fs_init_csum_seed(ext2_filsys fs) +{ + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return; + + fs->csum_seed = ext2fs_crc32c_le(~0, fs->super->s_uuid, + sizeof(fs->super->s_uuid)); +} + #ifndef EXT2_CUSTOM_MEMORY_ROUTINES #include <string.h> /* diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h index 729d5c53..3de9278b 100644 --- a/lib/ext2fs/ext2fsP.h +++ b/lib/ext2fs/ext2fsP.h @@ -75,7 +75,7 @@ struct ext2_inode_cache { struct ext2_inode_cache_ent { ext2_ino_t ino; - struct ext2_inode inode; + struct ext2_inode *inode; }; /* Function prototypes */ @@ -95,6 +95,23 @@ struct ext2fs_numeric_progress_struct { int skip_progress; }; +/* + * progress callback functions + */ +struct ext2fs_progress_ops { + void (*init)(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *label, __u64 max); + void (*update)(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + __u64 val); + void (*close)(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *message); +}; + +extern struct ext2fs_progress_ops ext2fs_numeric_progress_ops; + extern void ext2fs_numeric_progress_init(ext2_filsys fs, struct ext2fs_numeric_progress_struct * progress, const char *label, __u64 max); diff --git a/lib/ext2fs/ext3_extents.h b/lib/ext2fs/ext3_extents.h index 88fabc9d..4163436e 100644 --- a/lib/ext2fs/ext3_extents.h +++ b/lib/ext2fs/ext3_extents.h @@ -19,6 +19,17 @@ */ /* + * This is extent tail on-disk structure. + * All other extent structures are 12 bytes long. It turns out that + * block_size % 12 >= 4 for at least all powers of 2 greater than 512, which + * covers all valid ext4 block sizes. Therefore, this tail structure can be + * crammed into the end of the block without having to rebalance the tree. + */ +struct ext3_extent_tail { + __u32 et_checksum; /* crc32c(uuid+inum+extent_block) */ +}; + +/* * this is extent on-disk structure * it's used at the bottom of the tree */ diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c index 1889824c..9649a146 100644 --- a/lib/ext2fs/ext_attr.c +++ b/lib/ext2fs/ext_attr.c @@ -61,17 +61,29 @@ __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data) #undef NAME_HASH_SHIFT #undef VALUE_HASH_SHIFT -errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf) +errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, void *buf, + ext2_ino_t inum) { errcode_t retval; retval = io_channel_read_blk64(fs->io, block, 1, buf); if (retval) return retval; + + if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_ext_attr_block_csum_verify(fs, inum, block, buf)) + retval = EXT2_ET_EXT_ATTR_CSUM_INVALID; + #ifdef WORDS_BIGENDIAN ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1); #endif - return 0; + + return retval; +} + +errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf) +{ + return ext2fs_read_ext_attr3(fs, block, buf, 0); } errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) @@ -79,30 +91,40 @@ errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) return ext2fs_read_ext_attr2(fs, block, buf); } -errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf) +errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block, void *inbuf, + ext2_ino_t inum) { errcode_t retval; char *write_buf; -#ifdef WORDS_BIGENDIAN - char *buf = NULL; - retval = ext2fs_get_mem(fs->blocksize, &buf); +#ifdef WORDS_BIGENDIAN + retval = ext2fs_get_mem(fs->blocksize, &write_buf); if (retval) return retval; - write_buf = buf; - ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1); + ext2fs_swap_ext_attr(write_buf, inbuf, fs->blocksize, 1); #else write_buf = (char *) inbuf; #endif + + retval = ext2fs_ext_attr_block_csum_set(fs, inum, block, + (struct ext2_ext_attr_header *)write_buf); + if (retval) + return retval; + retval = io_channel_write_blk64(fs->io, block, 1, write_buf); #ifdef WORDS_BIGENDIAN - ext2fs_free_mem(&buf); + ext2fs_free_mem(&write_buf); #endif if (!retval) ext2fs_mark_changed(fs); return retval; } +errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf) +{ + return ext2fs_write_ext_attr3(fs, block, inbuf, 0); +} + errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) { return ext2fs_write_ext_attr2(fs, block, inbuf); @@ -111,9 +133,9 @@ errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) /* * This function adjusts the reference count of the EA block. */ -errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, +errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk, char *block_buf, int adjust, - __u32 *newcount) + __u32 *newcount, ext2_ino_t inum) { errcode_t retval; struct ext2_ext_attr_header *header; @@ -130,7 +152,7 @@ errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, block_buf = buf; } - retval = ext2fs_read_ext_attr2(fs, blk, block_buf); + retval = ext2fs_read_ext_attr3(fs, blk, block_buf, inum); if (retval) goto errout; @@ -139,7 +161,7 @@ errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, if (newcount) *newcount = header->h_refcount; - retval = ext2fs_write_ext_attr2(fs, blk, block_buf); + retval = ext2fs_write_ext_attr3(fs, blk, block_buf, inum); if (retval) goto errout; @@ -149,9 +171,18 @@ errout: return retval; } +errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, + char *block_buf, int adjust, + __u32 *newcount) +{ + return ext2fs_adjust_ea_refcount3(fs, blk, block_buf, adjust, + newcount, 0); +} + errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, char *block_buf, int adjust, __u32 *newcount) { - return ext2fs_adjust_ea_refcount(fs, blk, block_buf, adjust, newcount); + return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust, + newcount); } diff --git a/lib/ext2fs/extent.c b/lib/ext2fs/extent.c index 9cc7cba2..65bb0996 100644 --- a/lib/ext2fs/extent.c +++ b/lib/ext2fs/extent.c @@ -455,6 +455,13 @@ retry: return retval; } + if (!(handle->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_extent_block_csum_verify(handle->fs, handle->ino, + eh)) { + handle->level--; + return EXT2_ET_EXTENT_CSUM_INVALID; + } + newpath->left = newpath->entries = ext2fs_le16_to_cpu(eh->eh_entries); newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max); @@ -541,6 +548,7 @@ static errcode_t update_path(ext2_extent_handle_t handle) blk64_t blk; errcode_t retval; struct ext3_extent_idx *ix; + struct ext3_extent_header *eh; if (handle->level == 0) { retval = ext2fs_write_inode(handle->fs, handle->ino, @@ -550,6 +558,14 @@ static errcode_t update_path(ext2_extent_handle_t handle) blk = ext2fs_le32_to_cpu(ix->ei_leaf) + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); + /* then update the checksum */ + eh = (struct ext3_extent_header *) + handle->path[handle->level].buf; + retval = ext2fs_extent_block_csum_set(handle->fs, handle->ino, + eh); + if (retval) + return retval; + retval = io_channel_write_blk64(handle->fs->io, blk, 1, handle->path[handle->level].buf); } @@ -963,6 +979,11 @@ errcode_t ext2fs_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); diff --git a/lib/ext2fs/freefs.c b/lib/ext2fs/freefs.c index 28c4132f..d3e815cf 100644 --- a/lib/ext2fs/freefs.c +++ b/lib/ext2fs/freefs.c @@ -18,8 +18,6 @@ #include "ext2_fs.h" #include "ext2fsP.h" -static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache); - void ext2fs_free(ext2_filsys fs) { if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)) @@ -65,21 +63,6 @@ void ext2fs_free(ext2_filsys fs) } /* - * Free the inode cache structure - */ -static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache) -{ - if (--icache->refcount) - return; - if (icache->buffer) - ext2fs_free_mem(&icache->buffer); - if (icache->cache) - ext2fs_free_mem(&icache->cache); - icache->buffer_blk = 0; - ext2fs_free_mem(&icache); -} - -/* * This procedure frees a badblocks list. */ void ext2fs_u32_list_free(ext2_u32_list bb) diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c index 7265f94c..44ac4999 100644 --- a/lib/ext2fs/gen_bitmap64.c +++ b/lib/ext2fs/gen_bitmap64.c @@ -80,7 +80,7 @@ static void warn_bitmap(ext2fs_generic_bitmap bitmap, #endif } -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS #define INC_STAT(map, name) map->stats.name #else #define INC_STAT(map, name) ;; @@ -124,7 +124,7 @@ errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, if (retval) return retval; -#ifdef BMAP_STATS +#ifdef ENABLE_BMAP_STATS if (gettimeofday(&bitmap->stats.created, (struct timezone *) NULL) == -1) { perror("gettimeofday"); @@ -173,18 +173,18 @@ errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, return 0; } -#ifdef BMAP_STATS +#ifdef ENABLE_BMAP_STATS void ext2fs_print_bmap_statistics(ext2fs_generic_bitmap bitmap) { struct ext2_bmap_statistics *stats = &bitmap->stats; -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS float mark_seq_perc = 0.0, test_seq_perc = 0.0; float mark_back_perc = 0.0, test_back_perc = 0.0; #endif double inuse; struct timeval now; -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS if (stats->test_count) { test_seq_perc = ((float)stats->test_seq / stats->test_count) * 100; @@ -213,7 +213,7 @@ void ext2fs_print_bmap_statistics(ext2fs_generic_bitmap bitmap) fprintf(stderr, "\n[+] %s bitmap (type %d)\n", bitmap->description, stats->type); fprintf(stderr, "=================================================\n"); -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS fprintf(stderr, "%16llu bits long\n", bitmap->real_end - bitmap->start); fprintf(stderr, "%16lu copy_bmap\n%16lu resize_bmap\n", @@ -236,7 +236,7 @@ void ext2fs_print_bmap_statistics(ext2fs_generic_bitmap bitmap) fprintf(stderr, "%16llu bits marked backwards (%.2f%%)\n" "%16.2f seconds in use\n", stats->mark_back, mark_back_perc, inuse); -#endif /* BMAP_STATS_OPS */ +#endif /* ENABLE_BMAP_STATS_OPS */ } #endif @@ -253,7 +253,7 @@ void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap) if (!EXT2FS_IS_64_BITMAP(bmap)) return; -#ifdef BMAP_STATS +#ifdef ENABLE_BMAP_STATS if (getenv("E2FSPROGS_BITMAP_STATS")) { ext2fs_print_bmap_statistics(bmap); bmap->bitmap_ops->print_stats(bmap); @@ -293,10 +293,10 @@ errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src, return retval; -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS src->stats.copy_count++; #endif -#ifdef BMAP_STATS +#ifdef ENABLE_BMAP_STATS if (gettimeofday(&new_bmap->stats.created, (struct timezone *) NULL) == -1) { perror("gettimeofday"); @@ -322,7 +322,8 @@ errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src, ext2fs_free_mem(&new_bmap); return retval; } - sprintf(new_descr, "copy of %s", descr); + strcpy(new_descr, "copy of "); + strcat(new_descr, descr); new_bmap->description = new_descr; } @@ -442,7 +443,7 @@ int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap, arg >>= bitmap->cluster_bits; -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS if (arg == bitmap->stats.last_marked + 1) bitmap->stats.mark_seq++; if (arg < bitmap->stats.last_marked) @@ -509,7 +510,7 @@ int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap, arg >>= bitmap->cluster_bits; -#ifdef BMAP_STATS_OPS +#ifdef ENABLE_BMAP_STATS_OPS bitmap->stats.test_count++; if (arg == bitmap->stats.last_tested + 1) bitmap->stats.test_seq++; diff --git a/lib/ext2fs/gen_crc32ctable.c b/lib/ext2fs/gen_crc32ctable.c index 9996e9d8..a0742ee2 100644 --- a/lib/ext2fs/gen_crc32ctable.c +++ b/lib/ext2fs/gen_crc32ctable.c @@ -4,23 +4,27 @@ #define ENTRIES_PER_LINE 4 -#if CRC_LE_BITS <= 8 -#define LE_TABLE_SIZE (1 << CRC_LE_BITS) +#if CRC_LE_BITS > 8 +# define LE_TABLE_ROWS (CRC_LE_BITS/8) +# define LE_TABLE_SIZE 256 #else -#define LE_TABLE_SIZE 256 +# define LE_TABLE_ROWS 1 +# define LE_TABLE_SIZE (1 << CRC_LE_BITS) #endif -#if CRC_BE_BITS <= 8 -#define BE_TABLE_SIZE (1 << CRC_BE_BITS) +#if CRC_BE_BITS > 8 +# define BE_TABLE_ROWS (CRC_BE_BITS/8) +# define BE_TABLE_SIZE 256 #else -#define BE_TABLE_SIZE 256 +# define BE_TABLE_ROWS 1 +# define BE_TABLE_SIZE (1 << CRC_BE_BITS) #endif -static uint32_t crc32ctable_le[8][256]; -static uint32_t crc32ctable_be[8][256]; +static uint32_t crc32table_be[BE_TABLE_ROWS][256]; +static uint32_t crc32ctable_le[LE_TABLE_ROWS][256]; /** - * crc32cinit_le() - allocate and initialize LE table data + * crc32init_le() - allocate and initialize LE table data * * crc is the crc of the byte i; other entries are filled in based on the * fact that crctable[i^j] = crctable[i] ^ crctable[j]. @@ -34,13 +38,13 @@ static void crc32cinit_le(void) crc32ctable_le[0][0] = 0; for (i = LE_TABLE_SIZE >> 1; i; i >>= 1) { - crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + crc = (crc >> 1) ^ ((crc & 1) ? CRC32C_POLY_LE : 0); for (j = 0; j < LE_TABLE_SIZE; j += 2 * i) crc32ctable_le[0][i + j] = crc ^ crc32ctable_le[0][j]; } for (i = 0; i < LE_TABLE_SIZE; i++) { crc = crc32ctable_le[0][i]; - for (j = 1; j < 8; j++) { + for (j = 1; j < LE_TABLE_ROWS; j++) { crc = crc32ctable_le[0][crc & 0xff] ^ (crc >> 8); crc32ctable_le[j][i] = crc; } @@ -48,75 +52,65 @@ static void crc32cinit_le(void) } /** - * crc32cinit_be() - allocate and initialize BE table data + * crc32init_be() - allocate and initialize BE table data */ -static void crc32cinit_be(void) +static void crc32init_be(void) { unsigned i, j; uint32_t crc = 0x80000000; - crc32ctable_be[0][0] = 0; + crc32table_be[0][0] = 0; for (i = 1; i < BE_TABLE_SIZE; i <<= 1) { crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0); for (j = 0; j < i; j++) - crc32ctable_be[0][i + j] = crc ^ crc32ctable_be[0][j]; + crc32table_be[0][i + j] = crc ^ crc32table_be[0][j]; } for (i = 0; i < BE_TABLE_SIZE; i++) { - crc = crc32ctable_be[0][i]; - for (j = 1; j < 8; j++) { - crc = crc32ctable_be[0][(crc >> 24) & 0xff] ^ - (crc << 8); - crc32ctable_be[j][i] = crc; + crc = crc32table_be[0][i]; + for (j = 1; j < BE_TABLE_ROWS; j++) { + crc = crc32table_be[0][(crc >> 24) & 0xff] ^ (crc << 8); + crc32table_be[j][i] = crc; } } } -static void output_table(uint32_t table[8][256], int len, char trans) +static void output_table(uint32_t (*table)[256], int rows, int len, char *trans) { int i, j; - for (j = 0 ; j < 8; j++) { - printf("static const uint32_t t%d_%ce[] = {", j, trans); + for (j = 0 ; j < rows; j++) { + printf("{"); for (i = 0; i < len - 1; i++) { - if ((i % ENTRIES_PER_LINE) == 0) + if (i % ENTRIES_PER_LINE == 0) printf("\n"); - printf("to%ce(0x%8.8xL),", trans, table[j][i]); - if ((i % ENTRIES_PER_LINE) != (ENTRIES_PER_LINE - 1)) - printf(" "); - } - printf("to%ce(0x%8.8xL)};\n\n", trans, table[j][len - 1]); - - if (trans == 'l') { - if ((j+1)*8 >= CRC_LE_BITS) - break; - } else { - if ((j+1)*8 >= CRC_BE_BITS) - break; + printf("%s(0x%8.8xL), ", trans, table[j][i]); } + printf("%s(0x%8.8xL)},\n", trans, table[j][len - 1]); } } int main(int argc, char **argv) { - printf("/*\n"); - printf(" * crc32ctable.h - CRC32c tables\n"); - printf(" * this file is generated - do not edit\n"); - printf(" * # gen_crc32ctable > crc32c_table.h\n"); - printf(" * with\n"); - printf(" * CRC_LE_BITS = %d\n", CRC_LE_BITS); - printf(" * CRC_BE_BITS = %d\n", CRC_BE_BITS); - printf(" */\n"); - printf("#include <stdint.h>\n"); + printf("/* this file is generated - do not edit */\n\n"); + if (CRC_BE_BITS > 1) { + crc32init_be(); + printf("static const uint32_t " + "crc32table_be[%d][%d] = {", + BE_TABLE_ROWS, BE_TABLE_SIZE); + output_table(crc32table_be, LE_TABLE_ROWS, + BE_TABLE_SIZE, "tobe"); + printf("};\n"); + } if (CRC_LE_BITS > 1) { crc32cinit_le(); - output_table(crc32ctable_le, LE_TABLE_SIZE, 'l'); - } - - if (CRC_BE_BITS > 1) { - crc32cinit_be(); - output_table(crc32ctable_be, BE_TABLE_SIZE, 'b'); + printf("static const uint32_t " + "crc32ctable_le[%d][%d] = {", + LE_TABLE_ROWS, LE_TABLE_SIZE); + output_table(crc32ctable_le, LE_TABLE_ROWS, + LE_TABLE_SIZE, "tole"); + printf("};\n"); } return 0; diff --git a/lib/ext2fs/get_num_dirs.c b/lib/ext2fs/get_num_dirs.c new file mode 100644 index 00000000..f5644f8e --- /dev/null +++ b/lib/ext2fs/get_num_dirs.c @@ -0,0 +1,50 @@ +/* + * get_num_dirs.c -- calculate number of directories + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "config.h" +#include <stdio.h> +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <string.h> +#include <time.h> + +#include "ext2_fs.h" +#include "ext2fsP.h" + +/* + * Returns the number of directories in the filesystem as reported by + * the group descriptors. Of course, the group descriptors could be + * wrong! + */ +errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs) +{ + dgrp_t i; + ext2_ino_t num_dirs, max_dirs; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + num_dirs = 0; + max_dirs = fs->super->s_inodes_per_group; + for (i = 0; i < fs->group_desc_count; i++) { + if (ext2fs_bg_used_dirs_count(fs, i) > max_dirs) + num_dirs += max_dirs / 8; + else + num_dirs += ext2fs_bg_used_dirs_count(fs, i); + } + if (num_dirs > fs->super->s_inodes_count) + num_dirs = fs->super->s_inodes_count; + + *ret_num_dirs = num_dirs; + + return 0; +} + diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c index 5afdc272..62a19b36 100644 --- a/lib/ext2fs/initialize.c +++ b/lib/ext2fs/initialize.c @@ -458,8 +458,7 @@ ipg_retry: * bitmaps will be accounted for when allocated). */ free_blocks = 0; - csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + csum_flag = ext2fs_has_group_desc_csum(fs); reserved_inos = super->s_first_ino; for (i = 0; i < fs->group_desc_count; i++) { /* diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c index fd72d4cd..d154d7e3 100644 --- a/lib/ext2fs/inode.c +++ b/lib/ext2fs/inode.c @@ -72,8 +72,28 @@ errcode_t ext2fs_flush_icache(ext2_filsys fs) return 0; } +/* + * Free the inode cache structure + */ +void ext2fs_free_inode_cache(struct ext2_inode_cache *icache) +{ + int i; + + if (--icache->refcount) + return; + if (icache->buffer) + ext2fs_free_mem(&icache->buffer); + for (i = 0; i < icache->cache_size; i++) + ext2fs_free_mem(&icache->cache[i].inode); + if (icache->cache) + ext2fs_free_mem(&icache->cache); + icache->buffer_blk = 0; + ext2fs_free_mem(&icache); +} + static errcode_t create_icache(ext2_filsys fs) { + int i; errcode_t retval; if (fs->icache) @@ -84,10 +104,9 @@ static errcode_t create_icache(ext2_filsys fs) memset(fs->icache, 0, sizeof(struct ext2_inode_cache)); retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer); - if (retval) { - ext2fs_free_mem(&fs->icache); - return retval; - } + if (retval) + goto errout; + fs->icache->buffer_blk = 0; fs->icache->cache_last = -1; fs->icache->cache_size = 4; @@ -95,13 +114,22 @@ static errcode_t create_icache(ext2_filsys fs) retval = ext2fs_get_array(fs->icache->cache_size, sizeof(struct ext2_inode_cache_ent), &fs->icache->cache); - if (retval) { - ext2fs_free_mem(&fs->icache->buffer); - ext2fs_free_mem(&fs->icache); - return retval; + if (retval) + goto errout; + + for (i = 0; i < fs->icache->cache_size; i++) { + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), + &fs->icache->cache[i].inode); + if (retval) + goto errout; } + ext2fs_flush_icache(fs); return 0; +errout: + ext2fs_free_inode_cache(fs->icache); + fs->icache = 0; + return retval; } errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, @@ -148,8 +176,7 @@ errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, scan->current_group); scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); scan->blocks_left = scan->fs->inode_blocks_per_group; - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + if (ext2fs_has_group_desc_csum(fs)) { scan->inodes_left -= ext2fs_bg_itable_unused(fs, scan->current_group); scan->blocks_left = @@ -174,8 +201,7 @@ errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, } if (scan->fs->badblocks && scan->fs->badblocks->num) scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS; - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + if (ext2fs_has_group_desc_csum(fs)) scan->scan_flags |= EXT2_SF_DO_LAZY; *ret_scan = scan; return 0; @@ -241,8 +267,7 @@ static errcode_t get_next_blockgroup(ext2_inode_scan scan) scan->bytes_left = 0; scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super); scan->blocks_left = fs->inode_blocks_per_group; - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + if (ext2fs_has_group_desc_csum(fs)) { scan->inodes_left -= ext2fs_bg_itable_unused(fs, scan->current_group); scan->blocks_left = @@ -407,6 +432,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_large *iptr = (struct ext2_inode_large *)inode; EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN); @@ -469,6 +496,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, @@ -476,27 +509,39 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, scan->ptr += scan->inode_size - extra_bytes; scan->bytes_left -= scan->inode_size - extra_bytes; + /* Verify the inode checksum. */ + if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1, + (struct ext2_inode_large *)scan->temp_buffer)) + retval = EXT2_ET_INODE_CSUM_INVALID; + #ifdef WORDS_BIGENDIAN - memset(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 { + /* Verify the inode checksum. */ + if (!(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1, + (struct ext2_inode_large *)scan->ptr)) + retval = EXT2_ET_INODE_CSUM_INVALID; + #ifdef WORDS_BIGENDIAN - memset(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 +552,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 (iptr != (struct ext2_inode_large *)inode) { + memcpy(inode, iptr, bufsize); + ext2fs_free_mem(&iptr); + } return retval; } @@ -527,8 +576,11 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, unsigned long group, block, offset; char *ptr; errcode_t retval; - int clen, i, inodes_per_block, length; + int clen, i, inodes_per_block; io_channel io; + int length = EXT2_INODE_SIZE(fs->super); + struct ext2_inode_large *iptr; + int cache_slot; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); @@ -549,13 +601,11 @@ 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 > length) ? length : bufsize); + return 0; } } if (fs->flags & EXT2_FLAG_IMAGE_FILE) { @@ -580,11 +630,10 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, } offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); - length = EXT2_INODE_SIZE(fs->super); - if (bufsize < length) - length = bufsize; + cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size; + iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode; - ptr = (char *) inode; + ptr = (char *) iptr; while (length) { clen = length; if ((offset + length) > fs->blocksize) @@ -606,18 +655,23 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, ptr += clen; block_nr++; } + length = EXT2_INODE_SIZE(fs->super); + + /* Verify the inode checksum. */ + if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_inode_csum_verify(fs, ino, iptr)) + return EXT2_ET_INODE_CSUM_INVALID; #ifdef WORDS_BIGENDIAN - ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, - (struct ext2_inode_large *) inode, - 0, bufsize); + ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr, + (struct ext2_inode_large *) iptr, + 0, length); #endif - /* Update the inode cache */ - fs->icache->cache_last = (fs->icache->cache_last + 1) % - fs->icache->cache_size; - fs->icache->cache[fs->icache->cache_last].ino = ino; - fs->icache->cache[fs->icache->cache_last].inode = *inode; + /* Update the inode cache bookkeeping */ + fs->icache->cache_last = cache_slot; + fs->icache->cache[cache_slot].ino = ino; + memcpy(inode, iptr, (bufsize > length) ? length : bufsize); return 0; } @@ -635,9 +689,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); @@ -648,48 +703,54 @@ 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) { + 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); + fs->flags = old_flags; + 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 > length) ? length : bufsize); break; } } } else { retval = create_icache(fs); if (retval) - return retval; + goto errout; } + memcpy(w_inode, inode, (bufsize > length) ? length : 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 + retval = ext2fs_inode_csum_set(fs, ino, w_inode); + if (retval) + goto errout; + group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * EXT2_INODE_SIZE(fs->super); @@ -702,10 +763,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; - ptr = (char *) w_inode; while (length) { @@ -738,8 +795,7 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, fs->flags |= EXT2_FLAG_CHANGED; errout: - if (w_inode && w_inode != &temp_inode) - free(w_inode); + ext2fs_free_mem(&w_inode); return retval; } diff --git a/lib/ext2fs/jfs_compat.h b/lib/ext2fs/jfs_compat.h index 7b8aafd7..7947ef5a 100644 --- a/lib/ext2fs/jfs_compat.h +++ b/lib/ext2fs/jfs_compat.h @@ -17,6 +17,8 @@ #define cpu_to_be32(n) htonl(n) #define be32_to_cpu(n) ntohl(n) +#define cpu_to_be16(n) htons(n) +#define be16_to_cpu(n) ntohs(n) typedef unsigned int tid_t; typedef struct journal_s journal_t; diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h index 066c031e..5d2f337b 100644 --- a/lib/ext2fs/kernel-jbd.h +++ b/lib/ext2fs/kernel-jbd.h @@ -114,12 +114,24 @@ typedef struct journal_header_s #define JBD2_CRC32_CHKSUM 1 #define JBD2_MD5_CHKSUM 2 #define JBD2_SHA1_CHKSUM 3 +#define JBD2_CRC32C_CHKSUM 4 #define JBD2_CRC32_CHKSUM_SIZE 4 #define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32)) /* * Commit block header for storing transactional checksums: + * + * NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum* + * fields are used to store a checksum of the descriptor and data blocks. + * + * If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum + * field is used to store crc32c(uuid+commit_block). Each journal metadata + * block gets its own checksum, and data block checksums are stored in + * journal_block_tag (in the descriptor). The other h_chksum* fields are + * not used. + * + * Checksum v1 and v2 are mutually exclusive features. */ struct commit_header { __u32 h_magic; @@ -139,13 +151,19 @@ struct commit_header { typedef struct journal_block_tag_s { __u32 t_blocknr; /* The on-disk block number */ - __u32 t_flags; /* See below */ + __u16 t_checksum; /* truncated crc32c(uuid+seq+block) */ + __u16 t_flags; /* See below */ __u32 t_blocknr_high; /* most-significant high 32bits. */ } journal_block_tag_t; #define JBD_TAG_SIZE64 (sizeof(journal_block_tag_t)) #define JBD_TAG_SIZE32 (8) +/* Tail of descriptor block, for checksumming */ +struct journal_block_tail { + __u32 t_checksum; +}; + /* * The revoke descriptor: used on disk to describe a series of blocks to * be revoked from the log @@ -156,6 +174,10 @@ typedef struct journal_revoke_header_s int r_count; /* Count of bytes used in the block */ } journal_revoke_header_t; +/* Tail of revoke block, for checksumming */ +struct journal_revoke_tail { + __u32 r_checksum; +}; /* Definitions for the journal tag flags word: */ #define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ @@ -205,7 +227,10 @@ typedef struct journal_superblock_s __u32 s_max_trans_data; /* Limit of data blocks per trans. */ /* 0x0050 */ - __u32 s_padding[44]; + __u8 s_checksum_type; /* checksum type */ + __u8 s_padding2[3]; + __u32 s_padding[42]; + __u32 s_checksum; /* crc32c(superblock) */ /* 0x0100 */ __u8 s_users[16*48]; /* ids of all fs'es sharing the log */ @@ -224,18 +249,56 @@ 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) + +#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) +#ifdef E2FSCK_INCLUDE_INLINE_FUNCS +#if (__STDC_VERSION__ >= 199901L) +#define _INLINE_ extern inline +#else +#define _INLINE_ inline +#endif +#else /* !E2FSCK_INCLUDE_INLINE FUNCS */ +#if (__STDC_VERSION__ >= 199901L) +#define _INLINE_ inline +#else /* not C99 */ +#ifdef __GNUC__ +#define _INLINE_ extern __inline__ +#else /* For Watcom C */ +#define _INLINE_ extern inline +#endif /* __GNUC__ */ +#endif /* __STDC_VERSION__ >= 199901L */ +#endif /* INCLUDE_INLINE_FUNCS */ + +/* + * 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__ diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c index 2d03b573..2dec5dc2 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 b12bf2dd..4a854391 100644 --- a/lib/ext2fs/mkdir.c +++ b/lib/ext2fs/mkdir.c @@ -93,12 +93,14 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, inode.i_size = fs->blocksize; /* - * Write out the inode and inode data block + * Write out the inode and inode data block. The inode generation + * number is assigned by write_new_inode, which means that the call + * to write_dir_block must come after that. */ - retval = ext2fs_write_dir_block(fs, blk, block); + retval = ext2fs_write_new_inode(fs, ino, &inode); if (retval) goto cleanup; - retval = ext2fs_write_new_inode(fs, ino, &inode); + retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino); if (retval) goto cleanup; diff --git a/lib/ext2fs/mmp.c b/lib/ext2fs/mmp.c index 435c5f4e..d09178e6 100644 --- a/lib/ext2fs/mmp.c +++ b/lib/ext2fs/mmp.c @@ -33,6 +33,7 @@ errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf) { +#ifdef CONFIG_MMP struct mmp_struct *mmp_cmp; errcode_t retval = 0; @@ -74,6 +75,11 @@ errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf) } mmp_cmp = fs->mmp_cmp; + + if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_mmp_csum_verify(fs, mmp_cmp)) + retval = EXT2_ET_MMP_CSUM_INVALID; + #ifdef WORDS_BIGENDIAN ext2fs_swap_mmp(mmp_cmp); #endif @@ -88,10 +94,14 @@ errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf) out: return retval; +#else + return EXT2_ET_OP_NOT_SUPPORTED; +#endif } errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf) { +#ifdef CONFIG_MMP struct mmp_struct *mmp_s = buf; struct timeval tv; errcode_t retval = 0; @@ -108,6 +118,10 @@ errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf) ext2fs_swap_mmp(mmp_s); #endif + retval = ext2fs_mmp_csum_set(fs, mmp_s); + if (retval) + return retval; + /* I was tempted to make this use O_DIRECT and the mmp_fd, but * this caused no end of grief, while leaving it as-is works. */ retval = io_channel_write_blk64(fs->io, mmp_blk, -(int)sizeof(struct mmp_struct), buf); @@ -119,6 +133,9 @@ errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf) /* Make sure the block gets to disk quickly */ io_channel_flush(fs->io); return retval; +#else + return EXT2_ET_OP_NOT_SUPPORTED; +#endif } #ifdef HAVE_SRANDOM @@ -128,6 +145,7 @@ errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf) unsigned ext2fs_mmp_new_seq() { +#ifdef CONFIG_MMP unsigned new_seq; struct timeval tv; @@ -144,6 +162,9 @@ unsigned ext2fs_mmp_new_seq() } while (new_seq > EXT4_MMP_SEQ_MAX); return new_seq; +#else + return EXT2_ET_OP_NOT_SUPPORTED; +#endif } static errcode_t ext2fs_mmp_reset(ext2_filsys fs) @@ -180,8 +201,14 @@ out: return retval; } +errcode_t ext2fs_mmp_update(ext2_filsys fs) +{ + return ext2fs_mmp_update2(fs, 0); +} + errcode_t ext2fs_mmp_clear(ext2_filsys fs) { +#ifdef CONFIG_MMP errcode_t retval = 0; if (!(fs->flags & EXT2_FLAG_RW)) @@ -190,10 +217,14 @@ errcode_t ext2fs_mmp_clear(ext2_filsys fs) retval = ext2fs_mmp_reset(fs); return retval; +#else + return EXT2_ET_OP_NOT_SUPPORTED; +#endif } errcode_t ext2fs_mmp_init(ext2_filsys fs) { +#ifdef CONFIG_MMP struct ext2_super_block *sb = fs->super; blk64_t mmp_block; errcode_t retval; @@ -222,6 +253,9 @@ errcode_t ext2fs_mmp_init(ext2_filsys fs) out: return retval; +#else + return EXT2_ET_OP_NOT_SUPPORTED; +#endif } /* @@ -229,6 +263,7 @@ out: */ errcode_t ext2fs_mmp_start(ext2_filsys fs) { +#ifdef CONFIG_MMP struct mmp_struct *mmp_s; unsigned seq; unsigned int mmp_check_interval; @@ -318,6 +353,9 @@ clean_seq: mmp_error: return retval; +#else + return EXT2_ET_OP_NOT_SUPPORTED; +#endif } /* @@ -328,6 +366,7 @@ mmp_error: */ errcode_t ext2fs_mmp_stop(ext2_filsys fs) { +#ifdef CONFIG_MMP struct mmp_struct *mmp, *mmp_cmp; errcode_t retval = 0; @@ -357,6 +396,9 @@ mmp_error: } return retval; +#else + return EXT2_ET_OP_NOT_SUPPORTED; +#endif } #define EXT2_MIN_MMP_UPDATE_INTERVAL 60 @@ -364,8 +406,9 @@ 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) { +#ifdef CONFIG_MMP struct mmp_struct *mmp, *mmp_cmp; struct timeval tv; errcode_t retval = 0; @@ -375,7 +418,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); @@ -394,4 +438,7 @@ errcode_t ext2fs_mmp_update(ext2_filsys fs) mmp_error: return retval; +#else + return EXT2_ET_OP_NOT_SUPPORTED; +#endif } diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c index b0a1e476..2cd541d7 100644 --- a/lib/ext2fs/newdir.c +++ b/lib/ext2fs/newdir.c @@ -34,6 +34,8 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, char *buf; int rec_len; int filetype = 0; + struct ext2_dir_entry_tail *t; + int csum_size = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); @@ -43,7 +45,11 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; - retval = ext2fs_set_rec_len(fs, fs->blocksize, dir); + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + csum_size = sizeof(struct ext2_dir_entry_tail); + + retval = ext2fs_set_rec_len(fs, fs->blocksize - csum_size, dir); if (retval) return retval; @@ -57,7 +63,7 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, dir->inode = dir_ino; dir->name_len = 1 | filetype; dir->name[0] = '.'; - rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1); + rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); dir->rec_len = EXT2_DIR_REC_LEN(1); /* @@ -73,6 +79,11 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, dir->name[1] = '.'; } + + if (csum_size) { + t = EXT2_DIRENT_TAIL(buf, fs->blocksize); + ext2fs_initialize_dirent_tail(fs, t); + } *block = buf; return 0; } diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c index 113b80e7..89117f43 100644 --- a/lib/ext2fs/openfs.c +++ b/lib/ext2fs/openfs.c @@ -196,6 +196,16 @@ errcode_t ext2fs_open2(const char *name, const char *io_options, if (fs->orig_super) memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); + if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) { + retval = 0; + if (!ext2fs_verify_csum_type(fs, fs->super)) + retval = EXT2_ET_UNKNOWN_CSUM; + if (!ext2fs_superblock_csum_verify(fs, fs->super)) + retval = EXT2_ET_SB_CSUM_INVALID; + if (retval) + goto cleanup; + } + #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; ext2fs_swap_super(fs->super); @@ -264,6 +274,21 @@ errcode_t ext2fs_open2(const char *name, const char *io_options, retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } + + /* Enforce the block group descriptor size */ + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) { + if (fs->super->s_desc_size != EXT2_MIN_DESC_SIZE_64BIT) { + retval = EXT2_ET_BAD_DESC_SIZE; + goto cleanup; + } + } else { + if (fs->super->s_desc_size && + fs->super->s_desc_size != EXT2_MIN_DESC_SIZE) { + retval = EXT2_ET_BAD_DESC_SIZE; + goto cleanup; + } + } + fs->cluster_ratio_bits = fs->super->s_log_cluster_size - fs->super->s_log_block_size; if (EXT2_BLOCKS_PER_GROUP(fs->super) != @@ -301,6 +326,8 @@ errcode_t ext2fs_open2(const char *name, const char *io_options, retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } + /* Precompute the FS UUID to seed other checksums */ + ext2fs_init_csum_seed(fs); /* * Read group descriptors @@ -375,8 +402,7 @@ errcode_t ext2fs_open2(const char *name, const char *io_options, * If recovery is from backup superblock, Clear _UNININT flags & * reset bg_itable_unused to zero */ - if (superblock > 1 && EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + if (superblock > 1 && ext2fs_has_group_desc_csum(fs)) { dgrp_t group; for (group = 0; group < fs->group_desc_count; group++) { diff --git a/lib/ext2fs/progress.c b/lib/ext2fs/progress.c index 8c9a6f1d..83556b1a 100644 --- a/lib/ext2fs/progress.c +++ b/lib/ext2fs/progress.c @@ -19,6 +19,12 @@ static char spaces[80], backspaces[80]; static time_t last_update; +struct ext2fs_progress_ops ext2fs_numeric_progress_ops = { + .init = ext2fs_numeric_progress_init, + .update = ext2fs_numeric_progress_update, + .close = ext2fs_numeric_progress_close, +}; + static int int_log10(unsigned int arg) { int l; diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c index 3f3ed651..a885c696 100644 --- a/lib/ext2fs/rw_bitmaps.c +++ b/lib/ext2fs/rw_bitmaps.c @@ -36,7 +36,7 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) unsigned int nbits; errcode_t retval; char *block_buf = NULL, *inode_buf = NULL; - int csum_flag = 0; + int csum_flag; blk64_t blk; blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); ext2_ino_t ino_itr = 1; @@ -46,9 +46,7 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) - csum_flag = 1; + csum_flag = ext2fs_has_group_desc_csum(fs); inode_nbytes = block_nbytes = 0; if (do_block) { @@ -90,6 +88,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, @@ -115,6 +120,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, @@ -153,7 +164,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) errcode_t retval; int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8; - int csum_flag = 0; + int csum_flag; unsigned int cnt; blk64_t blk; blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); @@ -168,9 +179,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) fs->write_bitmaps = ext2fs_write_bitmaps; - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) - csum_flag = 1; + csum_flag = ext2fs_has_group_desc_csum(fs); retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) @@ -259,6 +268,15 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) retval = EXT2_ET_BLOCK_BITMAP_READ; goto cleanup; } + /* verify block bitmap checksum */ + if (!(fs->flags & + EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_block_bitmap_csum_verify(fs, i, + block_bitmap, block_nbytes)) { + retval = + EXT2_ET_BLOCK_BITMAP_READ; + goto cleanup; + } } else memset(block_bitmap, 0, block_nbytes); cnt = block_nbytes << 3; @@ -281,6 +299,16 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) retval = EXT2_ET_INODE_BITMAP_READ; goto cleanup; } + + /* verify inode bitmap checksum */ + if (!(fs->flags & + EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_inode_bitmap_csum_verify(fs, i, + inode_bitmap, inode_nbytes)) { + retval = + EXT2_ET_INODE_BITMAP_CSUM_INVALID; + goto cleanup; + } } else memset(inode_bitmap, 0, inode_nbytes); cnt = inode_nbytes << 3; diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c index 7c99373c..1295e818 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]); } @@ -348,6 +349,69 @@ 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) +{ + 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 diff --git a/lib/ext2fs/tst_super_size.c b/lib/ext2fs/tst_super_size.c index 85d87e12..50644b32 100644 --- a/lib/ext2fs/tst_super_size.c +++ b/lib/ext2fs/tst_super_size.c @@ -113,7 +113,7 @@ int main(int argc, char **argv) check_field(s_mmp_block, 8); check_field(s_raid_stripe_width, 4); check_field(s_log_groups_per_flex, 1); - check_field(s_reserved_char_pad, 1); + check_field(s_checksum_type, 1); check_field(s_reserved_pad, 2); check_field(s_kbytes_written, 8); check_field(s_snapshot_inum, 4); diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c index 8be57642..e18843ee 100644 --- a/misc/dumpe2fs.c +++ b/misc/dumpe2fs.c @@ -121,7 +121,7 @@ static void print_bg_opts(ext2_filsys fs, dgrp_t i) { int first = 1, bg_flags = 0; - if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) + if (ext2fs_has_group_desc_csum(fs)) bg_flags = ext2fs_bg_flags(fs, i); print_bg_opt(bg_flags, EXT2_BG_INODE_UNINIT, "INODE_UNINIT", @@ -197,7 +197,7 @@ static void list_desc (ext2_filsys fs) print_range(first_block, last_block); fputs(")", stdout); print_bg_opts(fs, i); - if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { + if (ext2fs_has_group_desc_csum(fs)) { unsigned csum = ext2fs_bg_checksum(fs, i); unsigned exp_csum = ext2fs_group_desc_csum(fs, i); @@ -235,10 +235,18 @@ static void list_desc (ext2_filsys fs) print_number(ext2fs_block_bitmap_loc(fs, i)); print_bg_rel_offset(fs, ext2fs_block_bitmap_loc(fs, i), 0, first_block, last_block); + if (fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) + printf(_(", csum 0x%08x"), + ext2fs_block_bitmap_checksum(fs, i)); fputs(_(", Inode bitmap at "), stdout); print_number(ext2fs_inode_bitmap_loc(fs, i)); print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0, first_block, last_block); + if (fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) + printf(_(", csum 0x%08x"), + ext2fs_inode_bitmap_checksum(fs, i)); fputs(_("\n Inode table at "), stdout); print_range(ext2fs_inode_table_loc(fs, i), ext2fs_inode_table_loc(fs, i) + @@ -316,6 +324,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; @@ -382,6 +400,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)); if (jsb->s_errno != 0) printf(_("Journal errno: %d\n"), (int) ntohl(jsb->s_errno)); @@ -410,6 +437,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" diff --git a/misc/mke2fs.c b/misc/mke2fs.c index 7448255d..7ff759dc 100644 --- a/misc/mke2fs.c +++ b/misc/mke2fs.c @@ -306,6 +306,27 @@ _("Warning: the backup superblock/group descriptors at block %u contain\n" ext2fs_badblocks_list_iterate_end(bb_iter); } +static void write_reserved_inodes(ext2_filsys fs) +{ + errcode_t retval; + ext2_ino_t ino; + struct ext2_inode *inode; + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) { + com_err("inode_init", retval, + "while allocating memory"); + exit(1); + } + bzero(inode, EXT2_INODE_SIZE(fs->super)); + + for (ino = 1; ino < EXT2_FIRST_INO(fs->super); ino++) + ext2fs_write_inode_full(fs, ino, inode, + EXT2_INODE_SIZE(fs->super)); + + ext2fs_free_mem(&inode); +} + static void write_inode_tables(ext2_filsys fs, int lazy_flag, int itable_zeroed) { errcode_t retval; @@ -351,6 +372,12 @@ static void write_inode_tables(ext2_filsys fs, int lazy_flag, int itable_zeroed) ext2fs_zero_blocks2(0, 0, 0, 0, 0); ext2fs_numeric_progress_close(fs, &progress, _("done \n")); + + /* Reserved inodes must always have correct checksums */ + if (fs->super->s_creator_os == EXT2_OS_LINUX && + fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) + write_reserved_inodes(fs); } static void create_root_dir(ext2_filsys fs) @@ -628,6 +655,23 @@ static void show_stats(ext2_filsys fs) } /* + * Returns true if making a file system for the Hurd, else 0 + */ +static int for_hurd(const char *os) +{ + if (!os) { +#ifdef __GNU__ + return 1; +#else + return 0; +#endif + } + if (isdigit(*os)) + return (atoi(os) == EXT2_OS_HURD); + return (strcasecmp(os, "GNU") == 0 || strcasecmp(os, "hurd") == 0); +} + +/* * Set the S_CREATOR_OS field. Return true if OS is known, * otherwise, 0. */ @@ -870,7 +914,7 @@ static __u32 ok_features[3] = { #ifdef CONFIG_QUOTA EXT4_FEATURE_RO_COMPAT_QUOTA| #endif - 0 + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM }; @@ -999,15 +1043,11 @@ static char **parse_fs_type(const char *fs_type, const char *size_type; struct str_list list; unsigned long long meg; - int is_hurd = 0; + int is_hurd = for_hurd(creator_os); if (init_list(&list)) return 0; - if (creator_os && (!strcasecmp(creator_os, "GNU") || - !strcasecmp(creator_os, "hurd"))) - is_hurd = 1; - if (fs_type) ext_type = fs_type; else if (is_hurd) @@ -1677,10 +1717,40 @@ profile_error: tmp = get_string_from_profile(fs_types, "default_features", ""); } + /* Mask off features which aren't supported by the Hurd */ + if (for_hurd(creator_os)) { + fs_param.s_feature_incompat &= ~EXT2_FEATURE_INCOMPAT_FILETYPE; + fs_param.s_feature_ro_compat &= + ~(EXT4_FEATURE_RO_COMPAT_HUGE_FILE | + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM); + } edit_feature(fs_features ? fs_features : tmp, &fs_param.s_feature_compat); if (tmp) free(tmp); + /* + * If the user specified features incompatible with the Hurd, complain + */ + if (for_hurd(creator_os)) { + if (fs_param.s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE) { + fprintf(stderr, _("The HURD does not support the " + "filetype feature.\n")); + exit(1); + } + if (fs_param.s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) { + fprintf(stderr, _("The HURD does not support the " + "huge_file feature.\n")); + exit(1); + } + if (fs_param.s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) { + fprintf(stderr, _("The HURD does not support the " + "metadata_csum feature.\n")); + exit(1); + } + } /* * We now need to do a sanity check of fs_blocks_count for @@ -1933,6 +2003,13 @@ profile_error: if (extended_opts) parse_extended_opts(&fs_param, extended_opts); + /* Don't allow user to set both metadata_csum and uninit_bg bits. */ + if ((fs_param.s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && + (fs_param.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + fs_param.s_feature_ro_compat &= + ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM; + /* Can't support bigalloc feature without extents feature */ if ((fs_param.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_BIGALLOC) && !(fs_param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)) { @@ -2077,7 +2154,8 @@ static int should_do_undo(const char *name) int csum_flag, force_undo; csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(&fs_param, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + EXT4_FEATURE_RO_COMPAT_GDT_CSUM | + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM); force_undo = get_int_from_profile(fs_types, "force_undo", 0); if (!force_undo && (!csum_flag || !lazy_itable_init)) return 0; @@ -2331,6 +2409,26 @@ int main (int argc, char *argv[]) com_err(device_name, retval, _("while setting up superblock")); exit(1); } + fs->progress_ops = &ext2fs_numeric_progress_ops; + + /* Check the user's mkfs options for metadata checksumming */ + if (!quiet && + EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super, + EXT3_FEATURE_INCOMPAT_EXTENTS)) + printf(_("Extents are not enabled. The file extent " + "tree can be checksummed, whereas block maps " + "cannot. Not enabling extents reduces the " + "coverage of metadata checksumming. " + "Pass -O extents to rectify.\n")); + if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super, + EXT4_FEATURE_INCOMPAT_64BIT)) + printf(_("64-bit filesystem support is not " + "enabled. The larger fields afforded by " + "this feature enable full-strength " + "checksumming. Pass -O 64bit to rectify.\n")); + } /* Can't undo discard ... */ if (!noaction && discard && (io_ptr != undo_io_manager)) { @@ -2356,6 +2454,7 @@ int main (int argc, char *argv[]) (fs_param.s_feature_ro_compat & (EXT4_FEATURE_RO_COMPAT_HUGE_FILE|EXT4_FEATURE_RO_COMPAT_GDT_CSUM| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE))) fs->super->s_kbytes_written = 1; @@ -2376,6 +2475,7 @@ int main (int argc, char *argv[]) } } else uuid_generate(fs->super->s_uuid); + ext2fs_init_csum_seed(fs); /* * Initialize the directory index variables @@ -2452,6 +2552,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); @@ -2498,8 +2602,7 @@ int main (int argc, char *argv[]) * inodes as unused; we want e2fsck to consider all * inodes as potentially containing recoverable data. */ - if (fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { + if (ext2fs_has_group_desc_csum(fs)) { for (i = 0; i < fs->group_desc_count; i++) ext2fs_bg_itable_unused_set(fs, i, 0); } diff --git a/misc/mke2fs.conf.in b/misc/mke2fs.conf.in index 0871f777..178733f3 100644 --- a/misc/mke2fs.conf.in +++ b/misc/mke2fs.conf.in @@ -16,7 +16,7 @@ inode_size = 256 } ext4dev = { - features = has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize + features = has_journal,extent,huge_file,flex_bg,metadata_csum,64bit,dir_nlink,extra_isize inode_size = 256 options = test_fs=1 } diff --git a/misc/tune2fs.c b/misc/tune2fs.c index 47a7711f..332aafd4 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; @@ -105,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); @@ -141,10 +144,11 @@ static __u32 ok_features[3] = { EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| EXT4_FEATURE_RO_COMPAT_GDT_CSUM | + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | #ifdef CONFIG_QUOTA EXT4_FEATURE_RO_COMPAT_QUOTA | #endif - EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM }; static __u32 clear_ok_features[3] = { @@ -161,10 +165,11 @@ static __u32 clear_ok_features[3] = { EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| + EXT4_FEATURE_RO_COMPAT_GDT_CSUM | #ifdef CONFIG_QUOTA EXT4_FEATURE_RO_COMPAT_QUOTA | #endif - EXT4_FEATURE_RO_COMPAT_GDT_CSUM + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM }; /* @@ -355,6 +360,28 @@ 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_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; @@ -367,15 +394,382 @@ 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; +} + +/* + * Rewrite directory blocks with checksums + */ +struct rewrite_dir_context { + char *buf; + errcode_t errcode; + ext2_ino_t dir; + int is_htree; +}; + +static int rewrite_dir_block(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct ext2_dx_countlimit *dcl = NULL; + struct rewrite_dir_context *ctx = priv_data; + int dcl_offset, changed = 0; + + ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0, + ctx->dir); + if (ctx->errcode) + return BLOCK_ABORT; + + /* if htree node... */ + if (ctx->is_htree) + ext2fs_get_dx_countlimit(fs, (struct ext2_dir_entry *)ctx->buf, + &dcl, &dcl_offset); + if (dcl) { + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + /* Ensure limit is the max size */ + int max_entries = (fs->blocksize - dcl_offset) / + sizeof(struct ext2_dx_entry); + if (ext2fs_le16_to_cpu(dcl->limit) != max_entries) { + changed = 1; + dcl->limit = ext2fs_cpu_to_le16(max_entries); + } + } else { + /* If htree block is full then rebuild the dir */ + if (ext2fs_le16_to_cpu(dcl->count) == + ext2fs_le16_to_cpu(dcl->limit)) { + request_dir_fsck_afterwards(fs); + return 0; + } + /* + * Ensure dcl->limit is small enough to leave room for + * the checksum tail. + */ + int max_entries = (fs->blocksize - (dcl_offset + + sizeof(struct ext2_dx_tail))) / + sizeof(struct ext2_dx_entry); + if (ext2fs_le16_to_cpu(dcl->limit) != max_entries) + dcl->limit = ext2fs_cpu_to_le16(max_entries); + /* Always rewrite checksum */ + changed = 1; + } + } else { + unsigned int rec_len, name_size; + char *top = ctx->buf + fs->blocksize; + struct ext2_dir_entry *de = (struct ext2_dir_entry *)ctx->buf; + struct ext2_dir_entry *last_de = NULL, *penultimate_de = NULL; + + /* Find last and penultimate dirent */ + while ((char *)de < top) { + penultimate_de = last_de; + last_de = de; + ctx->errcode = ext2fs_get_rec_len(fs, de, &rec_len); + if (!ctx->errcode && !rec_len) + ctx->errcode = EXT2_ET_DIR_CORRUPTED; + if (ctx->errcode) + return BLOCK_ABORT; + de = (struct ext2_dir_entry *)(((void *)de) + rec_len); + } + ctx->errcode = ext2fs_get_rec_len(fs, last_de, &rec_len); + if (ctx->errcode) + return BLOCK_ABORT; + name_size = last_de->name_len & 0xFF; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + if (!penultimate_de) + return 0; + if (last_de->inode || + name_size || + rec_len != sizeof(struct ext2_dir_entry_tail)) + return 0; + /* + * The last dirent is unused and the right length to + * have stored a checksum. Erase it. + */ + ctx->errcode = ext2fs_get_rec_len(fs, penultimate_de, + &rec_len); + if (!rec_len) + ctx->errcode = EXT2_ET_DIR_CORRUPTED; + if (ctx->errcode) + return BLOCK_ABORT; + ext2fs_set_rec_len(fs, rec_len + + sizeof(struct ext2_dir_entry_tail), + penultimate_de); + changed = 1; + } else { + int csum_size = sizeof(struct ext2_dir_entry_tail); + struct ext2_dir_entry_tail *t; + + /* + * If the last dirent looks like the tail, just update + * the checksum. + */ + if (!last_de->inode && + rec_len == csum_size) { + t = (struct ext2_dir_entry_tail *)last_de; + t->det_reserved_name_len = + EXT2_DIR_NAME_LEN_CSUM; + changed = 1; + goto out; + } + if (name_size & 3) + name_size = (name_size & ~3) + 4; + /* If there's not enough space for the tail, e2fsck */ + if (rec_len <= (8 + name_size + csum_size)) { + request_dir_fsck_afterwards(fs); + return 0; + } + /* Shorten that last de and insert the tail */ + ext2fs_set_rec_len(fs, rec_len - csum_size, last_de); + t = EXT2_DIRENT_TAIL(ctx->buf, fs->blocksize); + ext2fs_initialize_dirent_tail(fs, t); + + /* Always update checksum */ + changed = 1; + } + } + +out: + if (!changed) + return 0; + + ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf, + 0, ctx->dir); + if (ctx->errcode) + return BLOCK_ABORT; + + return 0; +} + +errcode_t rewrite_directory(ext2_filsys fs, ext2_ino_t dir, + struct ext2_inode *inode) +{ + errcode_t retval; + struct rewrite_dir_context ctx; + + retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); + if (retval) + return retval; + + ctx.is_htree = (inode->i_flags & EXT2_INDEX_FL); + ctx.dir = dir; + ctx.errcode = 0; + 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) +{ + 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; + + 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); + } + + 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) { + 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); + } + + retval = rewrite_extents(fs, ino, inode); + if (retval) { + com_err("rewrite_extents", retval, + "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); + } + } + + file_acl_block = ext2fs_file_acl_block(fs, inode); + if (!file_acl_block) + continue; + retval = ext2fs_read_ext_attr3(fs, file_acl_block, ea_buf, ino); + if (retval) { + com_err("rewrite_eablock", retval, + "while rewriting extended attribute"); + exit(1); + } + retval = ext2fs_write_ext_attr3(fs, file_acl_block, ea_buf, + ino); + if (retval) { + com_err("rewrite_eablock", retval, + "while rewriting extended attribute"); + exit(1); + } + } while (ino); + + ext2fs_free_mem(&inode); + ext2fs_free_mem(&ea_buf); + ext2fs_close_inode_scan(scan); +} + +static void rewrite_metadata_checksums(ext2_filsys fs) +{ + int i; + + fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; + ext2fs_init_csum_seed(fs); + for (i = 0; i < fs->group_desc_count; i++) + ext2fs_group_desc_csum_set(fs, i); + rewrite_inodes(fs); + ext2fs_read_bitmaps(fs); + ext2fs_mark_ib_dirty(fs); + 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, + 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); +} + +static void enable_uninit_bg(ext2_filsys fs) +{ + struct ext2_group_desc *gd; + int i; + + for (i = 0; i < fs->group_desc_count; i++) { + gd = ext2fs_group_desc(fs, fs->group_desc, i); + gd->bg_itable_unused = 0; + gd->bg_flags = EXT2_BG_INODE_ZEROED; + ext2fs_group_desc_csum_set(fs, i); + } + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; +} + +static void disable_uninit_bg(ext2_filsys fs, __u32 csum_feature_flag) +{ + struct ext2_group_desc *gd; + int i; + + /* Load bitmaps to ensure that the uninit ones get written out */ + fs->super->s_feature_ro_compat |= csum_feature_flag; + ext2fs_read_bitmaps(fs); + ext2fs_mark_ib_dirty(fs); + ext2fs_mark_bb_dirty(fs); + fs->super->s_feature_ro_compat &= ~csum_feature_flag; + + for (i = 0; i < fs->group_desc_count; i++) { + gd = ext2fs_group_desc(fs, fs->group_desc, i); + if ((gd->bg_flags & EXT2_BG_INODE_ZEROED) == 0) { + /* + * XXX what we really should do is zap + * uninitialized inode tables instead. + */ + request_fsck_afterwards(fs); + break; + } + gd->bg_itable_unused = 0; + gd->bg_flags = 0; + ext2fs_group_desc_csum_set(fs, i); + } + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; +} + /* * Update the feature set as provided by the user. */ static int update_feature_set(ext2_filsys fs, char *features) { struct ext2_super_block *sb = fs->super; - struct ext2_group_desc *gd; __u32 old_features[3]; - int i, type_err; + int type_err; unsigned int mask_err; #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \ @@ -540,36 +934,68 @@ mmp_error: } if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { - for (i = 0; i < fs->group_desc_count; i++) { - gd = ext2fs_group_desc(fs, fs->group_desc, i); - gd->bg_itable_unused = 0; - gd->bg_flags = EXT2_BG_INODE_ZEROED; - ext2fs_group_desc_csum_set(fs, i); - } - fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + if (check_fsck_needed(fs)) + exit(1); + rewrite_checksums = 1; + /* metadata_csum supersedes uninit_bg */ + fs->super->s_feature_ro_compat &= + ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM; + + /* if uninit_bg was previously off, rewrite group desc */ + if (!(old_features[E2P_FEATURE_RO_INCOMPAT] & + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + enable_uninit_bg(fs); + + /* + * Since metadata_csum supersedes uninit_bg, pretend like + * uninit_bg has been off all along. + */ + old_features[E2P_FEATURE_RO_INCOMPAT] &= + ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM; } if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { - for (i = 0; i < fs->group_desc_count; i++) { - gd = ext2fs_group_desc(fs, fs->group_desc, i); - if ((gd->bg_flags & EXT2_BG_INODE_ZEROED) == 0) { - /* - * XXX what we really should do is zap - * uninitialized inode tables instead. - */ - request_fsck_afterwards(fs); - break; - } - gd->bg_itable_unused = 0; - gd->bg_flags = 0; - gd->bg_checksum = 0; - } - fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + if (check_fsck_needed(fs)) + exit(1); + rewrite_checksums = 1; + /* + * If we're turning off metadata_csum and not turning on + * uninit_bg, rewrite group desc. + */ + if (!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + disable_uninit_bg(fs, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM); + else + /* + * metadata_csum previously provided uninit_bg, so if + * we're also setting the uninit_bg feature bit, + * pretend like it was previously enabled. Checksums + * will be rewritten with crc16 later. + */ + old_features[E2P_FEATURE_RO_INCOMPAT] |= + EXT4_FEATURE_RO_COMPAT_GDT_CSUM; } if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + /* Do not enable uninit_bg when metadata_csum enabled */ + if (fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) + fs->super->s_feature_ro_compat &= + ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM; + else + enable_uninit_bg(fs); + } + + if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + disable_uninit_bg(fs, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + + if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_QUOTA)) { /* * Set the Q_flag here and handle the quota options in the code @@ -2134,8 +2560,24 @@ retry_open: int set_csum = 0; dgrp_t i; - if (sb->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { + 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 (ext2fs_has_group_desc_csum(fs)) { /* * Determine if the block group checksums are * correct so we know whether or not to set @@ -2159,13 +2601,19 @@ retry_open: rc = 1; goto closefs; } + ext2fs_init_csum_seed(fs); if (set_csum) { for (i = 0; i < fs->group_desc_count; i++) ext2fs_group_desc_csum_set(fs, i); fs->flags &= ~EXT2_FLAG_SUPER_ONLY; } ext2fs_mark_super_dirty(fs); + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + rewrite_checksums = 1; } + if (rewrite_checksums) + rewrite_metadata_checksums(fs); if (I_flag) { if (mount_flags & EXT2_MF_MOUNTED) { fputs(_("The inode size may only be " diff --git a/resize/resize2fs.c b/resize/resize2fs.c index 7c4f86a3..e40a85c0 100644 --- a/resize/resize2fs.c +++ b/resize/resize2fs.c @@ -230,8 +230,7 @@ static void fix_uninit_block_bitmaps(ext2_filsys fs) dgrp_t g; int i; - if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM))) + if (!ext2fs_has_group_desc_csum(fs)) return; for (g=0; g < fs->group_desc_count; g++) { @@ -511,8 +510,7 @@ retry: */ group_block = ext2fs_group_first_block2(fs, old_fs->group_desc_count); - csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + csum_flag = ext2fs_has_group_desc_csum(fs); if (access("/sys/fs/ext4/features/lazy_itable_init", F_OK) == 0) lazy_itable_init = 1; if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) @@ -829,9 +827,9 @@ static void mark_fs_metablock(ext2_resize_t rfs, } } } - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && - (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) { + + if (ext2fs_has_group_desc_csum(fs) && + (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) { /* * If the block bitmap is uninitialized, which means * nothing other than standard metadata in use. @@ -890,8 +888,7 @@ static errcode_t blocks_to_move(ext2_resize_t rfs) for (blk = ext2fs_blocks_count(fs->super); blk < ext2fs_blocks_count(old_fs->super); blk++) { g = ext2fs_group_of_blk2(fs, blk); - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && + if (ext2fs_has_group_desc_csum(fs) && ext2fs_bg_flags_test(old_fs, g, EXT2_BG_BLOCK_UNINIT)) { /* * The block bitmap is uninitialized, so skip diff --git a/tests/f_mmp/script b/tests/f_mmp/script index d921672e..105a3058 100644 --- a/tests/f_mmp/script +++ b/tests/f_mmp/script @@ -1,5 +1,6 @@ FSCK_OPT=-yf +[ -f "$TMPFILE" ] && rm -f $TMPFILE TMPFILE=$test_name.tmp > $TMPFILE diff --git a/tests/f_mmp_garbage/script b/tests/f_mmp_garbage/script index 02cc12a4..d4d2cb3c 100644 --- a/tests/f_mmp_garbage/script +++ b/tests/f_mmp_garbage/script @@ -1,5 +1,6 @@ FSCK_OPT=-yf +[ -f "$TMPFILE" ] && rm -f $TMPFILE TMPFILE=$test_name.tmp > $TMPFILE diff --git a/tests/m_mmp/script b/tests/m_mmp/script index 02b0b4b8..dff98f05 100644 --- a/tests/m_mmp/script +++ b/tests/m_mmp/script @@ -2,6 +2,7 @@ DESCRIPTION="enable MMP during mke2fs" FS_SIZE=65536 MKE2FS_DEVICE_SECTSIZE=2048 export MKE2FS_DEVICE_SECTSIZE +[ -f "$TMPFILE" ] && rm -f $TMPFILE TMPFILE=$test_name.tmp > $TMPFILE stat -f $TMPFILE | grep -q "Type: tmpfs" diff --git a/tests/t_mmp_1on/script b/tests/t_mmp_1on/script index 8fc8158f..d15b1e33 100644 --- a/tests/t_mmp_1on/script +++ b/tests/t_mmp_1on/script @@ -1,5 +1,6 @@ FSCK_OPT=-yf +[ -f "$TMPFILE" ] && rm -f $TMPFILE TMPFILE=$test_name.tmp > $TMPFILE diff --git a/tests/t_mmp_2off/script b/tests/t_mmp_2off/script index 1dee14ed..572730b4 100644 --- a/tests/t_mmp_2off/script +++ b/tests/t_mmp_2off/script @@ -1,5 +1,6 @@ FSCK_OPT=-yf +[ -f "$TMPFILE" ] && rm -f $TMPFILE TMPFILE=$test_name.tmp > $TMPFILE @@ -7,5 +7,5 @@ * file may be redistributed under the GNU Public License v2. */ -#define E2FSPROGS_VERSION "1.42.7" +#define E2FSPROGS_VERSION "1.43-WIP" #define E2FSPROGS_DATE "21-Jan-2013" |