xref: /linux/fs/fat/fat_test.c (revision bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba)
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