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