From patchwork Tue Nov 24 21:34:12 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Dilger X-Patchwork-Id: 548286 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id D79161402C2 for ; Wed, 25 Nov 2015 08:35:32 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753531AbbKXVeQ (ORCPT ); Tue, 24 Nov 2015 16:34:16 -0500 Received: from smtp-out-so.shaw.ca ([64.59.136.139]:43615 "EHLO smtp-out-so.shaw.ca" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752133AbbKXVeP (ORCPT ); Tue, 24 Nov 2015 16:34:15 -0500 Received: from cabot.adilger.int ([96.51.76.157]) by shaw.ca with SMTP id 1LE4aTrb9j2TZ1LE5aUuAx; Tue, 24 Nov 2015 14:34:14 -0700 X-Authority-Analysis: v=2.1 cv=YtqvP9sX c=1 sm=1 tr=0 a=sQcIUxX0G8DGr1HsvJ29bQ==:117 a=sQcIUxX0G8DGr1HsvJ29bQ==:17 a=BPx90hO6AAAA:8 a=b_-_TkvZ7jHS0GvtqEkA:9 From: Andreas Dilger To: tytso@mit.edu Cc: linux-ext4@vger.kernel.org, David Turner , Andreas Dilger Subject: [PATCH] e2fsck: Correct ext4 dates generated by old kernels Date: Tue, 24 Nov 2015 14:34:12 -0700 Message-Id: <1448400852-90545-1-git-send-email-adilger@dilger.ca> X-Mailer: git-send-email 1.8.0 X-CMAE-Envelope: MS4wfOlpkg3NRCZ7W4Qjo8BrmWlRDd1cZQACTr6QB1xXa/KNggJ+zfoEig3zx9x3j5HneLHtFBxeLZ3QBSYvXkd+zM0InCKcH26zwhvDQFdSi5byQbsHop6+ jP4vrTGODhMkCchChSTFRekUE9ksugLrs/XQTjeLsjO7auQkDX3YfpVCEl0q5j9CRbk9rkhP6rytOlE+pc7jiiCJYkxoUYoKXs/VH/YdN2RI4LqkX+Fze5Cs q4Cl5jXWAHBrjEtql92+A3Sj8HMMFOib/Cxc6aj6m2Q= Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: David Turner Older kernels on 64-bit machines would incorrectly encode pre-1970 ext4 dates as post-2311 dates. Detect and correct this (assuming the current date is before 2242). Includes tests for this, as well as changes to debugfs to correctly set crtimes. Signed-off-by: David Turner - ext2_fs.h: declare EXT4_EPOCH_BITS/EXT4_EPOCH_MASK like the kernel instead of in a separate header in an unusual location - problem.h: move PR_1_EA_TIME_OUT_OF_RANGE to avoid master conflict - problem.c: fix PR_1_EA_TIME_OUT_OF_RANGE PR_*_OK flag usage - f_pre_1970_date_encoding/script: run debugfs less often, use $MKE2FS instead of mkfs.ext4, fit within 80 columns Signed-off-by: Andreas Dilger --- debugfs/set_fields.c | 2 +- e2fsck/pass1.c | 41 +++++++++++++++ e2fsck/problem.c | 5 ++ e2fsck/problem.h | 3 ++ lib/ext2fs/ext2_fs.h | 3 ++ tests/f_pre_1970_date_encoding/expect | 45 +++++++++++++++++ tests/f_pre_1970_date_encoding/name | 1 + tests/f_pre_1970_date_encoding/script | 94 +++++++++++++++++++++++++++++++++++ 8 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 tests/f_pre_1970_date_encoding/expect create mode 100644 tests/f_pre_1970_date_encoding/name create mode 100644 tests/f_pre_1970_date_encoding/script diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c index 8297e08..8782f80 100644 --- a/debugfs/set_fields.c +++ b/debugfs/set_fields.c @@ -212,7 +212,7 @@ static struct field_set_info inode_fields[] = { 4, parse_uint }, { "atime_extra", &set_inode.i_atime_extra, NULL, 4, parse_uint }, - { "crtime", &set_inode.i_crtime, NULL, 4, parse_uint }, + { "crtime", &set_inode.i_crtime, NULL, 4, parse_time }, { "crtime_extra", &set_inode.i_crtime_extra, NULL, 4, parse_uint }, { "bmap", NULL, NULL, 4, parse_bmap, FLAG_ARRAY }, diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 3bf481f..a46d344 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -348,6 +348,21 @@ fix: EXT2_INODE_SIZE(sb), "pass1"); } +static int check_inode_extra_negative_epoch(__u32 xtime, __u32 extra) { + return (xtime & (1 << 31)) != 0 && + (extra & EXT4_EPOCH_MASK) == EXT4_EPOCH_MASK; +} + +#define CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, xtime) \ + check_inode_extra_negative_epoch(inode->i_##xtime, \ + inode->i_##xtime##_extra) + +/* When today's date is earlier than 2242, we assume that atimes, + * ctimes, crtimes, and mtimes with years in the range 2310..2378 are + * actually pre-1970 dates mis-encoded. + */ +#define EXT4_EXTRA_NEGATIVE_DATE_CUTOFF 2 * (1LL << 32) + static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx) { struct ext2_super_block *sb = ctx->fs->super; @@ -388,6 +403,32 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx) /* it seems inode has an extended attribute(s) in body */ check_ea_in_inode(ctx, pctx); } + + /* + * If the inode's extended atime (ctime, crtime, mtime) is stored in + * the old, invalid format, repair it. + */ + if (sizeof(time_t) > 4 && ctx->now < EXT4_EXTRA_NEGATIVE_DATE_CUTOFF && + (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, atime) || + CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, ctime) || + CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, crtime) || + CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, mtime))) { + + if (!fix_problem(ctx, PR_1_EA_TIME_OUT_OF_RANGE, pctx)) + return; + + if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, atime)) + inode->i_atime_extra &= ~EXT4_EPOCH_MASK; + if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, ctime)) + inode->i_ctime_extra &= ~EXT4_EPOCH_MASK; + if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, crtime)) + inode->i_crtime_extra &= ~EXT4_EPOCH_MASK; + if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, mtime)) + inode->i_mtime_extra &= ~EXT4_EPOCH_MASK; + e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode, + EXT2_INODE_SIZE(sb), "pass1"); + } + } /* diff --git a/e2fsck/problem.c b/e2fsck/problem.c index f442a33..d90e383 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -987,6 +987,11 @@ static struct e2fsck_problem problem_table[] = { N_("@i %i logical @b %b (physical @b %c) violates cluster allocation rules.\nWill fix in pass 1B.\n"), PROMPT_NONE, 0 }, + /* Timestamp(s) on inode beyond 2310-04-04 are likely pre-1970. */ + { PR_1_EA_TIME_OUT_OF_RANGE, + N_("Timestamp(s) on @i %i beyond 2310-04-04 are likely pre-1970.\n"), + PROMPT_FIX, PR_PREEN_OK | PR_NO_OK }, + /* Pass 1b errors */ /* Pass 1B: Rescan for duplicate/bad blocks */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 212ed35..25863d3 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -587,6 +587,9 @@ struct problem_context { /* Inode logical block is misaligned */ #define PR_1_MISALIGNED_CLUSTER 0x010074 +/* Timestamp(s) on inode beyond 2310-04-04 are likely pre-1970. */ +#define PR_1_EA_TIME_OUT_OF_RANGE 0x010081 + /* * Pass 1b errors */ diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index 6c3620c..5b0eb82 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -459,6 +459,9 @@ struct ext2_inode_large { __u32 i_version_hi; /* high 32 bits for 64-bit version */ }; +#define EXT4_EPOCH_BITS 2 +#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1) + #define i_dir_acl i_size_high #if defined(__KERNEL__) || defined(__linux__) diff --git a/tests/f_pre_1970_date_encoding/expect b/tests/f_pre_1970_date_encoding/expect new file mode 100644 index 0000000..1a71571 --- /dev/null +++ b/tests/f_pre_1970_date_encoding/expect @@ -0,0 +1,45 @@ +times for year-1909 = + ctime: 0x8e475440:00000003 + atime: 0x8e475440:00000003 + mtime: 0x8e475440:00000003 +crtime: 0x8e475440:00000003 +times for year-1979 = + ctime: 0x11db6940:00000000 + atime: 0x11db6940:00000000 + mtime: 0x11db6940:00000000 +crtime: 0x11db6940:00000000 +times for year-2039 = + ctime: 0x82a37b40:00000001 + atime: 0x82a37b40:00000001 + mtime: 0x82a37b40:00000001 +crtime: 0x82a37b40:00000001 +times for year-2139 = + ctime: 0x3e9b9940:00000001 + atime: 0x3e9b9940:00000001 + mtime: 0x3e9b9940:00000001 +crtime: 0x3e9b9940:00000001 +times for year-1909 = + ctime: 0x8e475440:00000000 + atime: 0x8e475440:00000000 + mtime: 0x8e475440:00000000 +crtime: 0x8e475440:00000000 +times for year-1979 = + ctime: 0x11db6940:00000000 + atime: 0x11db6940:00000000 + mtime: 0x11db6940:00000000 +crtime: 0x11db6940:00000000 +times for year-2039 = + ctime: 0x82a37b40:00000001 + atime: 0x82a37b40:00000001 + mtime: 0x82a37b40:00000001 +crtime: 0x82a37b40:00000001 +times for year-2139 = + ctime: 0x3e9b9940:00000001 + atime: 0x3e9b9940:00000001 + mtime: 0x3e9b9940:00000001 +crtime: 0x3e9b9940:00000001 +times for year-1909 = + ctime: 0x8e475440:00000003 + atime: 0x8e475440:00000003 + mtime: 0x8e475440:00000003 +crtime: 0x8e475440:00000003 diff --git a/tests/f_pre_1970_date_encoding/name b/tests/f_pre_1970_date_encoding/name new file mode 100644 index 0000000..9805324 --- /dev/null +++ b/tests/f_pre_1970_date_encoding/name @@ -0,0 +1 @@ +correct mis-encoded pre-1970 dates diff --git a/tests/f_pre_1970_date_encoding/script b/tests/f_pre_1970_date_encoding/script new file mode 100644 index 0000000..e6d7bbd --- /dev/null +++ b/tests/f_pre_1970_date_encoding/script @@ -0,0 +1,94 @@ +if ! test -x $DEBUGFS_EXE; then + echo "$test_name: $test_description: skipped (no debugfs)" + return 0 +fi + +OUT=$test_name.log +TIMESTAMPS=$test_name.timestamps.log +EXP=$test_dir/expect +FSCK_OPT=-yf + +create_file_with_xtime_and_extra() { + name=$1 + time=$2 + extra=$3 + { + echo "write /dev/null $name" + for xtime in atime ctime mtime crtime; do + echo "set_inode_field $name $xtime @$time" + echo "set_inode_field $name ${xtime}_extra $extra" + done + } | $DEBUGFS -w -f /dev/stdin $TMPFILE >> $OUT 2>&1 +} + +get_file_xtime_and_extra() { + name=$1 + echo "times for $name =" >> $TIMESTAMPS + $DEBUGFS -R "stat $name" $TMPFILE 2>&1 | egrep '^( a| c| m|cr)time:' | + sed 's/ --.*//' >> $TIMESTAMPS +} + +rm -f $OUT $TIMESTAMPS + +# create an empty ext4 filesystem with 256-byte inodes for testing +> $TMPFILE +echo mkfs.ext4 -b 1024 -q -I 256 $TMPFILE 5000 >> $OUT +$MKE2FS -t ext4 -b 1024 -q -I 256 -F $TMPFILE 5000 >> $OUT 2>&1 + +# this is a pre-1970 file encoded with the old encoding. +# fsck should repair this +create_file_with_xtime_and_extra year-1909 -1907928000 3 + +# these are all already encoded correctly +create_file_with_xtime_and_extra year-1979 299592000 0 +create_file_with_xtime_and_extra year-2039 2191752000 1 +create_file_with_xtime_and_extra year-2139 5345352000 1 + +# confirm that the xtime is wrong on the pre-1970 file +get_file_xtime_and_extra year-1909 + +# and confirm that it is right on the remaining files +get_file_xtime_and_extra year-1979 +get_file_xtime_and_extra year-2039 +get_file_xtime_and_extra year-2139 + +# before we repair the filesystem, save off a copy so that +# we can use it later + +cp -a $TMPFILE $TMPFILE.sav + +# repair the filesystem +E2FSCK_TIME=1386393539 $FSCK $FSCK_OPT $TMPFILE >> $OUT 2>&1 + +# check that the dates and xtime_extra on the file is now correct +get_file_xtime_and_extra year-1909 + +# check that the remaining dates have not been altered +get_file_xtime_and_extra year-1979 +get_file_xtime_and_extra year-2039 +get_file_xtime_and_extra year-2139 + +# now we need to check that after the year 2242, e2fsck does not +# modify dates with extra_xtime=3 + +# restore the unrepaired filesystem +mv $TMPFILE.sav $TMPFILE + +#retry the repair +E2FSCK_TIME=9270393539 $FSCK $FSCK_OPT $TMPFILE >> $OUT 2>&1 + +# check that the 1909 file is unaltered (i.e. it has a post-2378 date) +get_file_xtime_and_extra year-1909 + +cmp -s $TIMESTAMPS $EXP +status=$? + +if [ "$status" = 0 ]; then + echo "$test_name: $test_description: ok" + touch $test_name.ok +else + echo "$test_name: $test_description: failed" + diff $DIFF_OPTS $EXP $TIMESTAMPS > $test_name.failed +fi + +unset OUT TIMESTAMPS EXP FSCK_OPT