1 /*
2 * Copyright (c) 2006-2025 RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-05-05 Bernard Implement mnt in dfs v2.0
9 */
10
11 #include <rtthread.h>
12
13 #include "dfs_private.h"
14
15 #include <dfs.h>
16 #include <dfs_dentry.h>
17 #include <dfs_mnt.h>
18 #include <dfs_pcache.h>
19
20 #define DBG_TAG "DFS.mnt"
21 #define DBG_LVL DBG_WARNING
22 #include <rtdbg.h>
23
24 static struct dfs_mnt *_root_mnt = RT_NULL;
25
26 RT_OBJECT_HOOKLIST_DEFINE(dfs_mnt_umnt);
27
28 /*
29 * mnt tree structure
30 *
31 * mnt_root <----------------------------------------+
32 * | (child) +----------+ |
33 * v (sibling) v | |
34 * mnt_child0 -> mnt_child1 | |
35 * | (child) | |
36 * v / (parent) | (root)
37 * mnt_child10 ---/
38 *
39 */
40
41 /**
42 * @brief Create a new dfs_mnt structure instance.
43 *
44 * This function allocates memory to create a new dfs_mnt structure instance and initializes it.
45 * If the memory allocation is successful, it copies the input path string into the instance and initializes related lists and flags.
46 *
47 * @param[in] path The path string to be mounted. This path information will be copied to the newly created dfs_mnt instance.
48 *
49 * @return If the memory allocation is successful, returns a pointer to the newly created dfs_mnt structure;
50 * if the memory allocation fails, returns RT_NULL.
51 */
dfs_mnt_create(const char * path)52 struct dfs_mnt *dfs_mnt_create(const char *path)
53 {
54 struct dfs_mnt *mnt = rt_calloc(1, sizeof(struct dfs_mnt));
55 if (mnt)
56 {
57 LOG_I("create mnt at %s", path);
58
59 mnt->fullpath = rt_strdup(path);
60 rt_list_init(&mnt->sibling);
61 rt_list_init(&mnt->child);
62 mnt->flags |= MNT_IS_ALLOCED;
63 rt_atomic_store(&(mnt->ref_count), 1);
64 }
65 else
66 {
67 rt_set_errno(-ENOMEM);
68 }
69
70 return mnt;
71 }
72
73 /**
74 * @brief Insert a child mount point into the mount tree.
75 *
76 * This function inserts a child mount point into the specified parent mount point's child list.
77 * If the parent mount point is not provided, it will try to find the appropriate mount point based on the child's path.
78 * If the child mount point is the root, it will update the global root mount point accordingly.
79 *
80 * @param[in,out] mnt Pointer to the parent dfs_mnt structure. If NULL, it will be updated to the appropriate mount point.
81 * @param[in] child Pointer to the child dfs_mnt structure to be inserted.
82 *
83 * @return Always returns 0 to indicate success.
84 */
dfs_mnt_insert(struct dfs_mnt * mnt,struct dfs_mnt * child)85 int dfs_mnt_insert(struct dfs_mnt* mnt, struct dfs_mnt* child)
86 {
87 if (child)
88 {
89 if (mnt == RT_NULL)
90 {
91 /* insert into root */
92 mnt = dfs_mnt_lookup(child->fullpath);
93 if (mnt == RT_NULL || (strcmp(child->fullpath, "/") == 0))
94 {
95 /* it's root mnt */
96 mnt = child;
97 mnt->flags |= MNT_IS_LOCKED;
98
99 /* ref to gobal root */
100 if (_root_mnt)
101 {
102 child = _root_mnt;
103 rt_atomic_sub(&(_root_mnt->parent->ref_count), 1);
104 rt_atomic_sub(&(_root_mnt->ref_count), 1);
105 _root_mnt->flags &= ~MNT_IS_LOCKED;
106
107 _root_mnt = dfs_mnt_ref(mnt);
108 mnt->parent = dfs_mnt_ref(mnt);
109 mnt->flags |= MNT_IS_ADDLIST;
110
111 mkdir("/dev", 0777);
112 }
113 else
114 {
115 _root_mnt = dfs_mnt_ref(mnt);
116 }
117 }
118 }
119
120 if (mnt)
121 {
122 child->flags |= MNT_IS_ADDLIST;
123 if (child != mnt)
124 {
125 /* not the root, insert into the child list */
126 rt_list_insert_before(&mnt->child, &child->sibling);
127 /* child ref self */
128 dfs_mnt_ref(child);
129 }
130 /* parent ref parent */
131 child->parent = dfs_mnt_ref(mnt);
132 }
133 }
134
135 return 0;
136 }
137
138 /**
139 * @brief Remove a mount point from the mount tree.
140 *
141 * This function attempts to remove a specified mount point from the mount tree.
142 * It can only remove a mount point if it has no child mount points. If the mount point
143 * has children, it logs a warning message instead of performing the removal.
144 *
145 * @param[in] mnt Pointer to the dfs_mnt structure representing the mount point to be removed.
146 *
147 * @return Returns RT_EOK if the mount point is successfully removed.
148 * Returns -RT_ERROR if the mount point has child mount points and cannot be removed.
149 */
dfs_mnt_remove(struct dfs_mnt * mnt)150 int dfs_mnt_remove(struct dfs_mnt* mnt)
151 {
152 int ret = -RT_ERROR;
153
154 if (rt_list_isempty(&mnt->child))
155 {
156 rt_list_remove(&mnt->sibling);
157 if (mnt->parent)
158 {
159 /* parent unref parent */
160 rt_atomic_sub(&(mnt->parent->ref_count), 1);
161 }
162
163 ret = RT_EOK;
164 }
165 else
166 {
167 LOG_W("remove a mnt point:%s with child.", mnt->fullpath);
168 }
169
170 return ret;
171 }
172
173 /**
174 * @brief Recursively search for a mount point associated with a specific device ID in the mount tree.
175 *
176 * This function traverses the mount tree starting from the given mount point `mnt` to find
177 * a mount point that is associated with the specified device ID `dev_id`. It uses a depth-first
178 * search algorithm to iterate through the child mount points.
179 *
180 * @param[in] mnt Pointer to the root dfs_mnt structure from which the search will start.
181 * @param[in] dev_id Pointer to the device ID to search for.
182 *
183 * @return If a mount point associated with the given device ID is found, returns a pointer to the corresponding dfs_mnt structure.
184 * Otherwise, returns RT_NULL.
185 */
_dfs_mnt_dev_lookup(struct dfs_mnt * mnt,rt_device_t dev_id)186 static struct dfs_mnt *_dfs_mnt_dev_lookup(struct dfs_mnt *mnt, rt_device_t dev_id)
187 {
188 struct dfs_mnt *ret = RT_NULL, *iter = RT_NULL;
189
190 rt_list_for_each_entry(iter, &mnt->child, sibling)
191 {
192 if (iter->dev_id == dev_id)
193 {
194 ret = iter;
195 break;
196 }
197 else
198 {
199 ret = _dfs_mnt_dev_lookup(iter, dev_id);
200 if (ret)
201 {
202 break;
203 }
204 }
205 }
206
207 return ret;
208 }
209
210 /**
211 * @brief Search for a mount point associated with a specific device ID in the mount tree.
212 *
213 * This function initiates a search for a mount point that is associated with the specified
214 * device ID `dev_id` starting from the root mount point. It first checks the root mount point
215 * directly, and if not found, it recursively searches the entire mount tree using the
216 * internal helper function `_dfs_mnt_dev_lookup`.
217 *
218 * @param[in] dev_id Pointer to the device ID to search for.
219 *
220 * @return If a mount point associated with the given device ID is found, returns a pointer to the corresponding dfs_mnt structure.
221 * Otherwise, returns RT_NULL.
222 */
dfs_mnt_dev_lookup(rt_device_t dev_id)223 struct dfs_mnt *dfs_mnt_dev_lookup(rt_device_t dev_id)
224 {
225 struct dfs_mnt *mnt = _root_mnt;
226 struct dfs_mnt *ret = RT_NULL;
227
228 if (mnt)
229 {
230 dfs_lock();
231
232 if (mnt->dev_id == dev_id)
233 {
234 dfs_unlock();
235 return mnt;
236 }
237
238 ret = _dfs_mnt_dev_lookup(mnt, dev_id);
239
240 dfs_unlock();
241 }
242
243 return ret;
244 }
245
246 /**
247 * @brief Look up the mount point associated with a given full path.
248 *
249 * This function searches the mount tree starting from the root mount point to find
250 * the most specific mount point that matches the given full path. It traverses down
251 * the mount tree to identify the deepest mount point that is a prefix of the given path.
252 *
253 * @param[in] fullpath The full path string for which to find the associated mount point.
254 *
255 * @return If a matching mount point is found, returns a pointer to the corresponding dfs_mnt structure.
256 * Otherwise, returns RT_NULL.
257 */
dfs_mnt_lookup(const char * fullpath)258 struct dfs_mnt *dfs_mnt_lookup(const char *fullpath)
259 {
260 struct dfs_mnt *mnt = _root_mnt;
261 struct dfs_mnt *iter = RT_NULL;
262
263 if (mnt)
264 {
265 int mnt_len = rt_strlen(mnt->fullpath);
266
267 dfs_lock();
268 if ((strncmp(mnt->fullpath, fullpath, mnt_len) == 0) &&
269 (mnt_len == 1 || (fullpath[mnt_len] == '\0') || (fullpath[mnt_len] == '/')))
270 {
271 while (!rt_list_isempty(&mnt->child))
272 {
273 rt_list_for_each_entry(iter, &mnt->child, sibling)
274 {
275 mnt_len = rt_strlen(iter->fullpath);
276 if ((strncmp(iter->fullpath, fullpath, mnt_len) == 0) &&
277 ((fullpath[mnt_len] == '\0') || (fullpath[mnt_len] == '/')))
278 {
279 mnt = iter;
280 break;
281 }
282 }
283
284 if (mnt != iter) break;
285 }
286 }
287 else
288 {
289 mnt = RT_NULL;
290 }
291 dfs_unlock();
292
293 if (mnt)
294 {
295 LOG_D("mnt_lookup: %s path @ mount point %p", fullpath, mnt);
296 DLOG(note, "mnt", "found mnt(%s)", mnt->fs_ops->name);
297 }
298 }
299
300 return mnt;
301 }
302
303 /**
304 * @brief Increase the reference count of a dfs_mnt structure instance.
305 *
306 * This function increments the reference count of the specified dfs_mnt structure.
307 * The reference count is used to track how many parts of the system are currently
308 * using this mount point.
309 *
310 * @param[in,out] mnt Pointer to the dfs_mnt structure whose reference count is to be increased.
311 * If the pointer is valid, the reference count within the structure will be modified.
312 * @return Returns the same pointer to the dfs_mnt structure that was passed in.
313 * If the input pointer is NULL, it simply returns NULL.
314 */
dfs_mnt_ref(struct dfs_mnt * mnt)315 struct dfs_mnt* dfs_mnt_ref(struct dfs_mnt* mnt)
316 {
317 if (mnt)
318 {
319 rt_atomic_add(&(mnt->ref_count), 1);
320 DLOG(note, "mnt", "mnt(%s),ref_count=%d", mnt->fs_ops->name, rt_atomic_load(&(mnt->ref_count)));
321 }
322
323 return mnt;
324 }
325
326 /**
327 * @brief Decrease the reference count of a dfs_mnt structure instance and free it if necessary.
328 *
329 * This function decrements the reference count of the specified dfs_mnt structure.
330 * If the reference count reaches zero after the decrement, it will perform the unmount operation,
331 * trigger the unmount hook, free the allocated path memory, and finally free the dfs_mnt structure itself.
332 *
333 * @param[in,out] mnt Pointer to the dfs_mnt structure whose reference count is to be decreased.
334 * If the reference count reaches zero, the structure will be freed.
335 *
336 * @return returns RT_EOK to indicate success.
337 */
dfs_mnt_unref(struct dfs_mnt * mnt)338 int dfs_mnt_unref(struct dfs_mnt *mnt)
339 {
340 rt_err_t ret = RT_EOK;
341 rt_base_t ref_count;
342
343 if (mnt)
344 {
345 ref_count = rt_atomic_sub(&(mnt->ref_count), 1) - 1;
346
347 if (ref_count == 0)
348 {
349 dfs_lock();
350
351 if (mnt->flags & MNT_IS_UMOUNT)
352 {
353 mnt->fs_ops->umount(mnt);
354
355 RT_OBJECT_HOOKLIST_CALL(dfs_mnt_umnt, (mnt));
356 }
357
358 /* free full path */
359 rt_free(mnt->fullpath);
360 mnt->fullpath = RT_NULL;
361
362 /* destroy self and the ref_count should be 0 */
363 DLOG(msg, "mnt", "mnt", DLOG_MSG, "free mnt(%s)", mnt->fs_ops->name);
364 rt_free(mnt);
365
366 dfs_unlock();
367 }
368 else
369 {
370 DLOG(note, "mnt", "mnt(%s),ref_count=%d", mnt->fs_ops->name, rt_atomic_load(&(mnt->ref_count)));
371 }
372 }
373
374 return ret;
375 }
376
377 /**
378 * @brief Set specific flags for a dfs_mnt structure instance.
379 *
380 * This function sets specific flags for the given dfs_mnt structure.
381 * If the MS_RDONLY flag is included in the input flags, it sets the MNT_RDONLY flag
382 * for the mount point and cleans the page cache if the page cache feature is enabled.
383 *
384 * @param[in,out] mnt Pointer to the dfs_mnt structure for which flags are to be set.
385 * The structure's `flags` member will be modified if necessary.
386 * @param[in] flags The flags to be set for the mount point. This includes the MS_RDONLY flag.
387 *
388 * @return returns 0 to indicate success.
389 */
dfs_mnt_setflags(struct dfs_mnt * mnt,int flags)390 int dfs_mnt_setflags(struct dfs_mnt *mnt, int flags)
391 {
392 int error = 0;
393
394 if (flags & MS_RDONLY)
395 {
396 mnt->flags |= MNT_RDONLY;
397 #ifdef RT_USING_PAGECACHE
398 dfs_pcache_clean(mnt);
399 #endif
400 }
401
402 return error;
403 }
404
405 /**
406 * @brief Destroy a dfs_mnt structure instance and unmount it if necessary.
407 *
408 * This function attempts to destroy the specified dfs_mnt structure instance.
409 * If the mount point is currently mounted, it marks the mount point as unmounted,
410 * sets the unmount flag, and removes it from the mount list if it was added.
411 * Finally, it decreases the reference count of the mount point and frees the
412 * structure if the reference count reaches zero.
413 *
414 * @param[in,out] mnt Pointer to the dfs_mnt structure to be destroyed.
415 *
416 * @return Returns RT_EOK to indicate success.
417 */
dfs_mnt_destroy(struct dfs_mnt * mnt)418 int dfs_mnt_destroy(struct dfs_mnt* mnt)
419 {
420 rt_err_t ret = RT_EOK;
421
422 if (mnt)
423 {
424 if (mnt->flags & MNT_IS_MOUNTED)
425 {
426 mnt->flags &= ~MNT_IS_MOUNTED;
427 mnt->flags |= MNT_IS_UMOUNT;
428 /* remote it from mnt list */
429 if (mnt->flags & MNT_IS_ADDLIST)
430 {
431 dfs_mnt_remove(mnt);
432 }
433 }
434
435 dfs_mnt_unref(mnt);
436 }
437
438 return ret;
439 }
440
441 /**
442 * @brief Recursively traverse the mount point tree and apply a callback function.
443 *
444 * This function performs a depth-first traversal of the mount point tree starting from the given mount point.
445 * It applies the specified callback function to each mount point in the tree. If the callback function returns
446 * a non-NULL pointer, the traversal stops and the result is returned immediately.
447 *
448 * @param[in] mnt Pointer to the root dfs_mnt structure from which the traversal will start.
449 * If NULL, the function will return RT_NULL without performing any traversal.
450 * @param[in] func Pointer to the callback function to be applied to each mount point.
451 * The callback function takes a pointer to a dfs_mnt structure and a generic parameter,
452 * and returns a pointer to a dfs_mnt structure or RT_NULL.
453 * @param[in] parameter Generic pointer to a parameter that will be passed to the callback function.
454 *
455 * @return If the callback function returns a non-NULL pointer during the traversal, returns that pointer.
456 * Otherwise, returns RT_NULL.
457 */
_dfs_mnt_foreach(struct dfs_mnt * mnt,struct dfs_mnt * (* func)(struct dfs_mnt * mnt,void * parameter),void * parameter)458 static struct dfs_mnt* _dfs_mnt_foreach(struct dfs_mnt *mnt, struct dfs_mnt* (*func)(struct dfs_mnt *mnt, void *parameter), void *parameter)
459 {
460 struct dfs_mnt *iter, *ret = NULL;
461
462 if (mnt)
463 {
464 ret = func(mnt, parameter);
465 if (ret == RT_NULL)
466 {
467 if (!rt_list_isempty(&mnt->child))
468 {
469 /* for each in mount point list */
470 rt_list_for_each_entry(iter, &mnt->child, sibling)
471 {
472 ret = _dfs_mnt_foreach(iter, func, parameter);
473 if (ret != RT_NULL)
474 {
475 break;
476 }
477 }
478 }
479 }
480 }
481 else
482 {
483 ret = RT_NULL;
484 }
485
486 return ret;
487 }
488
489 /**
490 * @brief Compare a mount point's device ID with a given device object.
491 *
492 * This function checks if the device ID associated with a specified mount point
493 * matches the given device object. If a match is found, it returns a pointer to
494 * the corresponding dfs_mnt structure; otherwise, it returns RT_NULL.
495 *
496 * @param[in] mnt Pointer to the dfs_mnt structure representing the mount point to be checked.
497 * @param[in] device Pointer to the device object to compare against the mount point's device ID.
498 *
499 * @return If the device ID of the mount point matches the given device object, returns a pointer to the dfs_mnt structure.
500 * Otherwise, returns RT_NULL.
501 */
_mnt_cmp_devid(struct dfs_mnt * mnt,void * device)502 static struct dfs_mnt* _mnt_cmp_devid(struct dfs_mnt *mnt, void *device)
503 {
504 struct dfs_mnt *ret = RT_NULL;
505 struct rt_device *dev = (struct rt_device*)device;
506
507 if (dev && mnt)
508 {
509 if (mnt->dev_id == dev)
510 {
511 ret = mnt;
512 }
513 }
514
515 return ret;
516 }
517
518 /**
519 * this function will return the mounted path for specified device.
520 *
521 * @param[in] device the device object which is mounted.
522 *
523 * @return the mounted path or NULL if none device mounted.
524 */
dfs_mnt_get_mounted_path(struct rt_device * device)525 const char *dfs_mnt_get_mounted_path(struct rt_device *device)
526 {
527 const char* path = RT_NULL;
528
529 if (_root_mnt)
530 {
531 struct dfs_mnt* mnt;
532
533 dfs_lock();
534 mnt = _dfs_mnt_foreach(_root_mnt, _mnt_cmp_devid, device);
535 dfs_unlock();
536
537 if (mnt) path = mnt->fullpath;
538 }
539
540 return path;
541 }
542
543 /**
544 * @brief Print information about a mount point to the console.
545 *
546 * This function is designed to be used as a callback in the mount point tree traversal.
547 * It prints the file system name, device name (or `(NULL)` if no device is associated),
548 * mount path, and reference count of the specified mount point to the console using `rt_kprintf`.
549 *
550 * @param[in] mnt Pointer to the dfs_mnt structure representing the mount point to be printed.
551 * If NULL, the function does nothing.
552 * @param[in] parameter A generic pointer to a parameter. This parameter is not used in this function.
553 *
554 * @return Always returns RT_NULL as it is a callback function mainly used for side - effects (printing).
555 */
_mnt_dump(struct dfs_mnt * mnt,void * parameter)556 static struct dfs_mnt* _mnt_dump(struct dfs_mnt *mnt, void *parameter)
557 {
558 if (mnt)
559 {
560 if (mnt->dev_id)
561 {
562 rt_kprintf("%-10s %-6s %-10s %d\n",
563 mnt->fs_ops->name, mnt->dev_id->parent.name, mnt->fullpath, rt_atomic_load(&(mnt->ref_count)));
564 }
565 else
566 {
567 rt_kprintf("%-10s (NULL) %-10s %d\n",
568 mnt->fs_ops->name, mnt->fullpath, rt_atomic_load(&(mnt->ref_count)));
569 }
570 }
571
572 return RT_NULL;
573 }
574
575 /**
576 * @brief Compare a mount point's full path with a given path.
577 *
578 * This function is designed to be used as a callback in the mount point tree traversal.
579 * It compares the full path of the specified mount point with the given path.
580 * If the mount point's full path starts with the given path, it returns a pointer to the dfs_mnt structure;
581 * otherwise, it returns RT_NULL.
582 *
583 * @param[in] mnt Pointer to the dfs_mnt structure representing the mount point to be checked.
584 * If NULL, the function will not perform the comparison and return RT_NULL.
585 * @param[in] parameter A generic pointer to a parameter, which should be cast to a `const char*`
586 * representing the path to compare against the mount point's full path.
587 *
588 * @return If the mount point's full path starts with the given path, returns a pointer to the dfs_mnt structure.
589 * Otherwise, returns RT_NULL.
590 */
_mnt_cmp_path(struct dfs_mnt * mnt,void * parameter)591 static struct dfs_mnt* _mnt_cmp_path(struct dfs_mnt* mnt, void *parameter)
592 {
593 const char* fullpath = (const char*)parameter;
594 struct dfs_mnt *ret = RT_NULL;
595
596 if (strncmp(mnt->fullpath, fullpath, rt_strlen(fullpath)) == 0)
597 {
598 ret = mnt;
599 }
600
601 return ret;
602 }
603
604 /**
605 * @brief Check if a mount point has a child mount point matching the given path.
606 *
607 * This function checks whether the specified mount point has a child mount point
608 * whose full path starts with the given path. It uses a depth-first traversal of
609 * the mount point tree starting from the provided mount point and applies the
610 * `_mnt_cmp_path` callback function to each mount point.
611 *
612 * @param[in] mnt Pointer to the root dfs_mnt structure from which the search will start.
613 * If NULL, the function will return RT_FALSE without performing any search.
614 * @param[in] fullpath The full path string to compare against the child mount points' paths.
615 * If NULL, the function will return RT_FALSE without performing any search.
616 *
617 * @return Returns RT_TRUE if a child mount point with a matching path is found.
618 * Returns RT_FALSE if no matching child mount point is found, or if either input parameter is NULL.
619 */
dfs_mnt_has_child_mnt(struct dfs_mnt * mnt,const char * fullpath)620 rt_bool_t dfs_mnt_has_child_mnt(struct dfs_mnt *mnt, const char* fullpath)
621 {
622 int ret = RT_FALSE;
623
624 if (mnt && fullpath)
625 {
626 struct dfs_mnt *m = RT_NULL;
627
628 dfs_lock();
629 m = _dfs_mnt_foreach(mnt, _mnt_cmp_path, (void*)fullpath);
630 dfs_unlock();
631
632 if (m)
633 {
634 ret = RT_TRUE;
635 }
636 }
637
638 return ret;
639 }
640
641 /**
642 * @brief List all mount points starting from a specified mount point.
643 *
644 * This function lists information about all mount points in the mount point tree,
645 * starting from the specified mount point. If the input mount point is NULL,
646 * it starts from the root mount point. It uses the `_dfs_mnt_foreach` function
647 * with the `_mnt_dump` callback to print mount point information.
648 *
649 * @param[in] mnt Pointer to the dfs_mnt structure from which to start listing mount points.
650 * If NULL, the function will start from the root mount point.
651 *
652 * @return Always returns 0 to indicate success.
653 */
dfs_mnt_list(struct dfs_mnt * mnt)654 int dfs_mnt_list(struct dfs_mnt *mnt)
655 {
656 if (!mnt) mnt = _root_mnt;
657
658 /* lock file system */
659 dfs_lock();
660 _dfs_mnt_foreach(mnt, _mnt_dump, RT_NULL);
661 /* unlock file system */
662 dfs_unlock();
663
664 return 0;
665 }
666
667 /**
668 * @brief Traverse all mount points in the mount tree and apply a callback function.
669 *
670 * @param[in] func Pointer to the callback function to be applied to each mount point.
671 * The callback function takes a pointer to a `dfs_mnt` structure and a generic parameter,
672 * and returns a pointer to a `dfs_mnt` structure or `RT_NULL`.
673 * @param[in] parameter Generic pointer to a parameter that will be passed to the callback function.
674 *
675 * @return Always returns 0.
676 */
dfs_mnt_foreach(struct dfs_mnt * (* func)(struct dfs_mnt * mnt,void * parameter),void * parameter)677 int dfs_mnt_foreach(struct dfs_mnt* (*func)(struct dfs_mnt *mnt, void *parameter), void *parameter)
678 {
679 /* lock file system */
680 dfs_lock();
681 _dfs_mnt_foreach(_root_mnt, func, parameter);
682 /* unlock file system */
683 dfs_unlock();
684
685 return 0;
686 }