1 /*
2 * Copyright (c) 2019 Peter Bigot Consulting, LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /* Basic littlefs operations:
8 * * create
9 * * write
10 * * stat
11 * * read
12 * * seek
13 * * tell
14 * * truncate
15 * * unlink
16 * * sync
17 */
18
19 #include <string.h>
20 #include <zephyr/ztest.h>
21 #include "testfs_tests.h"
22 #include "testfs_lfs.h"
23 #include <lfs.h>
24
25 #include <zephyr/fs/littlefs.h>
26
27 #define HELLO "hello"
28 #define GOODBYE "goodbye"
29
mount(struct fs_mount_t * mp)30 static int mount(struct fs_mount_t *mp)
31 {
32 TC_PRINT("mounting %s\n", mp->mnt_point);
33
34 zassert_equal(fs_mount(mp), 0,
35 "mount failed");
36
37 return TC_PASS;
38 }
39
clear_partition(struct fs_mount_t * mp)40 static int clear_partition(struct fs_mount_t *mp)
41 {
42 TC_PRINT("clearing partition %s\n", mp->mnt_point);
43
44 zassert_equal(testfs_lfs_wipe_partition(mp),
45 TC_PASS,
46 "failed to wipe partition");
47
48 return TC_PASS;
49 }
50
clean_statvfs(const struct fs_mount_t * mp)51 static int clean_statvfs(const struct fs_mount_t *mp)
52 {
53 struct fs_statvfs stat;
54
55 TC_PRINT("checking clean statvfs of %s\n", mp->mnt_point);
56
57 zassert_equal(fs_statvfs(mp->mnt_point, &stat), 0,
58 "statvfs failed");
59
60 TC_PRINT("%s: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n",
61 mp->mnt_point,
62 stat.f_bsize, stat.f_frsize, stat.f_blocks, stat.f_bfree);
63 zassert_equal(stat.f_bsize, 16,
64 "bsize fail");
65 zassert_equal(stat.f_frsize, 4096,
66 "frsize fail");
67 zassert_equal(stat.f_blocks, 16,
68 "blocks fail");
69 zassert_equal(stat.f_bfree, stat.f_blocks - 2U,
70 "bfree fail");
71
72 return TC_PASS;
73 }
74
create_write_hello(const struct fs_mount_t * mp)75 static int create_write_hello(const struct fs_mount_t *mp)
76 {
77 struct testfs_path path;
78 struct fs_file_t file;
79
80 fs_file_t_init(&file);
81 TC_PRINT("creating and writing file\n");
82
83 zassert_equal(fs_open(&file,
84 testfs_path_init(&path, mp,
85 HELLO,
86 TESTFS_PATH_END),
87 FS_O_CREATE | FS_O_RDWR),
88 0,
89 "open hello failed");
90
91 struct fs_dirent stat;
92
93 zassert_equal(fs_stat(path.path, &stat),
94 0,
95 "stat new hello failed");
96
97 zassert_equal(stat.type, FS_DIR_ENTRY_FILE,
98 "stat new hello not file");
99 zassert_equal(strcmp(stat.name, HELLO), 0,
100 "stat new hello not hello");
101 zassert_equal(stat.size, 0,
102 "stat new hello not empty");
103
104 zassert_equal(testfs_write_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
105 TESTFS_BUFFER_SIZE,
106 "write constant failed");
107
108 zassert_equal(fs_stat(path.path, &stat),
109 0,
110 "stat written hello failed");
111
112 zassert_equal(stat.type, FS_DIR_ENTRY_FILE,
113 "stat written hello not file");
114 zassert_equal(strcmp(stat.name, HELLO), 0,
115 "stat written hello not hello");
116
117 /* Anomalous behavior requiring upstream response */
118 if (true) {
119 /* VARIATION POINT: littlefs does not update the file size of
120 * an open file. See upstream issue #250.
121 */
122 zassert_equal(stat.size, 0,
123 "stat written hello bad size");
124 }
125
126 zassert_equal(fs_close(&file), 0,
127 "close hello failed");
128
129 zassert_equal(fs_stat(path.path, &stat),
130 0,
131 "stat closed hello failed");
132
133 zassert_equal(stat.type, FS_DIR_ENTRY_FILE,
134 "stat closed hello not file");
135 zassert_equal(strcmp(stat.name, HELLO), 0,
136 "stat closed hello not hello");
137 zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
138 "stat closed hello badsize");
139
140 return TC_PASS;
141 }
142
verify_hello(const struct fs_mount_t * mp)143 static int verify_hello(const struct fs_mount_t *mp)
144 {
145 struct testfs_path path;
146 struct fs_file_t file;
147
148 fs_file_t_init(&file);
149 TC_PRINT("opening and verifying file\n");
150
151 zassert_equal(fs_open(&file,
152 testfs_path_init(&path, mp,
153 HELLO,
154 TESTFS_PATH_END),
155 FS_O_CREATE | FS_O_RDWR),
156 0,
157 "verify hello open failed");
158
159 zassert_equal(fs_tell(&file), 0U,
160 "verify hello open tell failed");
161
162 zassert_equal(testfs_verify_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
163 TESTFS_BUFFER_SIZE,
164 "verify hello at start failed");
165
166 zassert_equal(fs_tell(&file), TESTFS_BUFFER_SIZE,
167 "verify hello read tell failed");
168
169 zassert_equal(fs_close(&file), 0,
170 "verify close hello failed");
171
172 return TC_PASS;
173 }
174
seek_within_hello(const struct fs_mount_t * mp)175 static int seek_within_hello(const struct fs_mount_t *mp)
176 {
177 struct testfs_path path;
178 struct fs_file_t file;
179
180 fs_file_t_init(&file);
181 TC_PRINT("seek and tell in file\n");
182
183 zassert_equal(fs_open(&file,
184 testfs_path_init(&path, mp,
185 HELLO,
186 TESTFS_PATH_END),
187 FS_O_CREATE | FS_O_RDWR),
188 0,
189 "verify hello open failed");
190
191 zassert_equal(fs_tell(&file), 0U,
192 "verify hello open tell failed");
193
194 struct fs_dirent stat;
195
196 zassert_equal(fs_stat(path.path, &stat),
197 0,
198 "stat old hello failed");
199 zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
200 "stat old hello bad size");
201
202 off_t pos = stat.size / 4;
203
204 zassert_equal(fs_seek(&file, pos, FS_SEEK_SET),
205 0,
206 "verify hello seek near mid failed");
207
208 zassert_equal(fs_tell(&file), pos,
209 "verify hello tell near mid failed");
210
211 zassert_equal(testfs_verify_incrementing(&file, pos, TESTFS_BUFFER_SIZE),
212 TESTFS_BUFFER_SIZE - pos,
213 "verify hello at middle failed");
214
215 zassert_equal(fs_tell(&file), stat.size,
216 "verify hello read middle tell failed");
217
218 zassert_equal(fs_seek(&file, -stat.size, FS_SEEK_CUR),
219 0,
220 "verify hello seek back from cur failed");
221
222 zassert_equal(fs_tell(&file), 0U,
223 "verify hello tell back from cur failed");
224
225 zassert_equal(fs_seek(&file, -pos, FS_SEEK_END),
226 0,
227 "verify hello seek from end failed");
228
229 zassert_equal(fs_tell(&file), stat.size - pos,
230 "verify hello tell from end failed");
231
232 zassert_equal(testfs_verify_incrementing(&file, stat.size - pos,
233 TESTFS_BUFFER_SIZE),
234 pos,
235 "verify hello at post middle failed");
236
237 zassert_equal(fs_close(&file), 0,
238 "verify close hello failed");
239
240 return TC_PASS;
241 }
242
truncate_hello(const struct fs_mount_t * mp)243 static int truncate_hello(const struct fs_mount_t *mp)
244 {
245 struct testfs_path path;
246 struct fs_file_t file;
247
248 fs_file_t_init(&file);
249 TC_PRINT("truncate in file\n");
250
251 zassert_equal(fs_open(&file,
252 testfs_path_init(&path, mp,
253 HELLO,
254 TESTFS_PATH_END),
255 FS_O_CREATE | FS_O_RDWR),
256 0,
257 "verify hello open failed");
258
259 struct fs_dirent stat;
260
261 zassert_equal(fs_stat(path.path, &stat),
262 0,
263 "stat old hello failed");
264 zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
265 "stat old hello bad size");
266
267 off_t pos = 3 * stat.size / 4;
268
269 zassert_equal(fs_tell(&file), 0U,
270 "truncate initial tell failed");
271
272 zassert_equal(fs_truncate(&file, pos),
273 0,
274 "truncate 3/4 failed");
275
276 zassert_equal(fs_tell(&file), 0U,
277 "truncate post tell failed");
278
279 zassert_equal(fs_stat(path.path, &stat),
280 0,
281 "stat open 3/4 failed");
282
283 /* Anomalous behavior requiring upstream response */
284 if (true) {
285 /* VARIATION POINT: littlefs does not update the file size of
286 * an open file. See upstream issue #250.
287 */
288 zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
289 "stat open 3/4 bad size");
290 }
291
292 zassert_equal(testfs_verify_incrementing(&file, 0, 64),
293 48,
294 "post truncate content unexpected");
295
296 zassert_equal(fs_close(&file), 0,
297 "post truncate close failed");
298
299 /* After close size is correct. */
300 zassert_equal(fs_stat(path.path, &stat),
301 0,
302 "stat closed truncated failed");
303 zassert_equal(stat.size, pos,
304 "stat closed truncated bad size");
305
306 return TC_PASS;
307 }
308
unlink_hello(const struct fs_mount_t * mp)309 static int unlink_hello(const struct fs_mount_t *mp)
310 {
311 struct testfs_path path;
312
313 TC_PRINT("unlink hello\n");
314
315 testfs_path_init(&path, mp,
316 HELLO,
317 TESTFS_PATH_END);
318
319 struct fs_dirent stat;
320
321 zassert_equal(fs_stat(path.path, &stat),
322 0,
323 "stat existing hello failed");
324 zassert_equal(fs_unlink(path.path),
325 0,
326 "unlink hello failed");
327 zassert_equal(fs_stat(path.path, &stat),
328 -ENOENT,
329 "stat existing hello failed");
330
331 return TC_PASS;
332 }
333
sync_goodbye(const struct fs_mount_t * mp)334 static int sync_goodbye(const struct fs_mount_t *mp)
335 {
336 struct testfs_path path;
337 struct fs_file_t file;
338
339 fs_file_t_init(&file);
340 TC_PRINT("sync goodbye\n");
341
342 zassert_equal(fs_open(&file,
343 testfs_path_init(&path, mp,
344 GOODBYE,
345 TESTFS_PATH_END),
346 FS_O_CREATE | FS_O_RDWR),
347 0,
348 "sync goodbye failed");
349
350 struct fs_dirent stat;
351
352 zassert_equal(fs_stat(path.path, &stat),
353 0,
354 "stat existing hello failed");
355 zassert_equal(stat.size, 0,
356 "stat new goodbye not empty");
357
358 zassert_equal(testfs_write_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
359 TESTFS_BUFFER_SIZE,
360 "write goodbye failed");
361
362 zassert_equal(fs_tell(&file), TESTFS_BUFFER_SIZE,
363 "tell goodbye failed");
364
365 if (true) {
366 /* Upstream issue #250 */
367 zassert_equal(stat.size, 0,
368 "stat new goodbye not empty");
369 }
370
371 zassert_equal(fs_sync(&file), 0,
372 "sync goodbye failed");
373
374 zassert_equal(fs_tell(&file), TESTFS_BUFFER_SIZE,
375 "tell synced moved");
376
377 zassert_equal(fs_stat(path.path, &stat),
378 0,
379 "stat existing hello failed");
380 printk("sync size %u\n", (uint32_t)stat.size);
381
382 zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
383 "stat synced goodbye not correct");
384
385 zassert_equal(fs_close(&file), 0,
386 "post sync close failed");
387
388 /* After close size is correct. */
389 zassert_equal(fs_stat(path.path, &stat),
390 0,
391 "stat sync failed");
392 zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
393 "stat sync bad size");
394
395 return TC_PASS;
396 }
397
verify_goodbye(const struct fs_mount_t * mp)398 static int verify_goodbye(const struct fs_mount_t *mp)
399 {
400 struct testfs_path path;
401 struct fs_file_t file;
402
403 fs_file_t_init(&file);
404 TC_PRINT("verify goodbye\n");
405
406 zassert_equal(fs_open(&file,
407 testfs_path_init(&path, mp,
408 GOODBYE,
409 TESTFS_PATH_END),
410 FS_O_CREATE | FS_O_RDWR),
411 0,
412 "verify goodbye failed");
413
414 zassert_equal(testfs_verify_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
415 TESTFS_BUFFER_SIZE,
416 "write goodbye failed");
417
418 zassert_equal(fs_close(&file), 0,
419 "post sync close failed");
420
421 return TC_PASS;
422 }
423
check_medium(void)424 static int check_medium(void)
425 {
426 struct fs_mount_t *mp = &testfs_medium_mnt;
427 struct fs_statvfs stat;
428
429 zassert_equal(clear_partition(mp), TC_PASS,
430 "clear partition failed");
431
432 zassert_equal(fs_mount(mp), 0,
433 "medium mount failed");
434
435 zassert_equal(fs_statvfs(mp->mnt_point, &stat), 0,
436 "statvfs failed");
437
438 TC_PRINT("%s: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n",
439 mp->mnt_point,
440 stat.f_bsize, stat.f_frsize, stat.f_blocks, stat.f_bfree);
441 zassert_equal(stat.f_bsize, MEDIUM_IO_SIZE,
442 "bsize fail");
443 zassert_equal(stat.f_frsize, 4096,
444 "frsize fail");
445 zassert_equal(stat.f_blocks, 240,
446 "blocks fail");
447 zassert_equal(stat.f_bfree, stat.f_blocks - 2U,
448 "bfree fail");
449
450 zassert_equal(fs_unmount(mp), 0,
451 "medium unmount failed");
452
453 return TC_PASS;
454 }
455
check_large(void)456 static int check_large(void)
457 {
458 struct fs_mount_t *mp = &testfs_large_mnt;
459 struct fs_statvfs stat;
460
461 zassert_equal(clear_partition(mp), TC_PASS,
462 "clear partition failed");
463
464 zassert_equal(fs_mount(mp), 0,
465 "large mount failed");
466
467 zassert_equal(fs_statvfs(mp->mnt_point, &stat), 0,
468 "statvfs failed");
469
470 TC_PRINT("%s: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n",
471 mp->mnt_point,
472 stat.f_bsize, stat.f_frsize, stat.f_blocks, stat.f_bfree);
473 zassert_equal(stat.f_bsize, LARGE_IO_SIZE,
474 "bsize fail");
475 zassert_equal(stat.f_frsize, 32768,
476 "frsize fail");
477 zassert_equal(stat.f_blocks, 96,
478 "blocks fail");
479 zassert_equal(stat.f_bfree, stat.f_blocks - 2U,
480 "bfree fail");
481
482 zassert_equal(fs_unmount(mp), 0,
483 "large unmount failed");
484
485 return TC_PASS;
486 }
487
num_files(struct fs_mount_t * mp)488 static int num_files(struct fs_mount_t *mp)
489 {
490 struct testfs_path path;
491 char name[2] = { 0 };
492 const char *pstr;
493 struct fs_file_t files[CONFIG_FS_LITTLEFS_NUM_FILES];
494 size_t fi = 0;
495 int rc;
496
497 memset(files, 0, sizeof(files));
498
499 TC_PRINT("CONFIG_FS_LITTLEFS_NUM_FILES=%u\n", CONFIG_FS_LITTLEFS_NUM_FILES);
500 while (fi < ARRAY_SIZE(files)) {
501 struct fs_file_t *const file = &files[fi];
502
503 name[0] = 'A' + fi;
504 pstr = testfs_path_init(&path, mp,
505 name,
506 TESTFS_PATH_END);
507
508 TC_PRINT("opening %s\n", pstr);
509 rc = fs_open(file, pstr, FS_O_CREATE | FS_O_RDWR);
510 zassert_equal(rc, 0, "open %s failed: %d", pstr, rc);
511
512 rc = testfs_write_incrementing(file, 0, TESTFS_BUFFER_SIZE);
513 zassert_equal(rc, TESTFS_BUFFER_SIZE, "write %s failed: %d", pstr, rc);
514
515 ++fi;
516 }
517
518 while (fi-- != 0) {
519 struct fs_file_t *const file = &files[fi];
520
521 name[0] = 'A' + fi;
522 pstr = testfs_path_init(&path, mp,
523 name,
524 TESTFS_PATH_END);
525
526 TC_PRINT("Close and unlink %s\n", pstr);
527
528 rc = fs_close(file);
529 zassert_equal(rc, 0, "close %s failed: %d", pstr, rc);
530
531 rc = fs_unlink(pstr);
532 zassert_equal(rc, 0, "unlink %s failed: %d", pstr, rc);
533 }
534
535 return TC_PASS;
536 }
537
num_dirs(struct fs_mount_t * mp)538 static int num_dirs(struct fs_mount_t *mp)
539 {
540 struct testfs_path path;
541 char name[3] = "Dx";
542 const char *pstr;
543 struct fs_dir_t dirs[CONFIG_FS_LITTLEFS_NUM_DIRS];
544 size_t di = 0;
545 int rc;
546
547 memset(dirs, 0, sizeof(dirs));
548
549 TC_PRINT("CONFIG_FS_LITTLEFS_NUM_DIRS=%u\n", CONFIG_FS_LITTLEFS_NUM_DIRS);
550 while (di < ARRAY_SIZE(dirs)) {
551 struct fs_dir_t *const dir = &dirs[di];
552
553 name[1] = 'A' + di;
554 pstr = testfs_path_init(&path, mp,
555 name,
556 TESTFS_PATH_END);
557
558 TC_PRINT("making and opening directory %s\n", pstr);
559 rc = fs_mkdir(pstr);
560 zassert_equal(rc, 0, "mkdir %s failed: %d", pstr, rc);
561
562 rc = fs_opendir(dir, pstr);
563 zassert_equal(rc, 0, "opendir %s failed: %d", name, rc);
564
565 ++di;
566 }
567
568 while (di-- != 0) {
569 struct fs_dir_t *const dir = &dirs[di];
570
571 name[1] = 'A' + di;
572 pstr = testfs_path_init(&path, mp,
573 name,
574 TESTFS_PATH_END);
575
576 TC_PRINT("Close and rmdir %s\n", pstr);
577
578 rc = fs_closedir(dir);
579 zassert_equal(rc, 0, "closedir %s failed: %d", name, rc);
580
581 rc = fs_unlink(pstr);
582 zassert_equal(rc, 0, "unlink %s failed: %d", name, rc);
583 }
584
585 return TC_PASS;
586 }
587
ZTEST(littlefs,test_lfs_basic)588 ZTEST(littlefs, test_lfs_basic)
589 {
590 struct fs_mount_t *mp = &testfs_small_mnt;
591
592 zassert_equal(clear_partition(mp), TC_PASS,
593 "clear partition failed");
594
595 zassert_equal(mount(mp), TC_PASS,
596 "clean mount failed");
597
598 zassert_equal(clean_statvfs(mp), TC_PASS,
599 "clean statvfs failed");
600
601 zassert_equal(create_write_hello(mp), TC_PASS,
602 "write hello failed");
603
604 zassert_equal(verify_hello(mp), TC_PASS,
605 "verify hello failed");
606
607 zassert_equal(seek_within_hello(mp), TC_PASS,
608 "seek within hello failed");
609
610 zassert_equal(truncate_hello(mp), TC_PASS,
611 "truncate hello failed");
612
613 zassert_equal(unlink_hello(mp), TC_PASS,
614 "unlink hello failed");
615
616 zassert_equal(sync_goodbye(mp), TC_PASS,
617 "sync goodbye failed");
618
619 zassert_equal(num_files(mp), TC_PASS,
620 "num_files failed");
621
622 zassert_equal(num_dirs(mp), TC_PASS,
623 "num_dirs failed");
624
625 TC_PRINT("unmounting %s\n", mp->mnt_point);
626 zassert_equal(fs_unmount(mp), 0,
627 "unmount small failed");
628
629 k_sleep(K_MSEC(100)); /* flush log messages */
630 TC_PRINT("checking double unmount diagnoses\n");
631 zassert_equal(fs_unmount(mp), -EINVAL,
632 "unmount unmounted failed");
633
634 zassert_equal(mount(mp), TC_PASS,
635 "remount failed");
636
637 zassert_equal(verify_goodbye(mp), TC_PASS,
638 "verify goodbye failed");
639
640 zassert_equal(fs_unmount(mp), 0,
641 "unmount2 small failed");
642
643 if (IS_ENABLED(CONFIG_APP_TEST_CUSTOM)) {
644 zassert_equal(check_medium(), TC_PASS,
645 "check medium failed");
646
647 zassert_equal(check_large(), TC_PASS,
648 "check large failed");
649 }
650 }
651