1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 * Copyright (c) 2020 Peter Bigot Consulting, LLC
4 * Copyright (c) 2020 Nordic Semiconductor ASA
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <zephyr/types.h>
12 #include <errno.h>
13 #include <init.h>
14 #include <fs/fs.h>
15 #include <fs/fs_sys.h>
16 #include <sys/check.h>
17 #include <sys/stat.h>
18
19
20 #define LOG_LEVEL CONFIG_FS_LOG_LEVEL
21 #include <logging/log.h>
22 LOG_MODULE_REGISTER(fs);
23
24 /* list of mounted file systems */
25 static sys_dlist_t fs_mnt_list;
26
27 /* lock to protect mount list operations */
28 static struct k_mutex mutex;
29
30 /* Maps an identifier used in mount points to the file system
31 * implementation.
32 */
33 struct registry_entry {
34 int type;
35 const struct fs_file_system_t *fstp;
36 };
37 static struct registry_entry registry[CONFIG_FILE_SYSTEM_MAX_TYPES];
38
registry_clear_entry(struct registry_entry * ep)39 static inline void registry_clear_entry(struct registry_entry *ep)
40 {
41 ep->fstp = NULL;
42 }
43
registry_add(int type,const struct fs_file_system_t * fstp)44 static int registry_add(int type,
45 const struct fs_file_system_t *fstp)
46 {
47 int rv = -ENOSPC;
48
49 for (size_t i = 0; i < ARRAY_SIZE(registry); ++i) {
50 struct registry_entry *ep = ®istry[i];
51
52 if (ep->fstp == NULL) {
53 ep->type = type;
54 ep->fstp = fstp;
55 rv = 0;
56 break;
57 }
58 }
59
60 return rv;
61 }
62
registry_find(int type)63 static struct registry_entry *registry_find(int type)
64 {
65 for (size_t i = 0; i < ARRAY_SIZE(registry); ++i) {
66 struct registry_entry *ep = ®istry[i];
67
68 if ((ep->fstp != NULL) && (ep->type == type)) {
69 return ep;
70 }
71 }
72 return NULL;
73 }
74
fs_type_get(int type)75 static const struct fs_file_system_t *fs_type_get(int type)
76 {
77 struct registry_entry *ep = registry_find(type);
78
79 return (ep != NULL) ? ep->fstp : NULL;
80 }
81
fs_get_mnt_point(struct fs_mount_t ** mnt_pntp,const char * name,size_t * match_len)82 static int fs_get_mnt_point(struct fs_mount_t **mnt_pntp,
83 const char *name, size_t *match_len)
84 {
85 struct fs_mount_t *mnt_p = NULL, *itr;
86 size_t longest_match = 0;
87 size_t len, name_len = strlen(name);
88 sys_dnode_t *node;
89
90 k_mutex_lock(&mutex, K_FOREVER);
91 SYS_DLIST_FOR_EACH_NODE(&fs_mnt_list, node) {
92 itr = CONTAINER_OF(node, struct fs_mount_t, node);
93 len = itr->mountp_len;
94
95 /*
96 * Move to next node if mount point length is
97 * shorter than longest_match match or if path
98 * name is shorter than the mount point name.
99 */
100 if ((len < longest_match) || (len > name_len)) {
101 continue;
102 }
103
104 /*
105 * Move to next node if name does not have a directory
106 * separator where mount point name ends.
107 */
108 if ((len > 1) && (name[len] != '/') && (name[len] != '\0')) {
109 continue;
110 }
111
112 /* Check for mount point match */
113 if (strncmp(name, itr->mnt_point, len) == 0) {
114 mnt_p = itr;
115 longest_match = len;
116 }
117 }
118 k_mutex_unlock(&mutex);
119
120 if (mnt_p == NULL) {
121 return -ENOENT;
122 }
123
124 *mnt_pntp = mnt_p;
125 if (match_len)
126 *match_len = mnt_p->mountp_len;
127
128 return 0;
129 }
130
131 /* File operations */
fs_open(struct fs_file_t * zfp,const char * file_name,fs_mode_t flags)132 int fs_open(struct fs_file_t *zfp, const char *file_name, fs_mode_t flags)
133 {
134 struct fs_mount_t *mp;
135 int rc = -EINVAL;
136
137 /* COpy flags to zfp for use with other fs_ API calls */
138 zfp->flags = flags;
139
140 if ((file_name == NULL) ||
141 (strlen(file_name) <= 1) || (file_name[0] != '/')) {
142 LOG_ERR("invalid file name!!");
143 return -EINVAL;
144 }
145
146 if (zfp->mp != NULL) {
147 return -EBUSY;
148 }
149
150 rc = fs_get_mnt_point(&mp, file_name, NULL);
151 if (rc < 0) {
152 LOG_ERR("mount point not found!!");
153 return rc;
154 }
155
156 if (((mp->flags & FS_MOUNT_FLAG_READ_ONLY) != 0) &&
157 (flags & FS_O_CREATE || flags & FS_O_WRITE)) {
158 return -EROFS;
159 }
160
161 CHECKIF(mp->fs->open == NULL) {
162 return -ENOTSUP;
163 }
164
165 zfp->mp = mp;
166 rc = mp->fs->open(zfp, file_name, flags);
167 if (rc < 0) {
168 LOG_ERR("file open error (%d)", rc);
169 zfp->mp = NULL;
170 return rc;
171 }
172
173 return rc;
174 }
175
fs_close(struct fs_file_t * zfp)176 int fs_close(struct fs_file_t *zfp)
177 {
178 int rc = -EINVAL;
179
180 if (zfp->mp == NULL) {
181 return 0;
182 }
183
184 CHECKIF(zfp->mp->fs->close == NULL) {
185 return -ENOTSUP;
186 }
187
188 rc = zfp->mp->fs->close(zfp);
189 if (rc < 0) {
190 LOG_ERR("file close error (%d)", rc);
191 return rc;
192 }
193
194 zfp->mp = NULL;
195
196 return rc;
197 }
198
fs_read(struct fs_file_t * zfp,void * ptr,size_t size)199 ssize_t fs_read(struct fs_file_t *zfp, void *ptr, size_t size)
200 {
201 int rc = -EINVAL;
202
203 if (zfp->mp == NULL) {
204 return -EBADF;
205 }
206
207 CHECKIF(zfp->mp->fs->read == NULL) {
208 return -ENOTSUP;
209 }
210
211 rc = zfp->mp->fs->read(zfp, ptr, size);
212 if (rc < 0) {
213 LOG_ERR("file read error (%d)", rc);
214 }
215
216 return rc;
217 }
218
fs_write(struct fs_file_t * zfp,const void * ptr,size_t size)219 ssize_t fs_write(struct fs_file_t *zfp, const void *ptr, size_t size)
220 {
221 int rc = -EINVAL;
222
223 if (zfp->mp == NULL) {
224 return -EBADF;
225 }
226
227 CHECKIF(zfp->mp->fs->write == NULL) {
228 return -ENOTSUP;
229 }
230
231 rc = zfp->mp->fs->write(zfp, ptr, size);
232 if (rc < 0) {
233 LOG_ERR("file write error (%d)", rc);
234 }
235
236 return rc;
237 }
238
fs_seek(struct fs_file_t * zfp,off_t offset,int whence)239 int fs_seek(struct fs_file_t *zfp, off_t offset, int whence)
240 {
241 int rc = -ENOTSUP;
242
243 if (zfp->mp == NULL) {
244 return -EBADF;
245 }
246
247 CHECKIF(zfp->mp->fs->lseek == NULL) {
248 return -ENOTSUP;
249 }
250
251 rc = zfp->mp->fs->lseek(zfp, offset, whence);
252 if (rc < 0) {
253 LOG_ERR("file seek error (%d)", rc);
254 }
255
256 return rc;
257 }
258
fs_tell(struct fs_file_t * zfp)259 off_t fs_tell(struct fs_file_t *zfp)
260 {
261 int rc = -ENOTSUP;
262
263 if (zfp->mp == NULL) {
264 return -EBADF;
265 }
266
267 CHECKIF(zfp->mp->fs->tell == NULL) {
268 return -ENOTSUP;
269 }
270
271 rc = zfp->mp->fs->tell(zfp);
272 if (rc < 0) {
273 LOG_ERR("file tell error (%d)", rc);
274 }
275
276 return rc;
277 }
278
fs_truncate(struct fs_file_t * zfp,off_t length)279 int fs_truncate(struct fs_file_t *zfp, off_t length)
280 {
281 int rc = -EINVAL;
282
283 if (zfp->mp == NULL) {
284 return -EBADF;
285 }
286
287 CHECKIF(zfp->mp->fs->truncate == NULL) {
288 return -ENOTSUP;
289 }
290
291 rc = zfp->mp->fs->truncate(zfp, length);
292 if (rc < 0) {
293 LOG_ERR("file truncate error (%d)", rc);
294 }
295
296 return rc;
297 }
298
fs_sync(struct fs_file_t * zfp)299 int fs_sync(struct fs_file_t *zfp)
300 {
301 int rc = -EINVAL;
302
303 if (zfp->mp == NULL) {
304 return -EBADF;
305 }
306
307 CHECKIF(zfp->mp->fs->sync == NULL) {
308 return -ENOTSUP;
309 }
310
311 rc = zfp->mp->fs->sync(zfp);
312 if (rc < 0) {
313 LOG_ERR("file sync error (%d)", rc);
314 }
315
316 return rc;
317 }
318
319 /* Directory operations */
fs_opendir(struct fs_dir_t * zdp,const char * abs_path)320 int fs_opendir(struct fs_dir_t *zdp, const char *abs_path)
321 {
322 struct fs_mount_t *mp;
323 int rc = -EINVAL;
324
325 if ((abs_path == NULL) ||
326 (strlen(abs_path) < 1) || (abs_path[0] != '/')) {
327 LOG_ERR("invalid file name!!");
328 return -EINVAL;
329 }
330
331 if (zdp->mp != NULL || zdp->dirp != NULL) {
332 return -EBUSY;
333 }
334
335
336 if (strcmp(abs_path, "/") == 0) {
337 /* Open VFS root dir, marked by zdp->mp == NULL */
338 k_mutex_lock(&mutex, K_FOREVER);
339
340 zdp->mp = NULL;
341 zdp->dirp = sys_dlist_peek_head(&fs_mnt_list);
342
343 k_mutex_unlock(&mutex);
344
345 return 0;
346 }
347
348 rc = fs_get_mnt_point(&mp, abs_path, NULL);
349 if (rc < 0) {
350 LOG_ERR("mount point not found!!");
351 return rc;
352 }
353
354 CHECKIF(mp->fs->opendir == NULL) {
355 return -ENOTSUP;
356 }
357
358 zdp->mp = mp;
359 rc = zdp->mp->fs->opendir(zdp, abs_path);
360 if (rc < 0) {
361 zdp->mp = NULL;
362 zdp->dirp = NULL;
363 LOG_ERR("directory open error (%d)", rc);
364 }
365
366 return rc;
367 }
368
fs_readdir(struct fs_dir_t * zdp,struct fs_dirent * entry)369 int fs_readdir(struct fs_dir_t *zdp, struct fs_dirent *entry)
370 {
371 if (zdp->mp) {
372 /* Delegate to mounted filesystem */
373 int rc = -EINVAL;
374
375 CHECKIF(zdp->mp->fs->readdir == NULL) {
376 return -ENOTSUP;
377 }
378
379 /* Loop until error or not special directory */
380 while (true) {
381 rc = zdp->mp->fs->readdir(zdp, entry);
382 if (rc < 0) {
383 break;
384 }
385 if (entry->name[0] == 0) {
386 break;
387 }
388 if (entry->type != FS_DIR_ENTRY_DIR) {
389 break;
390 }
391 if ((strcmp(entry->name, ".") != 0)
392 && (strcmp(entry->name, "..") != 0)) {
393 break;
394 }
395 }
396 if (rc < 0) {
397 LOG_ERR("directory read error (%d)", rc);
398 }
399
400 return rc;
401 }
402
403 /* VFS root dir */
404 if (zdp->dirp == NULL) {
405 /* No more entries */
406 entry->name[0] = 0;
407 return 0;
408 }
409
410 /* Find the current and next entries in the mount point dlist */
411 sys_dnode_t *node, *next = NULL;
412 bool found = false;
413
414 k_mutex_lock(&mutex, K_FOREVER);
415
416 SYS_DLIST_FOR_EACH_NODE(&fs_mnt_list, node) {
417 if (node == zdp->dirp) {
418 found = true;
419
420 /* Pull info from current entry */
421 struct fs_mount_t *mnt;
422
423 mnt = CONTAINER_OF(node, struct fs_mount_t, node);
424
425 entry->type = FS_DIR_ENTRY_DIR;
426 strncpy(entry->name, mnt->mnt_point + 1,
427 sizeof(entry->name) - 1);
428 entry->name[sizeof(entry->name) - 1] = 0;
429 entry->size = 0;
430
431 /* Save pointer to the next one, for later */
432 next = sys_dlist_peek_next(&fs_mnt_list, node);
433 break;
434 }
435 }
436
437 k_mutex_unlock(&mutex);
438
439 if (!found) {
440 /* Current entry must have been removed before this
441 * call to readdir -- return an error
442 */
443 return -ENOENT;
444 }
445
446 zdp->dirp = next;
447 return 0;
448 }
449
fs_closedir(struct fs_dir_t * zdp)450 int fs_closedir(struct fs_dir_t *zdp)
451 {
452 int rc = -EINVAL;
453
454 if (zdp->mp == NULL) {
455 /* VFS root dir */
456 zdp->dirp = NULL;
457 return 0;
458 }
459
460 CHECKIF(zdp->mp->fs->closedir == NULL) {
461 return -ENOTSUP;
462 }
463
464 rc = zdp->mp->fs->closedir(zdp);
465 if (rc < 0) {
466 LOG_ERR("directory close error (%d)", rc);
467 return rc;
468 }
469
470 zdp->mp = NULL;
471 zdp->dirp = NULL;
472 return rc;
473 }
474
475 /* Filesystem operations */
fs_mkdir(const char * abs_path)476 int fs_mkdir(const char *abs_path)
477 {
478 struct fs_mount_t *mp;
479 int rc = -EINVAL;
480
481 if ((abs_path == NULL) ||
482 (strlen(abs_path) <= 1) || (abs_path[0] != '/')) {
483 LOG_ERR("invalid file name!!");
484 return -EINVAL;
485 }
486
487 rc = fs_get_mnt_point(&mp, abs_path, NULL);
488 if (rc < 0) {
489 LOG_ERR("mount point not found!!");
490 return rc;
491 }
492
493 if (mp->flags & FS_MOUNT_FLAG_READ_ONLY) {
494 return -EROFS;
495 }
496
497 CHECKIF(mp->fs->mkdir == NULL) {
498 return -ENOTSUP;
499 }
500
501 rc = mp->fs->mkdir(mp, abs_path);
502 if (rc < 0) {
503 LOG_ERR("failed to create directory (%d)", rc);
504 }
505
506 return rc;
507 }
508
fs_unlink(const char * abs_path)509 int fs_unlink(const char *abs_path)
510 {
511 struct fs_mount_t *mp;
512 int rc = -EINVAL;
513
514 if ((abs_path == NULL) ||
515 (strlen(abs_path) <= 1) || (abs_path[0] != '/')) {
516 LOG_ERR("invalid file name!!");
517 return -EINVAL;
518 }
519
520 rc = fs_get_mnt_point(&mp, abs_path, NULL);
521 if (rc < 0) {
522 LOG_ERR("mount point not found!!");
523 return rc;
524 }
525
526 if (mp->flags & FS_MOUNT_FLAG_READ_ONLY) {
527 return -EROFS;
528 }
529
530 CHECKIF(mp->fs->unlink == NULL) {
531 return -ENOTSUP;
532 }
533
534 rc = mp->fs->unlink(mp, abs_path);
535 if (rc < 0) {
536 LOG_ERR("failed to unlink path (%d)", rc);
537 }
538
539 return rc;
540 }
541
fs_rename(const char * from,const char * to)542 int fs_rename(const char *from, const char *to)
543 {
544 struct fs_mount_t *mp;
545 size_t match_len;
546 int rc = -EINVAL;
547
548 if ((from == NULL) || (strlen(from) <= 1) || (from[0] != '/') ||
549 (to == NULL) || (strlen(to) <= 1) || (to[0] != '/')) {
550 LOG_ERR("invalid file name!!");
551 return -EINVAL;
552 }
553
554 rc = fs_get_mnt_point(&mp, from, &match_len);
555 if (rc < 0) {
556 LOG_ERR("mount point not found!!");
557 return rc;
558 }
559
560 if (mp->flags & FS_MOUNT_FLAG_READ_ONLY) {
561 return -EROFS;
562 }
563
564 /* Make sure both files are mounted on the same path */
565 if (strncmp(from, to, match_len) != 0) {
566 LOG_ERR("mount point not same!!");
567 return -EINVAL;
568 }
569
570 CHECKIF(mp->fs->rename == NULL) {
571 return -ENOTSUP;
572 }
573
574 rc = mp->fs->rename(mp, from, to);
575 if (rc < 0) {
576 LOG_ERR("failed to rename file or dir (%d)", rc);
577 }
578
579 return rc;
580 }
581
fs_stat(const char * abs_path,struct fs_dirent * entry)582 int fs_stat(const char *abs_path, struct fs_dirent *entry)
583 {
584 struct fs_mount_t *mp;
585 int rc = -EINVAL;
586
587 if ((abs_path == NULL) ||
588 (strlen(abs_path) <= 1) || (abs_path[0] != '/')) {
589 LOG_ERR("invalid file name!!");
590 return -EINVAL;
591 }
592
593 rc = fs_get_mnt_point(&mp, abs_path, NULL);
594 if (rc < 0) {
595 LOG_ERR("mount point not found!!");
596 return rc;
597 }
598
599 CHECKIF(mp->fs->stat == NULL) {
600 return -ENOTSUP;
601 }
602
603 rc = mp->fs->stat(mp, abs_path, entry);
604 if (rc == -ENOENT) {
605 /* File doesn't exist, which is a valid stat response */
606 } else if (rc < 0) {
607 LOG_ERR("failed get file or dir stat (%d)", rc);
608 }
609 return rc;
610 }
611
fs_statvfs(const char * abs_path,struct fs_statvfs * stat)612 int fs_statvfs(const char *abs_path, struct fs_statvfs *stat)
613 {
614 struct fs_mount_t *mp;
615 int rc;
616
617 if ((abs_path == NULL) ||
618 (strlen(abs_path) <= 1) || (abs_path[0] != '/')) {
619 LOG_ERR("invalid file name!!");
620 return -EINVAL;
621 }
622
623 rc = fs_get_mnt_point(&mp, abs_path, NULL);
624 if (rc < 0) {
625 LOG_ERR("mount point not found!!");
626 return rc;
627 }
628
629 if (mp->fs->statvfs != NULL) {
630 rc = mp->fs->statvfs(mp, abs_path, stat);
631 if (rc < 0) {
632 LOG_ERR("failed get file or dir stat (%d)", rc);
633 }
634 }
635
636 return rc;
637 }
638
fs_mount(struct fs_mount_t * mp)639 int fs_mount(struct fs_mount_t *mp)
640 {
641 struct fs_mount_t *itr;
642 const struct fs_file_system_t *fs;
643 sys_dnode_t *node;
644 int rc = -EINVAL;
645 size_t len = 0;
646
647 /* Do all the mp checks prior to locking the mutex on the file
648 * subsystem.
649 */
650 if ((mp == NULL) || (mp->mnt_point == NULL)) {
651 LOG_ERR("mount point not initialized!!");
652 return -EINVAL;
653 }
654
655 len = strlen(mp->mnt_point);
656
657 if ((len <= 1) || (mp->mnt_point[0] != '/')) {
658 LOG_ERR("invalid mount point!!");
659 return -EINVAL;
660 }
661
662 k_mutex_lock(&mutex, K_FOREVER);
663
664 /* Check if mount point already exists */
665 SYS_DLIST_FOR_EACH_NODE(&fs_mnt_list, node) {
666 itr = CONTAINER_OF(node, struct fs_mount_t, node);
667 /* continue if length does not match */
668 if (len != itr->mountp_len) {
669 continue;
670 }
671
672 if (strncmp(mp->mnt_point, itr->mnt_point, len) == 0) {
673 LOG_ERR("mount point already exists!!");
674 rc = -EBUSY;
675 goto mount_err;
676 }
677 }
678
679 /* Get file system information */
680 fs = fs_type_get(mp->type);
681 if (fs == NULL) {
682 LOG_ERR("requested file system type not registered!!");
683 rc = -ENOENT;
684 goto mount_err;
685 }
686
687 CHECKIF(fs->mount == NULL) {
688 LOG_ERR("fs type %d does not support mounting", mp->type);
689 rc = -ENOTSUP;
690 goto mount_err;
691 }
692
693 if (fs->unmount == NULL) {
694 LOG_WRN("mount path %s is not unmountable",
695 log_strdup(mp->mnt_point));
696 }
697
698 rc = fs->mount(mp);
699 if (rc < 0) {
700 LOG_ERR("fs mount error (%d)", rc);
701 goto mount_err;
702 }
703
704 /* Update mount point data and append it to the list */
705 mp->mountp_len = len;
706 mp->fs = fs;
707
708 sys_dlist_append(&fs_mnt_list, &mp->node);
709 LOG_DBG("fs mounted at %s", log_strdup(mp->mnt_point));
710
711 mount_err:
712 k_mutex_unlock(&mutex);
713 return rc;
714 }
715
716
fs_unmount(struct fs_mount_t * mp)717 int fs_unmount(struct fs_mount_t *mp)
718 {
719 int rc = -EINVAL;
720
721 if (mp == NULL) {
722 return rc;
723 }
724
725 k_mutex_lock(&mutex, K_FOREVER);
726
727 if (mp->fs == NULL) {
728 LOG_ERR("fs not mounted (mp == %p)", mp);
729 goto unmount_err;
730 }
731
732 CHECKIF(mp->fs->unmount == NULL) {
733 LOG_ERR("fs unmount not supported!!");
734 rc = -ENOTSUP;
735 goto unmount_err;
736 }
737
738 rc = mp->fs->unmount(mp);
739 if (rc < 0) {
740 LOG_ERR("fs unmount error (%d)", rc);
741 goto unmount_err;
742 }
743
744 /* clear file system interface */
745 mp->fs = NULL;
746
747 /* remove mount node from the list */
748 sys_dlist_remove(&mp->node);
749 LOG_DBG("fs unmounted from %s", log_strdup(mp->mnt_point));
750
751 unmount_err:
752 k_mutex_unlock(&mutex);
753 return rc;
754 }
755
fs_readmount(int * index,const char ** name)756 int fs_readmount(int *index, const char **name)
757 {
758 sys_dnode_t *node;
759 int rc = -ENOENT;
760 int cnt = 0;
761 struct fs_mount_t *itr = NULL;
762
763 *name = NULL;
764
765 k_mutex_lock(&mutex, K_FOREVER);
766
767 SYS_DLIST_FOR_EACH_NODE(&fs_mnt_list, node) {
768 if (*index == cnt) {
769 itr = CONTAINER_OF(node, struct fs_mount_t, node);
770 break;
771 }
772
773 ++cnt;
774 }
775
776 k_mutex_unlock(&mutex);
777
778 if (itr != NULL) {
779 rc = 0;
780 *name = itr->mnt_point;
781 ++(*index);
782 }
783
784 return rc;
785
786 }
787
788 /* Register File system */
fs_register(int type,const struct fs_file_system_t * fs)789 int fs_register(int type, const struct fs_file_system_t *fs)
790 {
791 int rc = 0;
792
793 k_mutex_lock(&mutex, K_FOREVER);
794
795 if (fs_type_get(type) != NULL) {
796 rc = -EALREADY;
797 } else {
798 rc = registry_add(type, fs);
799 }
800
801 k_mutex_unlock(&mutex);
802
803 LOG_DBG("fs register %d: %d", type, rc);
804
805 return rc;
806 }
807
808 /* Unregister File system */
fs_unregister(int type,const struct fs_file_system_t * fs)809 int fs_unregister(int type, const struct fs_file_system_t *fs)
810 {
811 int rc = 0;
812 struct registry_entry *ep;
813
814 k_mutex_lock(&mutex, K_FOREVER);
815
816 ep = registry_find(type);
817 if ((ep == NULL) || (ep->fstp != fs)) {
818 rc = -EINVAL;
819 } else {
820 registry_clear_entry(ep);
821 }
822
823 k_mutex_unlock(&mutex);
824
825 LOG_DBG("fs unregister %d: %d", type, rc);
826 return rc;
827 }
828
fs_init(const struct device * dev)829 static int fs_init(const struct device *dev)
830 {
831 k_mutex_init(&mutex);
832 sys_dlist_init(&fs_mnt_list);
833 return 0;
834 }
835
836 SYS_INIT(fs_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
837