1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * KUnit tests for FAT filesystems. 4 * 5 * Copyright (C) 2020 Google LLC. 6 * Author: David Gow <davidgow@google.com> 7 */ 8 9 #include <kunit/test.h> 10 11 #include "fat.h" 12 13 static void fat_checksum_test(struct kunit *test) 14 { 15 /* With no extension. */ 16 KUNIT_EXPECT_EQ(test, fat_checksum("VMLINUX "), (u8)44); 17 /* With 3-letter extension. */ 18 KUNIT_EXPECT_EQ(test, fat_checksum("README TXT"), (u8)115); 19 /* With short (1-letter) extension. */ 20 KUNIT_EXPECT_EQ(test, fat_checksum("ABCDEFGHA "), (u8)98); 21 } 22 23 struct fat_timestamp_testcase { 24 const char *name; 25 struct timespec64 ts; 26 __le16 time; 27 __le16 date; 28 u8 cs; 29 int time_offset; 30 }; 31 32 struct fat_unix2fat_clamp_testcase { 33 const char *name; 34 struct timespec64 ts; 35 __le16 time; 36 __le16 date; 37 u8 cs; 38 int time_offset; 39 }; 40 41 struct fat_truncate_atime_testcase { 42 const char *name; 43 struct timespec64 ts; 44 struct timespec64 expected; 45 int time_offset; 46 }; 47 48 static struct fat_timestamp_testcase time_test_cases[] = { 49 { 50 .name = "Earliest possible UTC (1980-01-01 00:00:00)", 51 .ts = {.tv_sec = 315532800LL, .tv_nsec = 0L}, 52 .time = cpu_to_le16(0), 53 .date = cpu_to_le16(33), 54 .cs = 0, 55 .time_offset = 0, 56 }, 57 { 58 .name = "Latest possible UTC (2107-12-31 23:59:58)", 59 .ts = {.tv_sec = 4354819198LL, .tv_nsec = 0L}, 60 .time = cpu_to_le16(49021), 61 .date = cpu_to_le16(65439), 62 .cs = 0, 63 .time_offset = 0, 64 }, 65 { 66 .name = "Earliest possible (UTC-11) (== 1979-12-31 13:00:00 UTC)", 67 .ts = {.tv_sec = 315493200LL, .tv_nsec = 0L}, 68 .time = cpu_to_le16(0), 69 .date = cpu_to_le16(33), 70 .cs = 0, 71 .time_offset = 11 * 60, 72 }, 73 { 74 .name = "Latest possible (UTC+11) (== 2108-01-01 10:59:58 UTC)", 75 .ts = {.tv_sec = 4354858798LL, .tv_nsec = 0L}, 76 .time = cpu_to_le16(49021), 77 .date = cpu_to_le16(65439), 78 .cs = 0, 79 .time_offset = -11 * 60, 80 }, 81 { 82 .name = "Leap Day / Year (1996-02-29 00:00:00)", 83 .ts = {.tv_sec = 825552000LL, .tv_nsec = 0L}, 84 .time = cpu_to_le16(0), 85 .date = cpu_to_le16(8285), 86 .cs = 0, 87 .time_offset = 0, 88 }, 89 { 90 .name = "Year 2000 is leap year (2000-02-29 00:00:00)", 91 .ts = {.tv_sec = 951782400LL, .tv_nsec = 0L}, 92 .time = cpu_to_le16(0), 93 .date = cpu_to_le16(10333), 94 .cs = 0, 95 .time_offset = 0, 96 }, 97 { 98 .name = "Year 2100 not leap year (2100-03-01 00:00:00)", 99 .ts = {.tv_sec = 4107542400LL, .tv_nsec = 0L}, 100 .time = cpu_to_le16(0), 101 .date = cpu_to_le16(61537), 102 .cs = 0, 103 .time_offset = 0, 104 }, 105 { 106 .name = "Leap year + timezone UTC+1 (== 2004-02-29 00:30:00 UTC)", 107 .ts = {.tv_sec = 1078014600LL, .tv_nsec = 0L}, 108 .time = cpu_to_le16(48064), 109 .date = cpu_to_le16(12380), 110 .cs = 0, 111 .time_offset = -60, 112 }, 113 { 114 .name = "Leap year + timezone UTC-1 (== 2004-02-29 23:30:00 UTC)", 115 .ts = {.tv_sec = 1078097400LL, .tv_nsec = 0L}, 116 .time = cpu_to_le16(960), 117 .date = cpu_to_le16(12385), 118 .cs = 0, 119 .time_offset = 60, 120 }, 121 { 122 .name = "VFAT odd-second resolution (1999-12-31 23:59:59)", 123 .ts = {.tv_sec = 946684799LL, .tv_nsec = 0L}, 124 .time = cpu_to_le16(49021), 125 .date = cpu_to_le16(10143), 126 .cs = 100, 127 .time_offset = 0, 128 }, 129 { 130 .name = "VFAT 10ms resolution (1980-01-01 00:00:00:0010)", 131 .ts = {.tv_sec = 315532800LL, .tv_nsec = 10000000L}, 132 .time = cpu_to_le16(0), 133 .date = cpu_to_le16(33), 134 .cs = 1, 135 .time_offset = 0, 136 }, 137 }; 138 139 static struct fat_unix2fat_clamp_testcase unix2fat_clamp_test_cases[] = { 140 { 141 .name = "Clamp to earliest FAT date for 1979-12-31 23:59:59 UTC", 142 .ts = {.tv_sec = 315532799LL, .tv_nsec = 0L}, 143 .time = cpu_to_le16(0), 144 .date = cpu_to_le16(33), 145 .cs = 0, 146 .time_offset = 0, 147 }, 148 { 149 .name = "Clamp after time_offset=-60 pushes 1980-01-01 00:30 UTC below 1980", 150 .ts = {.tv_sec = 315534600LL, .tv_nsec = 0L}, 151 .time = cpu_to_le16(0), 152 .date = cpu_to_le16(33), 153 .cs = 0, 154 .time_offset = -60, 155 }, 156 { 157 .name = "Clamp to latest FAT date for 2108-01-01 00:00:00 UTC", 158 .ts = {.tv_sec = 4354819200LL, .tv_nsec = 0L}, 159 .time = cpu_to_le16(49021), 160 .date = cpu_to_le16(65439), 161 .cs = 199, 162 .time_offset = 0, 163 }, 164 { 165 .name = "Clamp after time_offset=60 pushes 2107-12-31 23:30 UTC beyond 2107", 166 .ts = {.tv_sec = 4354817400LL, .tv_nsec = 0L}, 167 .time = cpu_to_le16(49021), 168 .date = cpu_to_le16(65439), 169 .cs = 199, 170 .time_offset = 60, 171 }, 172 }; 173 174 static struct fat_truncate_atime_testcase truncate_atime_test_cases[] = { 175 { 176 .name = "UTC atime truncates to 2004-02-29 00:00:00", 177 .ts = {.tv_sec = 1078058096LL, .tv_nsec = 789000000L}, 178 .expected = {.tv_sec = 1078012800LL, .tv_nsec = 0L}, 179 .time_offset = 0, 180 }, 181 { 182 .name = "time_offset=-60 truncates 2004-02-29 00:30 UTC to previous local midnight", 183 .ts = {.tv_sec = 1078014645LL, .tv_nsec = 123000000L}, 184 .expected = {.tv_sec = 1077930000LL, .tv_nsec = 0L}, 185 .time_offset = -60, 186 }, 187 { 188 .name = "time_offset=60 truncates 2004-02-29 23:30 UTC to next local midnight", 189 .ts = {.tv_sec = 1078097445LL, .tv_nsec = 123000000L}, 190 .expected = {.tv_sec = 1078095600LL, .tv_nsec = 0L}, 191 .time_offset = 60, 192 }, 193 }; 194 195 static void time_testcase_desc(struct fat_timestamp_testcase *t, 196 char *desc) 197 { 198 strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); 199 } 200 201 static void unix2fat_clamp_testcase_desc(struct fat_unix2fat_clamp_testcase *t, 202 char *desc) 203 { 204 strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); 205 } 206 207 static void truncate_atime_testcase_desc(struct fat_truncate_atime_testcase *t, 208 char *desc) 209 { 210 strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); 211 } 212 213 KUNIT_ARRAY_PARAM(fat_time, time_test_cases, time_testcase_desc); 214 KUNIT_ARRAY_PARAM(fat_unix2fat_clamp, unix2fat_clamp_test_cases, 215 unix2fat_clamp_testcase_desc); 216 KUNIT_ARRAY_PARAM(fat_truncate_atime, truncate_atime_test_cases, 217 truncate_atime_testcase_desc); 218 219 static void fat_test_set_time_offset(struct msdos_sb_info *sbi, int time_offset) 220 { 221 memset(sbi, 0, sizeof(*sbi)); 222 sbi->options.tz_set = 1; 223 sbi->options.time_offset = time_offset; 224 } 225 226 static void fat_time_fat2unix_test(struct kunit *test) 227 { 228 static struct msdos_sb_info fake_sb; 229 struct timespec64 ts; 230 struct fat_timestamp_testcase *testcase = 231 (struct fat_timestamp_testcase *)test->param_value; 232 233 fat_test_set_time_offset(&fake_sb, testcase->time_offset); 234 235 fat_time_fat2unix(&fake_sb, &ts, 236 testcase->time, 237 testcase->date, 238 testcase->cs); 239 KUNIT_EXPECT_EQ_MSG(test, 240 testcase->ts.tv_sec, 241 ts.tv_sec, 242 "Timestamp mismatch (seconds)\n"); 243 KUNIT_EXPECT_EQ_MSG(test, 244 testcase->ts.tv_nsec, 245 ts.tv_nsec, 246 "Timestamp mismatch (nanoseconds)\n"); 247 } 248 249 static void fat_time_unix2fat_test(struct kunit *test) 250 { 251 static struct msdos_sb_info fake_sb; 252 __le16 date, time; 253 u8 cs; 254 struct fat_timestamp_testcase *testcase = 255 (struct fat_timestamp_testcase *)test->param_value; 256 257 fat_test_set_time_offset(&fake_sb, testcase->time_offset); 258 259 fat_time_unix2fat(&fake_sb, &testcase->ts, 260 &time, &date, &cs); 261 KUNIT_EXPECT_EQ_MSG(test, 262 testcase->time, 263 time, 264 "Time mismatch\n"); 265 KUNIT_EXPECT_EQ_MSG(test, 266 testcase->date, 267 date, 268 "Date mismatch\n"); 269 KUNIT_EXPECT_EQ_MSG(test, 270 testcase->cs, 271 cs, 272 "Centisecond mismatch\n"); 273 } 274 275 static void fat_time_unix2fat_clamp_test(struct kunit *test) 276 { 277 static struct msdos_sb_info fake_sb; 278 __le16 date, time; 279 u8 cs; 280 struct fat_unix2fat_clamp_testcase *testcase = 281 (struct fat_unix2fat_clamp_testcase *)test->param_value; 282 283 fat_test_set_time_offset(&fake_sb, testcase->time_offset); 284 285 fat_time_unix2fat(&fake_sb, &testcase->ts, &time, &date, &cs); 286 KUNIT_EXPECT_EQ_MSG(test, 287 testcase->time, 288 time, 289 "Clamped time mismatch\n"); 290 KUNIT_EXPECT_EQ_MSG(test, 291 testcase->date, 292 date, 293 "Clamped date mismatch\n"); 294 KUNIT_EXPECT_EQ_MSG(test, 295 testcase->cs, 296 cs, 297 "Clamped centisecond mismatch\n"); 298 } 299 300 static void fat_time_unix2fat_no_csec_test(struct kunit *test) 301 { 302 static struct msdos_sb_info fake_sb; 303 struct timespec64 ts = { 304 .tv_sec = 946684799LL, 305 .tv_nsec = 0L, 306 }; 307 __le16 date, time; 308 309 fat_test_set_time_offset(&fake_sb, 0); 310 311 fat_time_unix2fat(&fake_sb, &ts, &time, &date, NULL); 312 KUNIT_EXPECT_EQ_MSG(test, 313 49021, 314 le16_to_cpu(time), 315 "Time mismatch without centiseconds\n"); 316 KUNIT_EXPECT_EQ_MSG(test, 317 10143, 318 le16_to_cpu(date), 319 "Date mismatch without centiseconds\n"); 320 } 321 322 static void fat_truncate_atime_test(struct kunit *test) 323 { 324 static struct msdos_sb_info fake_sb; 325 struct timespec64 actual; 326 struct fat_truncate_atime_testcase *testcase = 327 (struct fat_truncate_atime_testcase *)test->param_value; 328 329 fat_test_set_time_offset(&fake_sb, testcase->time_offset); 330 331 actual = fat_truncate_atime(&fake_sb, &testcase->ts); 332 KUNIT_EXPECT_EQ_MSG(test, 333 testcase->expected.tv_sec, 334 actual.tv_sec, 335 "Atime truncation seconds mismatch\n"); 336 KUNIT_EXPECT_EQ_MSG(test, 337 testcase->expected.tv_nsec, 338 actual.tv_nsec, 339 "Atime truncation nanoseconds mismatch\n"); 340 } 341 342 static struct kunit_case fat_test_cases[] = { 343 KUNIT_CASE(fat_checksum_test), 344 KUNIT_CASE_PARAM(fat_time_fat2unix_test, fat_time_gen_params), 345 KUNIT_CASE_PARAM(fat_time_unix2fat_test, fat_time_gen_params), 346 KUNIT_CASE_PARAM(fat_time_unix2fat_clamp_test, 347 fat_unix2fat_clamp_gen_params), 348 KUNIT_CASE(fat_time_unix2fat_no_csec_test), 349 KUNIT_CASE_PARAM(fat_truncate_atime_test, 350 fat_truncate_atime_gen_params), 351 {}, 352 }; 353 354 static struct kunit_suite fat_test_suite = { 355 .name = "fat_test", 356 .test_cases = fat_test_cases, 357 }; 358 359 kunit_test_suites(&fat_test_suite); 360 361 MODULE_DESCRIPTION("KUnit tests for FAT filesystems"); 362 MODULE_LICENSE("GPL v2"); 363