* [PATCH] fat: add KUnit tests for timestamp conversion helpers @ 2026-03-15 14:42 aviv.daum 2026-03-15 17:38 ` OGAWA Hirofumi 2026-03-15 22:24 ` [PATCH v2] " aviv.daum 0 siblings, 2 replies; 8+ messages in thread From: aviv.daum @ 2026-03-15 14:42 UTC (permalink / raw) To: OGAWA Hirofumi; +Cc: linux-kernel, avivdaum From: avivdaum <aviv.daum@gmail.com> Extend fat_test with coverage for FAT timestamp edge cases that are not currently exercised. Add tests for fat_time_unix2fat() clamping at the UTC boundaries and when time_offset pushes an otherwise valid timestamp outside the FAT date range. Also cover the NULL time_cs path used by existing callers, and verify fat_truncate_atime() truncation across timezone offsets. Signed-off-by: avivdaum <aviv.daum@gmail.com> --- fs/fat/fat_test.c | 173 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 169 insertions(+), 4 deletions(-) diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c index 1f0062659..dba96c150 100644 --- a/fs/fat/fat_test.c +++ b/fs/fat/fat_test.c @@ -29,6 +29,22 @@ struct fat_timestamp_testcase { int time_offset; }; +struct fat_unix2fat_clamp_testcase { + const char *name; + struct timespec64 ts; + __le16 time; + __le16 date; + u8 cs; + int time_offset; +}; + +struct fat_truncate_atime_testcase { + const char *name; + struct timespec64 ts; + struct timespec64 expected; + int time_offset; +}; + static struct fat_timestamp_testcase time_test_cases[] = { { .name = "Earliest possible UTC (1980-01-01 00:00:00)", @@ -120,13 +136,92 @@ static struct fat_timestamp_testcase time_test_cases[] = { }, }; +static struct fat_unix2fat_clamp_testcase unix2fat_clamp_test_cases[] = { + { + .name = "Clamp to earliest FAT date for 1979-12-31 23:59:59 UTC", + .ts = {.tv_sec = 315532799LL, .tv_nsec = 0L}, + .time = cpu_to_le16(0), + .date = cpu_to_le16(33), + .cs = 0, + .time_offset = 0, + }, + { + .name = "Clamp after time_offset=-60 pushes 1980-01-01 00:30 UTC below 1980", + .ts = {.tv_sec = 315534600LL, .tv_nsec = 0L}, + .time = cpu_to_le16(0), + .date = cpu_to_le16(33), + .cs = 0, + .time_offset = -60, + }, + { + .name = "Clamp to latest FAT date for 2108-01-01 00:00:00 UTC", + .ts = {.tv_sec = 4354819200LL, .tv_nsec = 0L}, + .time = cpu_to_le16(49021), + .date = cpu_to_le16(65439), + .cs = 199, + .time_offset = 0, + }, + { + .name = "Clamp after time_offset=60 pushes 2107-12-31 23:30 UTC beyond 2107", + .ts = {.tv_sec = 4354817400LL, .tv_nsec = 0L}, + .time = cpu_to_le16(49021), + .date = cpu_to_le16(65439), + .cs = 199, + .time_offset = 60, + }, +}; + +static struct fat_truncate_atime_testcase truncate_atime_test_cases[] = { + { + .name = "UTC atime truncates to 2004-02-29 00:00:00", + .ts = {.tv_sec = 1078058096LL, .tv_nsec = 789000000L}, + .expected = {.tv_sec = 1078012800LL, .tv_nsec = 0L}, + .time_offset = 0, + }, + { + .name = "time_offset=-60 truncates 2004-02-29 00:30 UTC to previous local midnight", + .ts = {.tv_sec = 1078014645LL, .tv_nsec = 123000000L}, + .expected = {.tv_sec = 1077930000LL, .tv_nsec = 0L}, + .time_offset = -60, + }, + { + .name = "time_offset=60 truncates 2004-02-29 23:30 UTC to next local midnight", + .ts = {.tv_sec = 1078097445LL, .tv_nsec = 123000000L}, + .expected = {.tv_sec = 1078095600LL, .tv_nsec = 0L}, + .time_offset = 60, + }, +}; + static void time_testcase_desc(struct fat_timestamp_testcase *t, char *desc) { strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); } +static void unix2fat_clamp_testcase_desc(struct fat_unix2fat_clamp_testcase *t, + char *desc) +{ + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); +} + +static void truncate_atime_testcase_desc(struct fat_truncate_atime_testcase *t, + char *desc) +{ + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); +} + KUNIT_ARRAY_PARAM(fat_time, time_test_cases, time_testcase_desc); +KUNIT_ARRAY_PARAM(fat_unix2fat_clamp, unix2fat_clamp_test_cases, + unix2fat_clamp_testcase_desc); +KUNIT_ARRAY_PARAM(fat_truncate_atime, truncate_atime_test_cases, + truncate_atime_testcase_desc); + +static void fat_test_set_time_offset(struct msdos_sb_info *sbi, int time_offset) +{ + *sbi = (struct msdos_sb_info){}; + sbi->options.tz_set = 1; + sbi->options.time_offset = time_offset; +} static void fat_time_fat2unix_test(struct kunit *test) { @@ -135,8 +230,7 @@ static void fat_time_fat2unix_test(struct kunit *test) struct fat_timestamp_testcase *testcase = (struct fat_timestamp_testcase *)test->param_value; - fake_sb.options.tz_set = 1; - fake_sb.options.time_offset = testcase->time_offset; + fat_test_set_time_offset(&fake_sb, testcase->time_offset); fat_time_fat2unix(&fake_sb, &ts, testcase->time, @@ -160,8 +254,7 @@ static void fat_time_unix2fat_test(struct kunit *test) struct fat_timestamp_testcase *testcase = (struct fat_timestamp_testcase *)test->param_value; - fake_sb.options.tz_set = 1; - fake_sb.options.time_offset = testcase->time_offset; + fat_test_set_time_offset(&fake_sb, testcase->time_offset); fat_time_unix2fat(&fake_sb, &testcase->ts, &time, &date, &cs); @@ -179,10 +272,82 @@ static void fat_time_unix2fat_test(struct kunit *test) "Centisecond mismatch\n"); } +static void fat_time_unix2fat_clamp_test(struct kunit *test) +{ + static struct msdos_sb_info fake_sb; + __le16 date, time; + u8 cs; + struct fat_unix2fat_clamp_testcase *testcase = + (struct fat_unix2fat_clamp_testcase *)test->param_value; + + fat_test_set_time_offset(&fake_sb, testcase->time_offset); + + fat_time_unix2fat(&fake_sb, &testcase->ts, &time, &date, &cs); + KUNIT_EXPECT_EQ_MSG(test, + le16_to_cpu(testcase->time), + le16_to_cpu(time), + "Clamped time mismatch\n"); + KUNIT_EXPECT_EQ_MSG(test, + le16_to_cpu(testcase->date), + le16_to_cpu(date), + "Clamped date mismatch\n"); + KUNIT_EXPECT_EQ_MSG(test, + testcase->cs, + cs, + "Clamped centisecond mismatch\n"); +} + +static void fat_time_unix2fat_no_csec_test(struct kunit *test) +{ + static struct msdos_sb_info fake_sb; + struct timespec64 ts = { + .tv_sec = 946684799LL, + .tv_nsec = 0L, + }; + __le16 date, time; + + fat_test_set_time_offset(&fake_sb, 0); + + fat_time_unix2fat(&fake_sb, &ts, &time, &date, NULL); + KUNIT_EXPECT_EQ_MSG(test, + 49021, + le16_to_cpu(time), + "Time mismatch without centiseconds\n"); + KUNIT_EXPECT_EQ_MSG(test, + 10143, + le16_to_cpu(date), + "Date mismatch without centiseconds\n"); +} + +static void fat_truncate_atime_test(struct kunit *test) +{ + static struct msdos_sb_info fake_sb; + struct timespec64 actual; + struct fat_truncate_atime_testcase *testcase = + (struct fat_truncate_atime_testcase *)test->param_value; + + fat_test_set_time_offset(&fake_sb, testcase->time_offset); + + actual = fat_truncate_atime(&fake_sb, &testcase->ts); + KUNIT_EXPECT_EQ_MSG(test, + testcase->expected.tv_sec, + actual.tv_sec, + "Atime truncation seconds mismatch\n"); + KUNIT_EXPECT_EQ_MSG(test, + testcase->expected.tv_nsec, + actual.tv_nsec, + "Atime truncation nanoseconds mismatch\n"); +} + static struct kunit_case fat_test_cases[] = { KUNIT_CASE(fat_checksum_test), KUNIT_CASE_PARAM(fat_time_fat2unix_test, fat_time_gen_params), KUNIT_CASE_PARAM(fat_time_unix2fat_test, fat_time_gen_params), + KUNIT_CASE_PARAM(fat_time_unix2fat_clamp_test, + fat_unix2fat_clamp_gen_params), + KUNIT_CASE(fat_time_unix2fat_no_csec_test), + KUNIT_CASE_PARAM(fat_truncate_atime_test, + fat_truncate_atime_gen_params), {}, }; -- 2.34.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] fat: add KUnit tests for timestamp conversion helpers 2026-03-15 14:42 [PATCH] fat: add KUnit tests for timestamp conversion helpers aviv.daum @ 2026-03-15 17:38 ` OGAWA Hirofumi 2026-03-15 22:24 ` [PATCH v2] " aviv.daum 1 sibling, 0 replies; 8+ messages in thread From: OGAWA Hirofumi @ 2026-03-15 17:38 UTC (permalink / raw) To: aviv.daum; +Cc: linux-kernel aviv.daum@gmail.com writes: > +static void fat_time_unix2fat_clamp_test(struct kunit *test) > +{ > + static struct msdos_sb_info fake_sb; > + __le16 date, time; > + u8 cs; > + struct fat_unix2fat_clamp_testcase *testcase = > + (struct fat_unix2fat_clamp_testcase *)test->param_value; > + > + fat_test_set_time_offset(&fake_sb, testcase->time_offset); > + > + fat_time_unix2fat(&fake_sb, &testcase->ts, &time, &date, &cs); > + KUNIT_EXPECT_EQ_MSG(test, > + le16_to_cpu(testcase->time), > + le16_to_cpu(time), > + "Clamped time mismatch\n"); > + KUNIT_EXPECT_EQ_MSG(test, > + le16_to_cpu(testcase->date), > + le16_to_cpu(date), > + "Clamped date mismatch\n"); It is unnecessary to convert to native endian? > + KUNIT_EXPECT_EQ_MSG(test, > + testcase->cs, > + cs, > + "Clamped centisecond mismatch\n"); > +} -- OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2] fat: add KUnit tests for timestamp conversion helpers 2026-03-15 14:42 [PATCH] fat: add KUnit tests for timestamp conversion helpers aviv.daum 2026-03-15 17:38 ` OGAWA Hirofumi @ 2026-03-15 22:24 ` aviv.daum 2026-03-16 3:25 ` OGAWA Hirofumi 2026-03-24 7:47 ` Christian Brauner 1 sibling, 2 replies; 8+ messages in thread From: aviv.daum @ 2026-03-15 22:24 UTC (permalink / raw) To: OGAWA Hirofumi; +Cc: linux-kernel, avivdaum From: avivdaum <aviv.daum@gmail.com> Extend fat_test with coverage for FAT timestamp edge cases that are not currently exercised. Add tests for fat_time_unix2fat() clamping at the UTC boundaries and when time_offset pushes an otherwise valid timestamp outside the FAT date range. Also cover the NULL time_cs path used by existing callers, and verify fat_truncate_atime() truncation across timezone offsets. Signed-off-by: avivdaum <aviv.daum@gmail.com> v2: - Compare __le16 values directly in unix2fat tests --- fs/fat/fat_test.c | 181 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 173 insertions(+), 8 deletions(-) diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c index 1f0062659..5c97a7fcc 100644 --- a/fs/fat/fat_test.c +++ b/fs/fat/fat_test.c @@ -29,6 +29,22 @@ struct fat_timestamp_testcase { int time_offset; }; +struct fat_unix2fat_clamp_testcase { + const char *name; + struct timespec64 ts; + __le16 time; + __le16 date; + u8 cs; + int time_offset; +}; + +struct fat_truncate_atime_testcase { + const char *name; + struct timespec64 ts; + struct timespec64 expected; + int time_offset; +}; + static struct fat_timestamp_testcase time_test_cases[] = { { .name = "Earliest possible UTC (1980-01-01 00:00:00)", @@ -120,13 +136,92 @@ static struct fat_timestamp_testcase time_test_cases[] = { }, }; +static struct fat_unix2fat_clamp_testcase unix2fat_clamp_test_cases[] = { + { + .name = "Clamp to earliest FAT date for 1979-12-31 23:59:59 UTC", + .ts = {.tv_sec = 315532799LL, .tv_nsec = 0L}, + .time = cpu_to_le16(0), + .date = cpu_to_le16(33), + .cs = 0, + .time_offset = 0, + }, + { + .name = "Clamp after time_offset=-60 pushes 1980-01-01 00:30 UTC below 1980", + .ts = {.tv_sec = 315534600LL, .tv_nsec = 0L}, + .time = cpu_to_le16(0), + .date = cpu_to_le16(33), + .cs = 0, + .time_offset = -60, + }, + { + .name = "Clamp to latest FAT date for 2108-01-01 00:00:00 UTC", + .ts = {.tv_sec = 4354819200LL, .tv_nsec = 0L}, + .time = cpu_to_le16(49021), + .date = cpu_to_le16(65439), + .cs = 199, + .time_offset = 0, + }, + { + .name = "Clamp after time_offset=60 pushes 2107-12-31 23:30 UTC beyond 2107", + .ts = {.tv_sec = 4354817400LL, .tv_nsec = 0L}, + .time = cpu_to_le16(49021), + .date = cpu_to_le16(65439), + .cs = 199, + .time_offset = 60, + }, +}; + +static struct fat_truncate_atime_testcase truncate_atime_test_cases[] = { + { + .name = "UTC atime truncates to 2004-02-29 00:00:00", + .ts = {.tv_sec = 1078058096LL, .tv_nsec = 789000000L}, + .expected = {.tv_sec = 1078012800LL, .tv_nsec = 0L}, + .time_offset = 0, + }, + { + .name = "time_offset=-60 truncates 2004-02-29 00:30 UTC to previous local midnight", + .ts = {.tv_sec = 1078014645LL, .tv_nsec = 123000000L}, + .expected = {.tv_sec = 1077930000LL, .tv_nsec = 0L}, + .time_offset = -60, + }, + { + .name = "time_offset=60 truncates 2004-02-29 23:30 UTC to next local midnight", + .ts = {.tv_sec = 1078097445LL, .tv_nsec = 123000000L}, + .expected = {.tv_sec = 1078095600LL, .tv_nsec = 0L}, + .time_offset = 60, + }, +}; + static void time_testcase_desc(struct fat_timestamp_testcase *t, char *desc) { strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); } +static void unix2fat_clamp_testcase_desc(struct fat_unix2fat_clamp_testcase *t, + char *desc) +{ + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); +} + +static void truncate_atime_testcase_desc(struct fat_truncate_atime_testcase *t, + char *desc) +{ + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); +} + KUNIT_ARRAY_PARAM(fat_time, time_test_cases, time_testcase_desc); +KUNIT_ARRAY_PARAM(fat_unix2fat_clamp, unix2fat_clamp_test_cases, + unix2fat_clamp_testcase_desc); +KUNIT_ARRAY_PARAM(fat_truncate_atime, truncate_atime_test_cases, + truncate_atime_testcase_desc); + +static void fat_test_set_time_offset(struct msdos_sb_info *sbi, int time_offset) +{ + *sbi = (struct msdos_sb_info){}; + sbi->options.tz_set = 1; + sbi->options.time_offset = time_offset; +} static void fat_time_fat2unix_test(struct kunit *test) { @@ -135,8 +230,7 @@ static void fat_time_fat2unix_test(struct kunit *test) struct fat_timestamp_testcase *testcase = (struct fat_timestamp_testcase *)test->param_value; - fake_sb.options.tz_set = 1; - fake_sb.options.time_offset = testcase->time_offset; + fat_test_set_time_offset(&fake_sb, testcase->time_offset); fat_time_fat2unix(&fake_sb, &ts, testcase->time, @@ -160,18 +254,17 @@ static void fat_time_unix2fat_test(struct kunit *test) struct fat_timestamp_testcase *testcase = (struct fat_timestamp_testcase *)test->param_value; - fake_sb.options.tz_set = 1; - fake_sb.options.time_offset = testcase->time_offset; + fat_test_set_time_offset(&fake_sb, testcase->time_offset); fat_time_unix2fat(&fake_sb, &testcase->ts, &time, &date, &cs); KUNIT_EXPECT_EQ_MSG(test, - le16_to_cpu(testcase->time), - le16_to_cpu(time), + testcase->time, + time, "Time mismatch\n"); KUNIT_EXPECT_EQ_MSG(test, - le16_to_cpu(testcase->date), - le16_to_cpu(date), + testcase->date, + date, "Date mismatch\n"); KUNIT_EXPECT_EQ_MSG(test, testcase->cs, @@ -179,10 +272,82 @@ static void fat_time_unix2fat_test(struct kunit *test) "Centisecond mismatch\n"); } +static void fat_time_unix2fat_clamp_test(struct kunit *test) +{ + static struct msdos_sb_info fake_sb; + __le16 date, time; + u8 cs; + struct fat_unix2fat_clamp_testcase *testcase = + (struct fat_unix2fat_clamp_testcase *)test->param_value; + + fat_test_set_time_offset(&fake_sb, testcase->time_offset); + + fat_time_unix2fat(&fake_sb, &testcase->ts, &time, &date, &cs); + KUNIT_EXPECT_EQ_MSG(test, + testcase->time, + time, + "Clamped time mismatch\n"); + KUNIT_EXPECT_EQ_MSG(test, + testcase->date, + date, + "Clamped date mismatch\n"); + KUNIT_EXPECT_EQ_MSG(test, + testcase->cs, + cs, + "Clamped centisecond mismatch\n"); +} + +static void fat_time_unix2fat_no_csec_test(struct kunit *test) +{ + static struct msdos_sb_info fake_sb; + struct timespec64 ts = { + .tv_sec = 946684799LL, + .tv_nsec = 0L, + }; + __le16 date, time; + + fat_test_set_time_offset(&fake_sb, 0); + + fat_time_unix2fat(&fake_sb, &ts, &time, &date, NULL); + KUNIT_EXPECT_EQ_MSG(test, + 49021, + le16_to_cpu(time), + "Time mismatch without centiseconds\n"); + KUNIT_EXPECT_EQ_MSG(test, + 10143, + le16_to_cpu(date), + "Date mismatch without centiseconds\n"); +} + +static void fat_truncate_atime_test(struct kunit *test) +{ + static struct msdos_sb_info fake_sb; + struct timespec64 actual; + struct fat_truncate_atime_testcase *testcase = + (struct fat_truncate_atime_testcase *)test->param_value; + + fat_test_set_time_offset(&fake_sb, testcase->time_offset); + + actual = fat_truncate_atime(&fake_sb, &testcase->ts); + KUNIT_EXPECT_EQ_MSG(test, + testcase->expected.tv_sec, + actual.tv_sec, + "Atime truncation seconds mismatch\n"); + KUNIT_EXPECT_EQ_MSG(test, + testcase->expected.tv_nsec, + actual.tv_nsec, + "Atime truncation nanoseconds mismatch\n"); +} + static struct kunit_case fat_test_cases[] = { KUNIT_CASE(fat_checksum_test), KUNIT_CASE_PARAM(fat_time_fat2unix_test, fat_time_gen_params), KUNIT_CASE_PARAM(fat_time_unix2fat_test, fat_time_gen_params), + KUNIT_CASE_PARAM(fat_time_unix2fat_clamp_test, + fat_unix2fat_clamp_gen_params), + KUNIT_CASE(fat_time_unix2fat_no_csec_test), + KUNIT_CASE_PARAM(fat_truncate_atime_test, + fat_truncate_atime_gen_params), {}, }; -- 2.34.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2] fat: add KUnit tests for timestamp conversion helpers 2026-03-15 22:24 ` [PATCH v2] " aviv.daum @ 2026-03-16 3:25 ` OGAWA Hirofumi 2026-03-24 7:19 ` OGAWA Hirofumi 2026-03-24 7:47 ` Christian Brauner 1 sibling, 1 reply; 8+ messages in thread From: OGAWA Hirofumi @ 2026-03-16 3:25 UTC (permalink / raw) To: Christian Brauner; +Cc: aviv.daum, linux-kernel aviv.daum@gmail.com writes: > From: avivdaum <aviv.daum@gmail.com> > > Extend fat_test with coverage for FAT timestamp edge cases that are not > currently exercised. > > Add tests for fat_time_unix2fat() clamping at the UTC boundaries and > when time_offset pushes an otherwise valid timestamp outside the FAT > date range. Also cover the NULL time_cs path used by existing callers, > and verify fat_truncate_atime() truncation across timezone offsets. > > Signed-off-by: avivdaum <aviv.daum@gmail.com> Looks good. Thanks. Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> > v2: > - Compare __le16 values directly in unix2fat tests > > --- > fs/fat/fat_test.c | 181 ++++++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 173 insertions(+), 8 deletions(-) > > diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c > index 1f0062659..5c97a7fcc 100644 > --- a/fs/fat/fat_test.c > +++ b/fs/fat/fat_test.c > @@ -29,6 +29,22 @@ struct fat_timestamp_testcase { > int time_offset; > }; > > +struct fat_unix2fat_clamp_testcase { > + const char *name; > + struct timespec64 ts; > + __le16 time; > + __le16 date; > + u8 cs; > + int time_offset; > +}; > + > +struct fat_truncate_atime_testcase { > + const char *name; > + struct timespec64 ts; > + struct timespec64 expected; > + int time_offset; > +}; > + > static struct fat_timestamp_testcase time_test_cases[] = { > { > .name = "Earliest possible UTC (1980-01-01 00:00:00)", > @@ -120,13 +136,92 @@ static struct fat_timestamp_testcase time_test_cases[] = { > }, > }; > > +static struct fat_unix2fat_clamp_testcase unix2fat_clamp_test_cases[] = { > + { > + .name = "Clamp to earliest FAT date for 1979-12-31 23:59:59 UTC", > + .ts = {.tv_sec = 315532799LL, .tv_nsec = 0L}, > + .time = cpu_to_le16(0), > + .date = cpu_to_le16(33), > + .cs = 0, > + .time_offset = 0, > + }, > + { > + .name = "Clamp after time_offset=-60 pushes 1980-01-01 00:30 UTC below 1980", > + .ts = {.tv_sec = 315534600LL, .tv_nsec = 0L}, > + .time = cpu_to_le16(0), > + .date = cpu_to_le16(33), > + .cs = 0, > + .time_offset = -60, > + }, > + { > + .name = "Clamp to latest FAT date for 2108-01-01 00:00:00 UTC", > + .ts = {.tv_sec = 4354819200LL, .tv_nsec = 0L}, > + .time = cpu_to_le16(49021), > + .date = cpu_to_le16(65439), > + .cs = 199, > + .time_offset = 0, > + }, > + { > + .name = "Clamp after time_offset=60 pushes 2107-12-31 23:30 UTC beyond 2107", > + .ts = {.tv_sec = 4354817400LL, .tv_nsec = 0L}, > + .time = cpu_to_le16(49021), > + .date = cpu_to_le16(65439), > + .cs = 199, > + .time_offset = 60, > + }, > +}; > + > +static struct fat_truncate_atime_testcase truncate_atime_test_cases[] = { > + { > + .name = "UTC atime truncates to 2004-02-29 00:00:00", > + .ts = {.tv_sec = 1078058096LL, .tv_nsec = 789000000L}, > + .expected = {.tv_sec = 1078012800LL, .tv_nsec = 0L}, > + .time_offset = 0, > + }, > + { > + .name = "time_offset=-60 truncates 2004-02-29 00:30 UTC to previous local midnight", > + .ts = {.tv_sec = 1078014645LL, .tv_nsec = 123000000L}, > + .expected = {.tv_sec = 1077930000LL, .tv_nsec = 0L}, > + .time_offset = -60, > + }, > + { > + .name = "time_offset=60 truncates 2004-02-29 23:30 UTC to next local midnight", > + .ts = {.tv_sec = 1078097445LL, .tv_nsec = 123000000L}, > + .expected = {.tv_sec = 1078095600LL, .tv_nsec = 0L}, > + .time_offset = 60, > + }, > +}; > + > static void time_testcase_desc(struct fat_timestamp_testcase *t, > char *desc) > { > strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); > } > > +static void unix2fat_clamp_testcase_desc(struct fat_unix2fat_clamp_testcase *t, > + char *desc) > +{ > + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); > +} > + > +static void truncate_atime_testcase_desc(struct fat_truncate_atime_testcase *t, > + char *desc) > +{ > + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); > +} > + > KUNIT_ARRAY_PARAM(fat_time, time_test_cases, time_testcase_desc); > +KUNIT_ARRAY_PARAM(fat_unix2fat_clamp, unix2fat_clamp_test_cases, > + unix2fat_clamp_testcase_desc); > +KUNIT_ARRAY_PARAM(fat_truncate_atime, truncate_atime_test_cases, > + truncate_atime_testcase_desc); > + > +static void fat_test_set_time_offset(struct msdos_sb_info *sbi, int time_offset) > +{ > + *sbi = (struct msdos_sb_info){}; > + sbi->options.tz_set = 1; > + sbi->options.time_offset = time_offset; > +} > > static void fat_time_fat2unix_test(struct kunit *test) > { > @@ -135,8 +230,7 @@ static void fat_time_fat2unix_test(struct kunit *test) > struct fat_timestamp_testcase *testcase = > (struct fat_timestamp_testcase *)test->param_value; > > - fake_sb.options.tz_set = 1; > - fake_sb.options.time_offset = testcase->time_offset; > + fat_test_set_time_offset(&fake_sb, testcase->time_offset); > > fat_time_fat2unix(&fake_sb, &ts, > testcase->time, > @@ -160,18 +254,17 @@ static void fat_time_unix2fat_test(struct kunit *test) > struct fat_timestamp_testcase *testcase = > (struct fat_timestamp_testcase *)test->param_value; > > - fake_sb.options.tz_set = 1; > - fake_sb.options.time_offset = testcase->time_offset; > + fat_test_set_time_offset(&fake_sb, testcase->time_offset); > > fat_time_unix2fat(&fake_sb, &testcase->ts, > &time, &date, &cs); > KUNIT_EXPECT_EQ_MSG(test, > - le16_to_cpu(testcase->time), > - le16_to_cpu(time), > + testcase->time, > + time, > "Time mismatch\n"); > KUNIT_EXPECT_EQ_MSG(test, > - le16_to_cpu(testcase->date), > - le16_to_cpu(date), > + testcase->date, > + date, > "Date mismatch\n"); > KUNIT_EXPECT_EQ_MSG(test, > testcase->cs, > @@ -179,10 +272,82 @@ static void fat_time_unix2fat_test(struct kunit *test) > "Centisecond mismatch\n"); > } > > +static void fat_time_unix2fat_clamp_test(struct kunit *test) > +{ > + static struct msdos_sb_info fake_sb; > + __le16 date, time; > + u8 cs; > + struct fat_unix2fat_clamp_testcase *testcase = > + (struct fat_unix2fat_clamp_testcase *)test->param_value; > + > + fat_test_set_time_offset(&fake_sb, testcase->time_offset); > + > + fat_time_unix2fat(&fake_sb, &testcase->ts, &time, &date, &cs); > + KUNIT_EXPECT_EQ_MSG(test, > + testcase->time, > + time, > + "Clamped time mismatch\n"); > + KUNIT_EXPECT_EQ_MSG(test, > + testcase->date, > + date, > + "Clamped date mismatch\n"); > + KUNIT_EXPECT_EQ_MSG(test, > + testcase->cs, > + cs, > + "Clamped centisecond mismatch\n"); > +} > + > +static void fat_time_unix2fat_no_csec_test(struct kunit *test) > +{ > + static struct msdos_sb_info fake_sb; > + struct timespec64 ts = { > + .tv_sec = 946684799LL, > + .tv_nsec = 0L, > + }; > + __le16 date, time; > + > + fat_test_set_time_offset(&fake_sb, 0); > + > + fat_time_unix2fat(&fake_sb, &ts, &time, &date, NULL); > + KUNIT_EXPECT_EQ_MSG(test, > + 49021, > + le16_to_cpu(time), > + "Time mismatch without centiseconds\n"); > + KUNIT_EXPECT_EQ_MSG(test, > + 10143, > + le16_to_cpu(date), > + "Date mismatch without centiseconds\n"); > +} > + > +static void fat_truncate_atime_test(struct kunit *test) > +{ > + static struct msdos_sb_info fake_sb; > + struct timespec64 actual; > + struct fat_truncate_atime_testcase *testcase = > + (struct fat_truncate_atime_testcase *)test->param_value; > + > + fat_test_set_time_offset(&fake_sb, testcase->time_offset); > + > + actual = fat_truncate_atime(&fake_sb, &testcase->ts); > + KUNIT_EXPECT_EQ_MSG(test, > + testcase->expected.tv_sec, > + actual.tv_sec, > + "Atime truncation seconds mismatch\n"); > + KUNIT_EXPECT_EQ_MSG(test, > + testcase->expected.tv_nsec, > + actual.tv_nsec, > + "Atime truncation nanoseconds mismatch\n"); > +} > + > static struct kunit_case fat_test_cases[] = { > KUNIT_CASE(fat_checksum_test), > KUNIT_CASE_PARAM(fat_time_fat2unix_test, fat_time_gen_params), > KUNIT_CASE_PARAM(fat_time_unix2fat_test, fat_time_gen_params), > + KUNIT_CASE_PARAM(fat_time_unix2fat_clamp_test, > + fat_unix2fat_clamp_gen_params), > + KUNIT_CASE(fat_time_unix2fat_no_csec_test), > + KUNIT_CASE_PARAM(fat_truncate_atime_test, > + fat_truncate_atime_gen_params), > {}, > }; -- OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] fat: add KUnit tests for timestamp conversion helpers 2026-03-16 3:25 ` OGAWA Hirofumi @ 2026-03-24 7:19 ` OGAWA Hirofumi 2026-03-24 7:48 ` Christian Brauner 0 siblings, 1 reply; 8+ messages in thread From: OGAWA Hirofumi @ 2026-03-24 7:19 UTC (permalink / raw) To: Christian Brauner; +Cc: aviv.daum, linux-kernel Hi, Christian Ping? IIRC, you requested me to send FAT patches (fs/*) to you. Thanks. OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> writes: > aviv.daum@gmail.com writes: > >> From: avivdaum <aviv.daum@gmail.com> >> >> Extend fat_test with coverage for FAT timestamp edge cases that are not >> currently exercised. >> >> Add tests for fat_time_unix2fat() clamping at the UTC boundaries and >> when time_offset pushes an otherwise valid timestamp outside the FAT >> date range. Also cover the NULL time_cs path used by existing callers, >> and verify fat_truncate_atime() truncation across timezone offsets. >> >> Signed-off-by: avivdaum <aviv.daum@gmail.com> > > Looks good. Thanks. > > Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> > >> v2: >> - Compare __le16 values directly in unix2fat tests >> >> --- >> fs/fat/fat_test.c | 181 ++++++++++++++++++++++++++++++++++++++++++++-- >> 1 file changed, 173 insertions(+), 8 deletions(-) >> >> diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c >> index 1f0062659..5c97a7fcc 100644 >> --- a/fs/fat/fat_test.c >> +++ b/fs/fat/fat_test.c >> @@ -29,6 +29,22 @@ struct fat_timestamp_testcase { >> int time_offset; >> }; >> >> +struct fat_unix2fat_clamp_testcase { >> + const char *name; >> + struct timespec64 ts; >> + __le16 time; >> + __le16 date; >> + u8 cs; >> + int time_offset; >> +}; >> + >> +struct fat_truncate_atime_testcase { >> + const char *name; >> + struct timespec64 ts; >> + struct timespec64 expected; >> + int time_offset; >> +}; >> + >> static struct fat_timestamp_testcase time_test_cases[] = { >> { >> .name = "Earliest possible UTC (1980-01-01 00:00:00)", >> @@ -120,13 +136,92 @@ static struct fat_timestamp_testcase time_test_cases[] = { >> }, >> }; >> >> +static struct fat_unix2fat_clamp_testcase unix2fat_clamp_test_cases[] = { >> + { >> + .name = "Clamp to earliest FAT date for 1979-12-31 23:59:59 UTC", >> + .ts = {.tv_sec = 315532799LL, .tv_nsec = 0L}, >> + .time = cpu_to_le16(0), >> + .date = cpu_to_le16(33), >> + .cs = 0, >> + .time_offset = 0, >> + }, >> + { >> + .name = "Clamp after time_offset=-60 pushes 1980-01-01 00:30 UTC below 1980", >> + .ts = {.tv_sec = 315534600LL, .tv_nsec = 0L}, >> + .time = cpu_to_le16(0), >> + .date = cpu_to_le16(33), >> + .cs = 0, >> + .time_offset = -60, >> + }, >> + { >> + .name = "Clamp to latest FAT date for 2108-01-01 00:00:00 UTC", >> + .ts = {.tv_sec = 4354819200LL, .tv_nsec = 0L}, >> + .time = cpu_to_le16(49021), >> + .date = cpu_to_le16(65439), >> + .cs = 199, >> + .time_offset = 0, >> + }, >> + { >> + .name = "Clamp after time_offset=60 pushes 2107-12-31 23:30 UTC beyond 2107", >> + .ts = {.tv_sec = 4354817400LL, .tv_nsec = 0L}, >> + .time = cpu_to_le16(49021), >> + .date = cpu_to_le16(65439), >> + .cs = 199, >> + .time_offset = 60, >> + }, >> +}; >> + >> +static struct fat_truncate_atime_testcase truncate_atime_test_cases[] = { >> + { >> + .name = "UTC atime truncates to 2004-02-29 00:00:00", >> + .ts = {.tv_sec = 1078058096LL, .tv_nsec = 789000000L}, >> + .expected = {.tv_sec = 1078012800LL, .tv_nsec = 0L}, >> + .time_offset = 0, >> + }, >> + { >> + .name = "time_offset=-60 truncates 2004-02-29 00:30 UTC to previous local midnight", >> + .ts = {.tv_sec = 1078014645LL, .tv_nsec = 123000000L}, >> + .expected = {.tv_sec = 1077930000LL, .tv_nsec = 0L}, >> + .time_offset = -60, >> + }, >> + { >> + .name = "time_offset=60 truncates 2004-02-29 23:30 UTC to next local midnight", >> + .ts = {.tv_sec = 1078097445LL, .tv_nsec = 123000000L}, >> + .expected = {.tv_sec = 1078095600LL, .tv_nsec = 0L}, >> + .time_offset = 60, >> + }, >> +}; >> + >> static void time_testcase_desc(struct fat_timestamp_testcase *t, >> char *desc) >> { >> strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); >> } >> >> +static void unix2fat_clamp_testcase_desc(struct fat_unix2fat_clamp_testcase *t, >> + char *desc) >> +{ >> + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); >> +} >> + >> +static void truncate_atime_testcase_desc(struct fat_truncate_atime_testcase *t, >> + char *desc) >> +{ >> + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); >> +} >> + >> KUNIT_ARRAY_PARAM(fat_time, time_test_cases, time_testcase_desc); >> +KUNIT_ARRAY_PARAM(fat_unix2fat_clamp, unix2fat_clamp_test_cases, >> + unix2fat_clamp_testcase_desc); >> +KUNIT_ARRAY_PARAM(fat_truncate_atime, truncate_atime_test_cases, >> + truncate_atime_testcase_desc); >> + >> +static void fat_test_set_time_offset(struct msdos_sb_info *sbi, int time_offset) >> +{ >> + *sbi = (struct msdos_sb_info){}; >> + sbi->options.tz_set = 1; >> + sbi->options.time_offset = time_offset; >> +} >> >> static void fat_time_fat2unix_test(struct kunit *test) >> { >> @@ -135,8 +230,7 @@ static void fat_time_fat2unix_test(struct kunit *test) >> struct fat_timestamp_testcase *testcase = >> (struct fat_timestamp_testcase *)test->param_value; >> >> - fake_sb.options.tz_set = 1; >> - fake_sb.options.time_offset = testcase->time_offset; >> + fat_test_set_time_offset(&fake_sb, testcase->time_offset); >> >> fat_time_fat2unix(&fake_sb, &ts, >> testcase->time, >> @@ -160,18 +254,17 @@ static void fat_time_unix2fat_test(struct kunit *test) >> struct fat_timestamp_testcase *testcase = >> (struct fat_timestamp_testcase *)test->param_value; >> >> - fake_sb.options.tz_set = 1; >> - fake_sb.options.time_offset = testcase->time_offset; >> + fat_test_set_time_offset(&fake_sb, testcase->time_offset); >> >> fat_time_unix2fat(&fake_sb, &testcase->ts, >> &time, &date, &cs); >> KUNIT_EXPECT_EQ_MSG(test, >> - le16_to_cpu(testcase->time), >> - le16_to_cpu(time), >> + testcase->time, >> + time, >> "Time mismatch\n"); >> KUNIT_EXPECT_EQ_MSG(test, >> - le16_to_cpu(testcase->date), >> - le16_to_cpu(date), >> + testcase->date, >> + date, >> "Date mismatch\n"); >> KUNIT_EXPECT_EQ_MSG(test, >> testcase->cs, >> @@ -179,10 +272,82 @@ static void fat_time_unix2fat_test(struct kunit *test) >> "Centisecond mismatch\n"); >> } >> >> +static void fat_time_unix2fat_clamp_test(struct kunit *test) >> +{ >> + static struct msdos_sb_info fake_sb; >> + __le16 date, time; >> + u8 cs; >> + struct fat_unix2fat_clamp_testcase *testcase = >> + (struct fat_unix2fat_clamp_testcase *)test->param_value; >> + >> + fat_test_set_time_offset(&fake_sb, testcase->time_offset); >> + >> + fat_time_unix2fat(&fake_sb, &testcase->ts, &time, &date, &cs); >> + KUNIT_EXPECT_EQ_MSG(test, >> + testcase->time, >> + time, >> + "Clamped time mismatch\n"); >> + KUNIT_EXPECT_EQ_MSG(test, >> + testcase->date, >> + date, >> + "Clamped date mismatch\n"); >> + KUNIT_EXPECT_EQ_MSG(test, >> + testcase->cs, >> + cs, >> + "Clamped centisecond mismatch\n"); >> +} >> + >> +static void fat_time_unix2fat_no_csec_test(struct kunit *test) >> +{ >> + static struct msdos_sb_info fake_sb; >> + struct timespec64 ts = { >> + .tv_sec = 946684799LL, >> + .tv_nsec = 0L, >> + }; >> + __le16 date, time; >> + >> + fat_test_set_time_offset(&fake_sb, 0); >> + >> + fat_time_unix2fat(&fake_sb, &ts, &time, &date, NULL); >> + KUNIT_EXPECT_EQ_MSG(test, >> + 49021, >> + le16_to_cpu(time), >> + "Time mismatch without centiseconds\n"); >> + KUNIT_EXPECT_EQ_MSG(test, >> + 10143, >> + le16_to_cpu(date), >> + "Date mismatch without centiseconds\n"); >> +} >> + >> +static void fat_truncate_atime_test(struct kunit *test) >> +{ >> + static struct msdos_sb_info fake_sb; >> + struct timespec64 actual; >> + struct fat_truncate_atime_testcase *testcase = >> + (struct fat_truncate_atime_testcase *)test->param_value; >> + >> + fat_test_set_time_offset(&fake_sb, testcase->time_offset); >> + >> + actual = fat_truncate_atime(&fake_sb, &testcase->ts); >> + KUNIT_EXPECT_EQ_MSG(test, >> + testcase->expected.tv_sec, >> + actual.tv_sec, >> + "Atime truncation seconds mismatch\n"); >> + KUNIT_EXPECT_EQ_MSG(test, >> + testcase->expected.tv_nsec, >> + actual.tv_nsec, >> + "Atime truncation nanoseconds mismatch\n"); >> +} >> + >> static struct kunit_case fat_test_cases[] = { >> KUNIT_CASE(fat_checksum_test), >> KUNIT_CASE_PARAM(fat_time_fat2unix_test, fat_time_gen_params), >> KUNIT_CASE_PARAM(fat_time_unix2fat_test, fat_time_gen_params), >> + KUNIT_CASE_PARAM(fat_time_unix2fat_clamp_test, >> + fat_unix2fat_clamp_gen_params), >> + KUNIT_CASE(fat_time_unix2fat_no_csec_test), >> + KUNIT_CASE_PARAM(fat_truncate_atime_test, >> + fat_truncate_atime_gen_params), >> {}, >> }; -- OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] fat: add KUnit tests for timestamp conversion helpers 2026-03-24 7:19 ` OGAWA Hirofumi @ 2026-03-24 7:48 ` Christian Brauner 2026-03-24 8:02 ` OGAWA Hirofumi 0 siblings, 1 reply; 8+ messages in thread From: Christian Brauner @ 2026-03-24 7:48 UTC (permalink / raw) To: OGAWA Hirofumi; +Cc: aviv.daum, linux-kernel On Tue, Mar 24, 2026 at 04:19:50PM +0900, OGAWA Hirofumi wrote: > Hi, Christian > > Ping? IIRC, you requested me to send FAT patches (fs/*) to you. Thanks, sorry I missed this. Can you please update the MAINTAINERS entry to: diff --git a/MAINTAINERS b/MAINTAINERS index 61bf550fd37c..cb81b80a0f88 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27671,6 +27671,7 @@ F: drivers/mtd/nand/raw/vf610_nfc.c VFAT/FAT/MSDOS FILESYSTEM M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> +L: linux-fsdevel@vger.kernel.org S: Maintained F: Documentation/filesystems/vfat.rst F: fs/fat/ then this gets auto-handled on-list. :) Thanks! ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2] fat: add KUnit tests for timestamp conversion helpers 2026-03-24 7:48 ` Christian Brauner @ 2026-03-24 8:02 ` OGAWA Hirofumi 0 siblings, 0 replies; 8+ messages in thread From: OGAWA Hirofumi @ 2026-03-24 8:02 UTC (permalink / raw) To: Christian Brauner; +Cc: aviv.daum, linux-kernel Christian Brauner <brauner@kernel.org> writes: > On Tue, Mar 24, 2026 at 04:19:50PM +0900, OGAWA Hirofumi wrote: >> Hi, Christian >> >> Ping? IIRC, you requested me to send FAT patches (fs/*) to you. > > Thanks, sorry I missed this. Can you please update the MAINTAINERS entry to: > > diff --git a/MAINTAINERS b/MAINTAINERS > index 61bf550fd37c..cb81b80a0f88 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -27671,6 +27671,7 @@ F: drivers/mtd/nand/raw/vf610_nfc.c > > VFAT/FAT/MSDOS FILESYSTEM > M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> > +L: linux-fsdevel@vger.kernel.org > S: Maintained > F: Documentation/filesystems/vfat.rst > F: fs/fat/ > > then this gets auto-handled on-list. :) I should send this patch from me? Or could you apply the your patch yourself? Then I'm, of course, Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Thanks. -- OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] fat: add KUnit tests for timestamp conversion helpers 2026-03-15 22:24 ` [PATCH v2] " aviv.daum 2026-03-16 3:25 ` OGAWA Hirofumi @ 2026-03-24 7:47 ` Christian Brauner 1 sibling, 0 replies; 8+ messages in thread From: Christian Brauner @ 2026-03-24 7:47 UTC (permalink / raw) To: OGAWA Hirofumi, aviv.daum; +Cc: Christian Brauner, linux-kernel On Mon, 16 Mar 2026 00:24:04 +0200, aviv.daum@gmail.com wrote: > Extend fat_test with coverage for FAT timestamp edge cases that are not > currently exercised. > > Add tests for fat_time_unix2fat() clamping at the UTC boundaries and > when time_offset pushes an otherwise valid timestamp outside the FAT > date range. Also cover the NULL time_cs path used by existing callers, > and verify fat_truncate_atime() truncation across timezone offsets. > > [...] Applied to the vfs-7.1.fat branch of the vfs/vfs.git tree. Patches in the vfs-7.1.fat branch should appear in linux-next soon. Please report any outstanding bugs that were missed during review in a new review to the original patch series allowing us to drop it. It's encouraged to provide Acked-bys and Reviewed-bys even though the patch has now been applied. If possible patch trailers will be updated. Note that commit hashes shown below are subject to change due to rebase, trailer updates or similar. If in doubt, please check the listed branch. tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git branch: vfs-7.1.fat [1/1] fat: add KUnit tests for timestamp conversion helpers https://git.kernel.org/vfs/vfs/c/24d061105f04 ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-03-24 8:03 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-15 14:42 [PATCH] fat: add KUnit tests for timestamp conversion helpers aviv.daum 2026-03-15 17:38 ` OGAWA Hirofumi 2026-03-15 22:24 ` [PATCH v2] " aviv.daum 2026-03-16 3:25 ` OGAWA Hirofumi 2026-03-24 7:19 ` OGAWA Hirofumi 2026-03-24 7:48 ` Christian Brauner 2026-03-24 8:02 ` OGAWA Hirofumi 2026-03-24 7:47 ` Christian Brauner
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox