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