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