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 }