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