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