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
fat_checksum_test(struct kunit * test)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
fat_clus_to_blknr_test(struct kunit * test)23 static void fat_clus_to_blknr_test(struct kunit *test)
24 {
25 static const 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
fat_get_blknr_offset_test(struct kunit * test)35 static void fat_get_blknr_offset_test(struct kunit *test)
36 {
37 static const 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
time_testcase_desc(struct fat_timestamp_testcase * t,char * desc)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
unix2fat_clamp_testcase_desc(struct fat_unix2fat_clamp_testcase * t,char * desc)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
truncate_atime_testcase_desc(struct fat_truncate_atime_testcase * t,char * desc)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
fat_test_set_time_offset(struct msdos_sb_info * sbi,int time_offset)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
fat_time_fat2unix_test(struct kunit * test)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
fat_time_unix2fat_test(struct kunit * test)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
fat_time_unix2fat_clamp_test(struct kunit * test)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
fat_time_unix2fat_no_csec_test(struct kunit * test)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
fat_truncate_atime_test(struct kunit * test)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