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