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