1 /*
2 * Copyright (c) 2016 Intel Corporation
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <errno.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <zephyr/shell/shell.h>
12 #include <zephyr/init.h>
13 #include <zephyr/fs/fs.h>
14 #include <zephyr/sd/sd_spec.h>
15 #include <stdlib.h>
16 #include <inttypes.h>
17 #include <limits.h>
18
19 #ifdef CONFIG_FILE_SYSTEM_SHELL_MOUNT_COMMAND
20 /* FAT */
21 #ifdef CONFIG_FAT_FILESYSTEM_ELM
22 #include <ff.h>
23 #define FATFS_MNTP "/RAM:"
24 /* FatFs work area */
25 FATFS fat_fs;
26 /* mounting info */
27 static struct fs_mount_t fatfs_mnt = {
28 .type = FS_FATFS,
29 .fs_data = &fat_fs,
30 };
31 #endif
32 /* LITTLEFS */
33 #ifdef CONFIG_FILE_SYSTEM_LITTLEFS
34 #include <zephyr/fs/littlefs.h>
35
36 /* TODO: Implement dynamic storage dev selection */
37 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
38
39 #if defined(CONFIG_DISK_DRIVER_SDMMC)
40 #define DISK_NAME "SD"
41 #elif defined(CONFIG_DISK_DRIVER_MMC)
42 #define DISK_NAME "SD2"
43 #else
44 #error "No disk device defined, is your board supported?"
45 #endif
46
47 FS_LITTLEFS_DECLARE_CUSTOM_CONFIG(
48 lfs_data,
49 CONFIG_SDHC_BUFFER_ALIGNMENT,
50 SDMMC_DEFAULT_BLOCK_SIZE,
51 SDMMC_DEFAULT_BLOCK_SIZE,
52 SDMMC_DEFAULT_BLOCK_SIZE,
53 2 * SDMMC_DEFAULT_BLOCK_SIZE);
54
55 static struct fs_mount_t littlefs_mnt = {
56 .type = FS_LITTLEFS,
57 .fs_data = &lfs_data,
58 .flags = FS_MOUNT_FLAG_USE_DISK_ACCESS,
59 .storage_dev = DISK_NAME,
60 };
61 #else
62 #include <zephyr/storage/flash_map.h>
63
64 #define STORAGE_PARTIION_NODE_ID DT_PHANDLE(DT_INST(0, zephyr_fstab_littlefs), partition)
65
66 #if DT_FIXED_PARTITION_EXISTS(STORAGE_PARTIION_NODE_ID)
67 #define STORAGE_PARTITION_ID DT_FIXED_PARTITION_ID(STORAGE_PARTIION_NODE_ID)
68 #else
69 #define STORAGE_PARTITION storage_partition
70 #define STORAGE_PARTITION_ID FIXED_PARTITION_ID(STORAGE_PARTITION)
71 #endif
72
73 FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(lfs_data);
74 static struct fs_mount_t littlefs_mnt = {
75 .type = FS_LITTLEFS,
76 .fs_data = &lfs_data,
77 .storage_dev = (void *)STORAGE_PARTITION_ID,
78 };
79 #endif
80 #endif
81 #endif
82
83 #define BUF_CNT 64
84
85 #define MAX_PATH_LEN 128
86 #define MAX_FILENAME_LEN 128
87 #define MAX_INPUT_LEN 20
88
89 #define SHELL_FS "fs"
90
91 /* Maintenance guarantees this begins with '/' and is NUL-terminated. */
92 static char cwd[MAX_PATH_LEN] = "/";
93
create_abs_path(const char * name,char * path,size_t len)94 static void create_abs_path(const char *name, char *path, size_t len)
95 {
96 if (name[0] == '/') {
97 strncpy(path, name, len);
98 path[len - 1] = '\0';
99 } else {
100 if (cwd[1] == '\0') {
101 __ASSERT_NO_MSG(len >= 2);
102 *path++ = '/';
103 --len;
104
105 strncpy(path, name, len);
106 path[len - 1] = '\0';
107 } else {
108 strncpy(path, cwd, len);
109 path[len - 1] = '\0';
110
111 size_t plen = strlen(path);
112
113 if (plen < len) {
114 path += plen;
115 *path++ = '/';
116 len -= plen + 1U;
117 strncpy(path, name, len);
118 path[len - 1] = '\0';
119 }
120 }
121 }
122 }
123
cmd_cd(const struct shell * sh,size_t argc,char ** argv)124 static int cmd_cd(const struct shell *sh, size_t argc, char **argv)
125 {
126 char path[MAX_PATH_LEN];
127 struct fs_dirent entry;
128 int err;
129
130 if (argc < 2) {
131 strcpy(cwd, "/");
132 return 0;
133 }
134
135 if (strcmp(argv[1], "..") == 0) {
136 char *prev = strrchr(cwd, '/');
137
138 if (!prev || prev == cwd) {
139 strcpy(cwd, "/");
140 } else {
141 *prev = '\0';
142 }
143
144 /* No need to test that a parent exists */
145 return 0;
146 }
147
148 create_abs_path(argv[1], path, sizeof(path));
149
150 err = fs_stat(path, &entry);
151 if (err != 0) {
152 shell_error(sh, "%s doesn't exist", path);
153 return -ENOENT;
154 }
155
156 if (entry.type != FS_DIR_ENTRY_DIR) {
157 shell_error(sh, "%s is not a directory", path);
158 return -ENOTDIR;
159 }
160
161 strncpy(cwd, path, sizeof(cwd));
162 cwd[sizeof(cwd) - 1] = '\0';
163
164 return 0;
165 }
166
cmd_ls(const struct shell * sh,size_t argc,char ** argv)167 static int cmd_ls(const struct shell *sh, size_t argc, char **argv)
168 {
169 char path[MAX_PATH_LEN];
170 struct fs_dir_t dir;
171 int err;
172
173 if (argc < 2) {
174 strncpy(path, cwd, sizeof(path));
175 path[sizeof(path) - 1] = '\0';
176 } else {
177 create_abs_path(argv[1], path, sizeof(path));
178 }
179
180 fs_dir_t_init(&dir);
181
182 err = fs_opendir(&dir, path);
183 if (err != 0) {
184 shell_error(sh, "Unable to open %s (err %d)", path, err);
185 return -EIO;
186 }
187
188 while (1) {
189 struct fs_dirent entry;
190 const char *name_end;
191
192 err = fs_readdir(&dir, &entry);
193 if (err != 0) {
194 shell_error(sh, "Unable to read directory");
195 break;
196 }
197
198 /* Check for end of directory listing */
199 if (entry.name[0] == '\0') {
200 break;
201 }
202
203 name_end = (entry.type == FS_DIR_ENTRY_DIR) ? "/" : "";
204 if (IS_ENABLED(CONFIG_FILE_SYSTEM_SHELL_LS_SIZE)) {
205 shell_print(sh, "%8zu %s%s", entry.size, entry.name, name_end);
206 } else {
207 shell_print(sh, "%s%s", entry.name, name_end);
208 }
209 }
210
211 fs_closedir(&dir);
212
213 return 0;
214 }
215
cmd_pwd(const struct shell * sh,size_t argc,char ** argv)216 static int cmd_pwd(const struct shell *sh, size_t argc, char **argv)
217 {
218 shell_print(sh, "%s", cwd);
219
220 return 0;
221 }
222
cmd_trunc(const struct shell * sh,size_t argc,char ** argv)223 static int cmd_trunc(const struct shell *sh, size_t argc, char **argv)
224 {
225 char path[MAX_PATH_LEN];
226 struct fs_file_t file;
227 int length;
228 int err;
229
230 create_abs_path(argv[1], path, sizeof(path));
231
232 if (argc > 2) {
233 length = strtol(argv[2], NULL, 0);
234 } else {
235 length = 0;
236 }
237
238 fs_file_t_init(&file);
239 err = fs_open(&file, path, FS_O_WRITE);
240 if (err != 0) {
241 shell_error(sh, "Failed to open %s (%d)", path, err);
242 return -EIO;
243 }
244
245 err = fs_truncate(&file, length);
246 if (err != 0) {
247 shell_error(sh, "Failed to truncate %s (%d)", path, err);
248 err = -EIO;
249 }
250
251 fs_close(&file);
252
253 return err;
254 }
255
cmd_mkdir(const struct shell * sh,size_t argc,char ** argv)256 static int cmd_mkdir(const struct shell *sh, size_t argc, char **argv)
257 {
258 int err;
259 char path[MAX_PATH_LEN];
260
261 create_abs_path(argv[1], path, sizeof(path));
262
263 err = fs_mkdir(path);
264 if (err != 0) {
265 shell_error(sh, "Error creating dir[%d]", err);
266 err = -EIO;
267 }
268
269 return err;
270 }
271
cmd_rm(const struct shell * sh,size_t argc,char ** argv)272 static int cmd_rm(const struct shell *sh, size_t argc, char **argv)
273 {
274 int err;
275 char path[MAX_PATH_LEN];
276
277 create_abs_path(argv[1], path, sizeof(path));
278
279 err = fs_unlink(path);
280 if (err != 0) {
281 shell_error(sh, "Failed to remove %s (%d)", path, err);
282 err = -EIO;
283 }
284
285 return err;
286 }
287
cmd_cp(const struct shell * sh,size_t argc,char ** argv)288 static int cmd_cp(const struct shell *sh, size_t argc, char **argv)
289 {
290 int err;
291 int close_err;
292 char path_src[MAX_PATH_LEN];
293 char path_dst[MAX_PATH_LEN];
294 struct fs_file_t file_src;
295 struct fs_file_t file_dst;
296 uint8_t buf[BUF_CNT];
297 ssize_t buf_len;
298 ssize_t num_written;
299
300 create_abs_path(argv[1], path_src, sizeof(path_src));
301 create_abs_path(argv[2], path_dst, sizeof(path_dst));
302
303 fs_file_t_init(&file_src);
304 fs_file_t_init(&file_dst);
305
306 err = fs_open(&file_src, path_src, FS_O_READ);
307 if (err != 0) {
308 shell_error(sh, "Failed to open %s (%d)", path_src, err);
309 err = -EIO;
310 goto exit;
311 }
312
313 err = fs_open(&file_dst, path_dst, FS_O_CREATE | FS_O_TRUNC | FS_O_WRITE);
314 if (err != 0) {
315 shell_error(sh, "Failed to open %s (%d)", path_dst, err);
316 err = -EIO;
317 goto close_src;
318 }
319
320 while (true) {
321 buf_len = fs_read(&file_src, buf, BUF_CNT);
322 if (buf_len < 0) {
323 shell_error(sh, "Failed to read %s (%d)", path_src, (int)buf_len);
324 err = -EIO;
325 goto close;
326 }
327 if (buf_len == 0) {
328 break;
329 }
330
331 num_written = fs_write(&file_dst, buf, buf_len);
332 if (num_written < 0) {
333 shell_error(sh, "Failed to write %s (%d)", path_dst, (int)num_written);
334 err = -EIO;
335 goto close;
336 }
337 if (num_written != buf_len) {
338 shell_error(sh, "Failed to write %s", path_dst);
339 err = -EIO;
340 goto close;
341 }
342 }
343
344 close:
345 close_err = fs_close(&file_dst);
346 if (close_err != 0) {
347 shell_error(sh, "Failed to close %s", path_dst);
348 err = -EIO;
349 }
350
351 close_src:
352 close_err = fs_close(&file_src);
353 if (close_err != 0) {
354 shell_error(sh, "Failed to close %s", path_src);
355 err = -EIO;
356 }
357
358 exit:
359 return err;
360 }
361
cmd_read(const struct shell * sh,size_t argc,char ** argv)362 static int cmd_read(const struct shell *sh, size_t argc, char **argv)
363 {
364 char path[MAX_PATH_LEN];
365 struct fs_dirent dirent;
366 struct fs_file_t file;
367 int count;
368 off_t offset;
369 int err;
370
371 create_abs_path(argv[1], path, sizeof(path));
372
373 if (argc > 2) {
374 count = strtol(argv[2], NULL, 0);
375 if (count <= 0) {
376 count = INT_MAX;
377 }
378 } else {
379 count = INT_MAX;
380 }
381
382 if (argc > 3) {
383 offset = strtol(argv[3], NULL, 0);
384 } else {
385 offset = 0;
386 }
387
388 err = fs_stat(path, &dirent);
389 if (err != 0) {
390 shell_error(sh, "Failed to obtain file %s (err: %d)", path, err);
391 return -EIO;
392 }
393
394 if (dirent.type != FS_DIR_ENTRY_FILE) {
395 shell_error(sh, "Not a file %s", path);
396 return -EIO;
397 }
398
399 shell_print(sh, "File size: %zd", dirent.size);
400
401 fs_file_t_init(&file);
402 err = fs_open(&file, path, FS_O_READ);
403 if (err != 0) {
404 shell_error(sh, "Failed to open %s (%d)", path, err);
405 return -EIO;
406 }
407
408 if (offset > 0) {
409 err = fs_seek(&file, offset, FS_SEEK_SET);
410 if (err != 0) {
411 shell_error(sh, "Failed to seek %s (%d)", path, err);
412 fs_close(&file);
413 return -EIO;
414 }
415 }
416
417 ssize_t read = 0;
418 while (count > 0) {
419 uint8_t buf[16];
420 int i;
421
422 read = fs_read(&file, buf, MIN(count, sizeof(buf)));
423 if (read <= 0) {
424 break;
425 }
426
427 shell_fprintf(sh, SHELL_NORMAL, "%08X ", (uint32_t)offset);
428
429 for (i = 0; i < read; i++) {
430 shell_fprintf(sh, SHELL_NORMAL, "%02X ", buf[i]);
431 }
432 for (; i < sizeof(buf); i++) {
433 shell_fprintf(sh, SHELL_NORMAL, " ");
434 }
435 i = sizeof(buf) - i;
436 shell_fprintf(sh, SHELL_NORMAL, "%*c", i * 3, ' ');
437
438 for (i = 0; i < read; i++) {
439 shell_fprintf(sh, SHELL_NORMAL, "%c",
440 buf[i] < 32 || buf[i] > 127 ? '.' : buf[i]);
441 }
442
443 shell_print(sh, "");
444
445 offset += read;
446 count -= read;
447 }
448
449 if (read < 0) {
450 shell_error(sh, "Failed to read from file %s (err: %zd)", path, read);
451 }
452
453 fs_close(&file);
454
455 return 0;
456 }
457
cmd_cat(const struct shell * sh,size_t argc,char ** argv)458 static int cmd_cat(const struct shell *sh, size_t argc, char **argv)
459 {
460 char path[MAX_PATH_LEN];
461 uint8_t buf[BUF_CNT];
462 struct fs_dirent dirent;
463 struct fs_file_t file;
464 int err;
465 ssize_t read;
466
467 fs_file_t_init(&file);
468
469 for (size_t i = 1; i < argc; ++i) {
470 create_abs_path(argv[i], path, sizeof(path));
471
472 err = fs_stat(path, &dirent);
473 if (err < 0) {
474 shell_error(sh, "Failed to obtain file %s (err: %d)", path, err);
475 continue;
476 }
477
478 if (dirent.type != FS_DIR_ENTRY_FILE) {
479 shell_error(sh, "Not a file %s", path);
480 continue;
481 }
482
483 err = fs_open(&file, path, FS_O_READ);
484 if (err < 0) {
485 shell_error(sh, "Failed to open %s (%d)", path, err);
486 continue;
487 }
488
489 while (true) {
490 read = fs_read(&file, buf, sizeof(buf));
491 if (read <= 0) {
492 break;
493 }
494
495 for (int j = 0; j < read; j++) {
496 shell_fprintf(sh, SHELL_NORMAL, "%c", buf[j]);
497 }
498 }
499
500 if (read < 0) {
501 shell_error(sh, "Failed to read from file %s (err: %zd)", path, read);
502 }
503
504 fs_close(&file);
505 }
506
507 return 0;
508 }
509
cmd_statvfs(const struct shell * sh,size_t argc,char ** argv)510 static int cmd_statvfs(const struct shell *sh, size_t argc, char **argv)
511 {
512 int err;
513 char path[MAX_PATH_LEN];
514 struct fs_statvfs stat;
515
516 create_abs_path(argv[1], path, sizeof(path));
517
518 err = fs_statvfs(path, &stat);
519 if (err < 0) {
520 shell_error(sh, "Failed to statvfs %s (%d)", path, err);
521 return -EIO;
522 }
523
524 shell_fprintf(sh, SHELL_NORMAL, "bsize %lu, frsize %lu, blocks %lu, bfree %lu\n",
525 stat.f_bsize, stat.f_frsize, stat.f_blocks, stat.f_bfree);
526
527 return 0;
528 }
529
cmd_write(const struct shell * sh,size_t argc,char ** argv)530 static int cmd_write(const struct shell *sh, size_t argc, char **argv)
531 {
532 char path[MAX_PATH_LEN];
533 uint8_t buf[BUF_CNT];
534 uint8_t buf_len;
535 int arg_offset;
536 struct fs_file_t file;
537 off_t offset = -1;
538 int err;
539
540 create_abs_path(argv[1], path, sizeof(path));
541
542 if (!strcmp(argv[2], "-o")) {
543 if (argc < 4) {
544 shell_error(sh, "Missing argument");
545 return -EINVAL;
546 }
547
548 offset = strtol(argv[3], NULL, 0);
549
550 arg_offset = 4;
551 } else {
552 arg_offset = 2;
553 }
554
555 fs_file_t_init(&file);
556 err = fs_open(&file, path, FS_O_CREATE | FS_O_WRITE);
557 if (err != 0) {
558 shell_error(sh, "Failed to open %s (%d)", path, err);
559 return -EIO;
560 }
561
562 if (offset < 0) {
563 err = fs_seek(&file, 0, FS_SEEK_END);
564 } else {
565 err = fs_seek(&file, offset, FS_SEEK_SET);
566 }
567 if (err != 0) {
568 shell_error(sh, "Failed to seek %s (%d)", path, err);
569 fs_close(&file);
570 return -EIO;
571 }
572
573 buf_len = 0U;
574 while (arg_offset < argc) {
575 buf[buf_len++] = strtol(argv[arg_offset++], NULL, 16);
576
577 if ((buf_len == BUF_CNT) || (arg_offset == argc)) {
578 err = fs_write(&file, buf, buf_len);
579 if (err < 0) {
580 shell_error(sh, "Failed to write %s (%d)", path, err);
581 fs_close(&file);
582 return -EIO;
583 }
584
585 buf_len = 0U;
586 }
587 }
588
589 fs_close(&file);
590
591 return 0;
592 }
593
594 #ifdef CONFIG_FILE_SYSTEM_SHELL_TEST_COMMANDS
595 const static uint8_t speed_types[][4] = {"B", "KiB", "MiB", "GiB"};
596 const static uint32_t speed_divisor = 1024;
597
file_size_output(const struct shell * sh,double size)598 static void file_size_output(const struct shell *sh, double size)
599 {
600 uint8_t speed_index = 0;
601
602 while (size >= (double)speed_divisor && speed_index < ARRAY_SIZE(speed_types)) {
603 size /= (double)speed_divisor;
604 ++speed_index;
605 }
606
607 shell_print(sh, "File size: %.1f%s", size, speed_types[speed_index]);
608 }
609
speed_output(const struct shell * sh,uint64_t total_time,double loops,double size)610 static void speed_output(const struct shell *sh, uint64_t total_time, double loops, double size)
611 {
612 double time_per_loop = (double)total_time / loops;
613 double throughput = size;
614 uint8_t speed_index = 0;
615
616 if (time_per_loop > 0) {
617 throughput /= (time_per_loop / 1000.0);
618 }
619
620 while (throughput >= (double)speed_divisor && speed_index < ARRAY_SIZE(speed_types)) {
621 throughput /= (double)speed_divisor;
622 ++speed_index;
623 }
624
625 shell_print(sh, "Total: %llums, Per loop: ~%.0fms, Speed: ~%.1f%sps", total_time,
626 time_per_loop, throughput, speed_types[speed_index]);
627 }
628
cmd_read_test(const struct shell * sh,size_t argc,char ** argv)629 static int cmd_read_test(const struct shell *sh, size_t argc, char **argv)
630 {
631 char path[MAX_PATH_LEN];
632 struct fs_dirent dirent;
633 struct fs_file_t file;
634 int err;
635 uint32_t repeat;
636 uint8_t random_data[CONFIG_FILE_SYSTEM_SHELL_BUFFER_SIZE];
637 uint32_t i;
638 uint64_t start_time;
639 uint64_t loop_time;
640 uint64_t total_time = 0;
641 uint32_t loops = 0;
642 uint32_t size;
643
644 if (argc < 3) {
645 shell_error(sh, "Missing parameters: read_test <path> <repeat>");
646 return -EINVAL;
647 }
648
649 create_abs_path(argv[1], path, sizeof(path));
650 repeat = strtol(argv[2], NULL, 0);
651
652 if (repeat == 0 || repeat > 10) {
653 shell_error(sh, "<repeat> must be between 1 and 10.");
654 return -EINVAL;
655 }
656
657 err = fs_stat(path, &dirent);
658
659 if (err != 0) {
660 shell_error(sh, "File status failed: %d", err);
661 return err;
662 }
663
664 if (dirent.type != FS_DIR_ENTRY_FILE) {
665 shell_error(sh, "Provided path is not a file");
666 return -ENOENT;
667 }
668
669 /* Store size of file so we can ensure it was fully read */
670 size = dirent.size;
671 file_size_output(sh, (double)size);
672
673 while (loops < repeat) {
674 start_time = k_uptime_get();
675
676 fs_file_t_init(&file);
677 err = fs_open(&file, path, FS_O_READ);
678 if (err != 0) {
679 shell_error(sh, "Failed to open %s (%d)", path, err);
680 return -EIO;
681 }
682
683 /* Read data in chunk by chunk until the full size has been read */
684 i = 0;
685 while (1) {
686 err = fs_read(&file, random_data, sizeof(random_data));
687 if (err < 0) {
688 shell_error(sh, "Failed to read %s (%d)", path, err);
689 fs_close(&file);
690 return -EIO;
691 }
692
693 i += err;
694
695 if (err == 0) {
696 /* Read finished */
697 break;
698 }
699 }
700
701 /* Ensure file contents is fully read then close file */
702 fs_close(&file);
703
704 if (i != size) {
705 shell_error(sh, "File read error, expected %d bytes but only read %d", size,
706 i);
707 return -EIO;
708 }
709
710 ++loops;
711 loop_time = k_uptime_delta(&start_time);
712 total_time += loop_time;
713 shell_print(sh, "Loop #%u done in %llums.", loops, loop_time);
714 }
715
716 speed_output(sh, total_time, (double)loops, (double)size);
717
718 return 0;
719 }
720
cmd_erase_write_test(const struct shell * sh,size_t argc,char ** argv)721 static int cmd_erase_write_test(const struct shell *sh, size_t argc, char **argv)
722 {
723 char path[MAX_PATH_LEN];
724 struct fs_file_t file;
725 int err;
726 uint32_t size;
727 uint32_t repeat;
728 uint8_t random_data[CONFIG_FILE_SYSTEM_SHELL_BUFFER_SIZE];
729 uint32_t i;
730 uint64_t start_time;
731 uint64_t loop_time;
732 uint64_t total_time = 0;
733 uint32_t loops = 0;
734
735 if (argc < 4) {
736 shell_error(sh, "Missing parameters: erase_write_test <path> <size> <repeat>");
737 return -EINVAL;
738 }
739
740 create_abs_path(argv[1], path, sizeof(path));
741 size = strtol(argv[2], NULL, 0);
742 repeat = strtol(argv[3], NULL, 0);
743
744 if (size == 0) {
745 shell_error(sh, "<size> must be at least 1.");
746 return -EINVAL;
747 }
748
749 if (repeat == 0 || repeat > 10) {
750 shell_error(sh, "<repeat> must be between 1 and 10.");
751 return -EINVAL;
752 }
753
754 /* Generate random data, the contents is not important */
755 i = 0;
756 while (i < sizeof(random_data)) {
757 random_data[i] = (uint8_t)(i % 255);
758 ++i;
759 }
760
761 while (loops < repeat) {
762 start_time = k_uptime_get();
763
764 fs_file_t_init(&file);
765 err = fs_open(&file, path, FS_O_CREATE | FS_O_WRITE);
766 if (err != 0) {
767 shell_error(sh, "Failed to open %s (%d)", path, err);
768 return -EIO;
769 }
770
771 /* Truncate the file size to 0 (if supported, erase if not) */
772 err = fs_truncate(&file, 0);
773
774 if (err == -ENOTSUP) {
775 fs_close(&file);
776
777 err = fs_unlink(path);
778 if (err != 0) {
779 shell_error(sh, "Failed to delete %s (%d)", path, err);
780 return -EIO;
781 }
782
783 err = fs_open(&file, path, FS_O_CREATE | FS_O_WRITE);
784 if (err != 0) {
785 shell_error(sh, "Failed to open %s (%d)", path, err);
786 return -EIO;
787 }
788 } else if (err != 0) {
789 shell_error(sh, "Failed to truncate %s (%d)", path, err);
790 fs_close(&file);
791 return -EIO;
792 }
793
794 /* Write data out chunk by chunk until the full size has been written */
795 i = 0;
796 while (i < size) {
797 uint32_t write_size = size - i;
798
799 if (write_size > sizeof(random_data)) {
800 write_size = sizeof(random_data);
801 }
802
803 err = fs_write(&file, random_data, write_size);
804 if (err < 0) {
805 shell_error(sh, "Failed to write %s (%d)", path, err);
806 fs_close(&file);
807 return -EIO;
808 }
809
810 i += write_size;
811 }
812
813 /* Ensure file contents is fully written then close file */
814 fs_sync(&file);
815 fs_close(&file);
816
817 ++loops;
818 loop_time = k_uptime_delta(&start_time);
819 total_time += loop_time;
820 shell_print(sh, "Loop #%u done in %llums.", loops, loop_time);
821 }
822
823 speed_output(sh, total_time, (double)loops, (double)size);
824
825 return 0;
826 }
827 #endif
828
829 #ifdef CONFIG_FILE_SYSTEM_SHELL_MOUNT_COMMAND
830
mntpt_prepare(char * mntpt)831 static char *mntpt_prepare(char *mntpt)
832 {
833 char *cpy_mntpt;
834
835 cpy_mntpt = k_malloc(strlen(mntpt) + 1);
836 if (cpy_mntpt != NULL) {
837 strcpy(cpy_mntpt, mntpt);
838 }
839 return cpy_mntpt;
840 }
841
842 #if defined(CONFIG_FAT_FILESYSTEM_ELM)
cmd_mount_fat(const struct shell * sh,size_t argc,char ** argv)843 static int cmd_mount_fat(const struct shell *sh, size_t argc, char **argv)
844 {
845 char *mntpt;
846 int res;
847
848 mntpt = mntpt_prepare(argv[1]);
849 if (mntpt == NULL) {
850 shell_error(sh, "Failed to allocate buffer for mount point");
851 return -EIO;
852 }
853
854 fatfs_mnt.mnt_point = (const char *)mntpt;
855 res = fs_mount(&fatfs_mnt);
856 if (res != 0) {
857 shell_error(sh, "Error mounting FAT fs. Error Code [%d]", res);
858 k_free((void *)fatfs_mnt.mnt_point);
859 fatfs_mnt.mnt_point = NULL;
860 return -EIO;
861 }
862
863 shell_print(sh, "Successfully mounted fat fs:%s", fatfs_mnt.mnt_point);
864
865 return 0;
866 }
867 #endif
868
869 #if defined(CONFIG_FILE_SYSTEM_LITTLEFS)
cmd_mount_littlefs(const struct shell * sh,size_t argc,char ** argv)870 static int cmd_mount_littlefs(const struct shell *sh, size_t argc, char **argv)
871 {
872 if (littlefs_mnt.mnt_point != NULL) {
873 return -EBUSY;
874 }
875
876 char *mntpt = mntpt_prepare(argv[1]);
877
878 if (mntpt == NULL) {
879 shell_error(sh, "Failed to allocate mount point");
880 return -EIO;
881 }
882
883 littlefs_mnt.mnt_point = mntpt;
884
885 int rc = fs_mount(&littlefs_mnt);
886
887 if (rc != 0) {
888 shell_error(sh, "Error mounting as littlefs: %d", rc);
889 k_free((void *)littlefs_mnt.mnt_point);
890 littlefs_mnt.mnt_point = NULL;
891 return -EIO;
892 }
893
894 return rc;
895 }
896 #endif
897
898 SHELL_STATIC_SUBCMD_SET_CREATE(sub_fs_mount,
899 #if defined(CONFIG_FAT_FILESYSTEM_ELM)
900 SHELL_CMD_ARG(fat, NULL,
901 "Mount fatfs. fs mount fat <mount-point>",
902 cmd_mount_fat, 2, 0),
903 #endif
904
905 #if defined(CONFIG_FILE_SYSTEM_LITTLEFS)
906 SHELL_CMD_ARG(littlefs, NULL,
907 "Mount littlefs. fs mount littlefs <mount-point>",
908 cmd_mount_littlefs, 2, 0),
909 #endif
910
911 SHELL_SUBCMD_SET_END
912 );
913 #endif
914
915 SHELL_STATIC_SUBCMD_SET_CREATE(sub_fs,
916 SHELL_CMD(cd, NULL, "Change working directory", cmd_cd),
917 SHELL_CMD(ls, NULL, "List files in current directory", cmd_ls),
918 SHELL_CMD_ARG(mkdir, NULL, "Create directory", cmd_mkdir, 2, 0),
919 #ifdef CONFIG_FILE_SYSTEM_SHELL_MOUNT_COMMAND
920 SHELL_CMD(mount, &sub_fs_mount,
921 "<Mount fs, syntax:- fs mount <fs type> <mount-point>", NULL),
922 #endif
923 SHELL_CMD(pwd, NULL, "Print current working directory", cmd_pwd),
924 SHELL_CMD_ARG(read, NULL, "Read from file", cmd_read, 2, 255),
925 SHELL_CMD_ARG(cat, NULL,
926 "Concatenate files and print on the standard output",
927 cmd_cat, 2, 255),
928 SHELL_CMD_ARG(rm, NULL, "Remove file", cmd_rm, 2, 0),
929 SHELL_CMD_ARG(cp, NULL, "Copy file", cmd_cp, 3, 0),
930 SHELL_CMD_ARG(statvfs, NULL, "Show file system state", cmd_statvfs, 2, 0),
931 SHELL_CMD_ARG(trunc, NULL, "Truncate file", cmd_trunc, 2, 255),
932 SHELL_CMD_ARG(write, NULL, "Write file", cmd_write, 3, 255),
933 #ifdef CONFIG_FILE_SYSTEM_SHELL_TEST_COMMANDS
934 SHELL_CMD_ARG(read_test, NULL, "Read file test", cmd_read_test, 2, 2),
935 SHELL_CMD_ARG(erase_write_test, NULL, "Erase/write file test", cmd_erase_write_test, 3, 3),
936 #endif
937 SHELL_SUBCMD_SET_END
938 );
939
940 SHELL_CMD_REGISTER(fs, &sub_fs, "File system commands", NULL);
941