From: David Turner <novalis@novalis.org>
To: Theodore Ts'o <tytso@mit.edu>
Cc: Andreas Dilger <adilger@dilger.ca>, linux-ext4@vger.kernel.org
Subject: Re: [PATCH] e2fsck: Correct ext4 dates generated by old kernels
Date: Wed, 09 Mar 2016 03:57:25 -0500 [thread overview]
Message-ID: <1457513845.13390.17.camel@frank> (raw)
In-Reply-To: <20160307152741.GJ30238@thunk.org>
[-- Attachment #1: Type: text/plain, Size: 1188 bytes --]
On Mon, 2016-03-07 at 10:27 -0500, Theodore Ts'o wrote:
> On Mon, Mar 07, 2016 at 04:34:04AM -0700, Andreas Dilger wrote:
> > On maint or master? I'd applied and tested it on maint before sending it.
>
> At least on my tree David Turner's 64-bit date changes only landed on
> the master/next branch. It was never on the maint branch....
>
> - Ted
Sorry for the delay in responding; I hadn't had time to look at this
until today. I've figured out the problem.
In commit 188960ea4, the following change was made:
@@ -224,15 +232,19 @@ time_t string_to_time(const char *arg)
/* interpret it as an integer */
arg++;
fallback:
- ret = strtoul(arg, &tmp, 0);
+ ret = strtoll(arg+1, &tmp, 0);
if (*tmp)
This is wrong at least for the non-goto case, because arg was just
incremented just above. So it removes the first character *after* the @
as well. I think it's wrong in the general case too; there's no reason
to skip the first character.
I've attached new version of the patch which corrects this error (it's
against master -- let me know if that's not right).
Thanks.
[-- Attachment #2: 0001-e2fsck-Correct-ext4-dates-generated-by-old-kernels.patch --]
[-- Type: text/x-patch, Size: 10125 bytes --]
>From 9447baaf5a111dbecf03f2e2be470be02acad2f1 Mon Sep 17 00:00:00 2001
From: David Turner <novalis@novalis.org>
Date: Thu, 18 Feb 2016 12:57:56 -0700
Subject: [PATCH] e2fsck: Correct ext4 dates generated by old kernels
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).
Include tests for this, as well as changes to debugfs to correctly
set crtimes.
Fix an off-by-one bug in debugfs's literal unix time parsing.
Signed-off-by: David Turner <novalis@novalis.org>
- 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 <adilger@dilger.ca>
---
debugfs/util.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/util.c b/debugfs/util.c
index 770e7e1..bd5de79 100644
--- a/debugfs/util.c
+++ b/debugfs/util.c
@@ -232,7 +232,7 @@ extern __s64 string_to_time(const char *arg)
/* interpret it as an integer */
arg++;
fallback:
- ret = strtoll(arg+1, &tmp, 0);
+ ret = strtoll(arg, &tmp, 0);
if (*tmp)
return -1;
return ret;
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 631d735..60ed41f 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -448,6 +448,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;
@@ -492,6 +507,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 b39eab2..1e645e4 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1124,6 +1124,11 @@ static struct e2fsck_problem problem_table[] = {
N_("@i %i has corrupt @x header. "),
PROMPT_CLEAR_INODE, 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 b3f5b8f..edc381d 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -657,6 +657,9 @@ struct problem_context {
/* Missing extent header */
#define PR_1_MISSING_EXTENT_HEADER 0x010081
+/* Timestamp(s) on inode beyond 2310-04-04 are likely pre-1970. */
+#define PR_1_EA_TIME_OUT_OF_RANGE 0x010082
+
/*
* Pass 1b errors
*/
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index cdb68e8..9918356 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -481,6 +481,9 @@ struct ext2_inode_large {
(offsetof(struct ext2_inode_large, i_checksum_hi) + sizeof(__u16) - \
EXT2_GOOD_OLD_INODE_SIZE)
+#define EXT4_EPOCH_BITS 2
+#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
+
#define i_dir_acl i_size_high
#define i_checksum_lo osd2.linux2.l_i_checksum_lo
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
--
1.9.1
next prev parent reply other threads:[~2016-03-09 8:57 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-02-18 19:57 [PATCH] e2fsck: Correct ext4 dates generated by old kernels Andreas Dilger
2016-03-07 3:43 ` Theodore Ts'o
2016-03-07 11:34 ` Andreas Dilger
2016-03-07 15:27 ` Theodore Ts'o
2016-03-09 8:57 ` David Turner [this message]
-- strict thread matches above, loose matches on Subject: below --
2015-11-24 21:34 Andreas Dilger
2015-11-24 21:37 ` Andreas Dilger
2015-11-25 6:01 ` David Turner
2015-11-25 9:46 ` Theodore Ts'o
2015-11-26 5:27 ` David Turner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1457513845.13390.17.camel@frank \
--to=novalis@novalis.org \
--cc=adilger@dilger.ca \
--cc=linux-ext4@vger.kernel.org \
--cc=tytso@mit.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.