| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686 |
- /*
- * Copyright (c) 2006-2025 RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2023-05-05 Bernard Implement mnt in dfs v2.0
- */
- #include <rtthread.h>
- #include "dfs_private.h"
- #include <dfs.h>
- #include <dfs_dentry.h>
- #include <dfs_mnt.h>
- #include <dfs_pcache.h>
- #define DBG_TAG "DFS.mnt"
- #define DBG_LVL DBG_WARNING
- #include <rtdbg.h>
- static struct dfs_mnt *_root_mnt = RT_NULL;
- RT_OBJECT_HOOKLIST_DEFINE(dfs_mnt_umnt);
- /*
- * mnt tree structure
- *
- * mnt_root <----------------------------------------+
- * | (child) +----------+ |
- * v (sibling) v | |
- * mnt_child0 -> mnt_child1 | |
- * | (child) | |
- * v / (parent) | (root)
- * mnt_child10 ---/
- *
- */
- /**
- * @brief Create a new dfs_mnt structure instance.
- *
- * This function allocates memory to create a new dfs_mnt structure instance and initializes it.
- * If the memory allocation is successful, it copies the input path string into the instance and initializes related lists and flags.
- *
- * @param[in] path The path string to be mounted. This path information will be copied to the newly created dfs_mnt instance.
- *
- * @return If the memory allocation is successful, returns a pointer to the newly created dfs_mnt structure;
- * if the memory allocation fails, returns RT_NULL.
- */
- struct dfs_mnt *dfs_mnt_create(const char *path)
- {
- struct dfs_mnt *mnt = rt_calloc(1, sizeof(struct dfs_mnt));
- if (mnt)
- {
- LOG_I("create mnt at %s", path);
- mnt->fullpath = rt_strdup(path);
- rt_list_init(&mnt->sibling);
- rt_list_init(&mnt->child);
- mnt->flags |= MNT_IS_ALLOCED;
- rt_atomic_store(&(mnt->ref_count), 1);
- }
- else
- {
- rt_set_errno(-ENOMEM);
- }
- return mnt;
- }
- /**
- * @brief Insert a child mount point into the mount tree.
- *
- * This function inserts a child mount point into the specified parent mount point's child list.
- * If the parent mount point is not provided, it will try to find the appropriate mount point based on the child's path.
- * If the child mount point is the root, it will update the global root mount point accordingly.
- *
- * @param[in,out] mnt Pointer to the parent dfs_mnt structure. If NULL, it will be updated to the appropriate mount point.
- * @param[in] child Pointer to the child dfs_mnt structure to be inserted.
- *
- * @return Always returns 0 to indicate success.
- */
- int dfs_mnt_insert(struct dfs_mnt* mnt, struct dfs_mnt* child)
- {
- if (child)
- {
- if (mnt == RT_NULL)
- {
- /* insert into root */
- mnt = dfs_mnt_lookup(child->fullpath);
- if (mnt == RT_NULL || (strcmp(child->fullpath, "/") == 0))
- {
- /* it's root mnt */
- mnt = child;
- mnt->flags |= MNT_IS_LOCKED;
- /* ref to gobal root */
- if (_root_mnt)
- {
- child = _root_mnt;
- rt_atomic_sub(&(_root_mnt->parent->ref_count), 1);
- rt_atomic_sub(&(_root_mnt->ref_count), 1);
- _root_mnt->flags &= ~MNT_IS_LOCKED;
- _root_mnt = dfs_mnt_ref(mnt);
- mnt->parent = dfs_mnt_ref(mnt);
- mnt->flags |= MNT_IS_ADDLIST;
- mkdir("/dev", 0777);
- }
- else
- {
- _root_mnt = dfs_mnt_ref(mnt);
- }
- }
- }
- if (mnt)
- {
- child->flags |= MNT_IS_ADDLIST;
- if (child != mnt)
- {
- /* not the root, insert into the child list */
- rt_list_insert_before(&mnt->child, &child->sibling);
- /* child ref self */
- dfs_mnt_ref(child);
- }
- /* parent ref parent */
- child->parent = dfs_mnt_ref(mnt);
- }
- }
- return 0;
- }
- /**
- * @brief Remove a mount point from the mount tree.
- *
- * This function attempts to remove a specified mount point from the mount tree.
- * It can only remove a mount point if it has no child mount points. If the mount point
- * has children, it logs a warning message instead of performing the removal.
- *
- * @param[in] mnt Pointer to the dfs_mnt structure representing the mount point to be removed.
- *
- * @return Returns RT_EOK if the mount point is successfully removed.
- * Returns -RT_ERROR if the mount point has child mount points and cannot be removed.
- */
- int dfs_mnt_remove(struct dfs_mnt* mnt)
- {
- int ret = -RT_ERROR;
- if (rt_list_isempty(&mnt->child))
- {
- rt_list_remove(&mnt->sibling);
- if (mnt->parent)
- {
- /* parent unref parent */
- rt_atomic_sub(&(mnt->parent->ref_count), 1);
- }
- ret = RT_EOK;
- }
- else
- {
- LOG_W("remove a mnt point:%s with child.", mnt->fullpath);
- }
- return ret;
- }
- /**
- * @brief Recursively search for a mount point associated with a specific device ID in the mount tree.
- *
- * This function traverses the mount tree starting from the given mount point `mnt` to find
- * a mount point that is associated with the specified device ID `dev_id`. It uses a depth-first
- * search algorithm to iterate through the child mount points.
- *
- * @param[in] mnt Pointer to the root dfs_mnt structure from which the search will start.
- * @param[in] dev_id Pointer to the device ID to search for.
- *
- * @return If a mount point associated with the given device ID is found, returns a pointer to the corresponding dfs_mnt structure.
- * Otherwise, returns RT_NULL.
- */
- static struct dfs_mnt *_dfs_mnt_dev_lookup(struct dfs_mnt *mnt, rt_device_t dev_id)
- {
- struct dfs_mnt *ret = RT_NULL, *iter = RT_NULL;
- rt_list_for_each_entry(iter, &mnt->child, sibling)
- {
- if (iter->dev_id == dev_id)
- {
- ret = iter;
- break;
- }
- else
- {
- ret = _dfs_mnt_dev_lookup(iter, dev_id);
- if (ret)
- {
- break;
- }
- }
- }
- return ret;
- }
- /**
- * @brief Search for a mount point associated with a specific device ID in the mount tree.
- *
- * This function initiates a search for a mount point that is associated with the specified
- * device ID `dev_id` starting from the root mount point. It first checks the root mount point
- * directly, and if not found, it recursively searches the entire mount tree using the
- * internal helper function `_dfs_mnt_dev_lookup`.
- *
- * @param[in] dev_id Pointer to the device ID to search for.
- *
- * @return If a mount point associated with the given device ID is found, returns a pointer to the corresponding dfs_mnt structure.
- * Otherwise, returns RT_NULL.
- */
- struct dfs_mnt *dfs_mnt_dev_lookup(rt_device_t dev_id)
- {
- struct dfs_mnt *mnt = _root_mnt;
- struct dfs_mnt *ret = RT_NULL;
- if (mnt)
- {
- dfs_lock();
- if (mnt->dev_id == dev_id)
- {
- dfs_unlock();
- return mnt;
- }
- ret = _dfs_mnt_dev_lookup(mnt, dev_id);
- dfs_unlock();
- }
- return ret;
- }
- /**
- * @brief Look up the mount point associated with a given full path.
- *
- * This function searches the mount tree starting from the root mount point to find
- * the most specific mount point that matches the given full path. It traverses down
- * the mount tree to identify the deepest mount point that is a prefix of the given path.
- *
- * @param[in] fullpath The full path string for which to find the associated mount point.
- *
- * @return If a matching mount point is found, returns a pointer to the corresponding dfs_mnt structure.
- * Otherwise, returns RT_NULL.
- */
- struct dfs_mnt *dfs_mnt_lookup(const char *fullpath)
- {
- struct dfs_mnt *mnt = _root_mnt;
- struct dfs_mnt *iter = RT_NULL;
- if (mnt)
- {
- int mnt_len = rt_strlen(mnt->fullpath);
- dfs_lock();
- if ((strncmp(mnt->fullpath, fullpath, mnt_len) == 0) &&
- (mnt_len == 1 || (fullpath[mnt_len] == '\0') || (fullpath[mnt_len] == '/')))
- {
- while (!rt_list_isempty(&mnt->child))
- {
- rt_list_for_each_entry(iter, &mnt->child, sibling)
- {
- mnt_len = rt_strlen(iter->fullpath);
- if ((strncmp(iter->fullpath, fullpath, mnt_len) == 0) &&
- ((fullpath[mnt_len] == '\0') || (fullpath[mnt_len] == '/')))
- {
- mnt = iter;
- break;
- }
- }
- if (mnt != iter) break;
- }
- }
- else
- {
- mnt = RT_NULL;
- }
- dfs_unlock();
- if (mnt)
- {
- LOG_D("mnt_lookup: %s path @ mount point %p", fullpath, mnt);
- DLOG(note, "mnt", "found mnt(%s)", mnt->fs_ops->name);
- }
- }
- return mnt;
- }
- /**
- * @brief Increase the reference count of a dfs_mnt structure instance.
- *
- * This function increments the reference count of the specified dfs_mnt structure.
- * The reference count is used to track how many parts of the system are currently
- * using this mount point.
- *
- * @param[in,out] mnt Pointer to the dfs_mnt structure whose reference count is to be increased.
- * If the pointer is valid, the reference count within the structure will be modified.
- * @return Returns the same pointer to the dfs_mnt structure that was passed in.
- * If the input pointer is NULL, it simply returns NULL.
- */
- struct dfs_mnt* dfs_mnt_ref(struct dfs_mnt* mnt)
- {
- if (mnt)
- {
- rt_atomic_add(&(mnt->ref_count), 1);
- DLOG(note, "mnt", "mnt(%s),ref_count=%d", mnt->fs_ops->name, rt_atomic_load(&(mnt->ref_count)));
- }
- return mnt;
- }
- /**
- * @brief Decrease the reference count of a dfs_mnt structure instance and free it if necessary.
- *
- * This function decrements the reference count of the specified dfs_mnt structure.
- * If the reference count reaches zero after the decrement, it will perform the unmount operation,
- * trigger the unmount hook, free the allocated path memory, and finally free the dfs_mnt structure itself.
- *
- * @param[in,out] mnt Pointer to the dfs_mnt structure whose reference count is to be decreased.
- * If the reference count reaches zero, the structure will be freed.
- *
- * @return returns RT_EOK to indicate success.
- */
- int dfs_mnt_unref(struct dfs_mnt *mnt)
- {
- rt_err_t ret = RT_EOK;
- rt_base_t ref_count;
- if (mnt)
- {
- ref_count = rt_atomic_sub(&(mnt->ref_count), 1) - 1;
- if (ref_count == 0)
- {
- dfs_lock();
- if (mnt->flags & MNT_IS_UMOUNT)
- {
- mnt->fs_ops->umount(mnt);
- RT_OBJECT_HOOKLIST_CALL(dfs_mnt_umnt, (mnt));
- }
- /* free full path */
- rt_free(mnt->fullpath);
- mnt->fullpath = RT_NULL;
- /* destroy self and the ref_count should be 0 */
- DLOG(msg, "mnt", "mnt", DLOG_MSG, "free mnt(%s)", mnt->fs_ops->name);
- rt_free(mnt);
- dfs_unlock();
- }
- else
- {
- DLOG(note, "mnt", "mnt(%s),ref_count=%d", mnt->fs_ops->name, rt_atomic_load(&(mnt->ref_count)));
- }
- }
- return ret;
- }
- /**
- * @brief Set specific flags for a dfs_mnt structure instance.
- *
- * This function sets specific flags for the given dfs_mnt structure.
- * If the MS_RDONLY flag is included in the input flags, it sets the MNT_RDONLY flag
- * for the mount point and cleans the page cache if the page cache feature is enabled.
- *
- * @param[in,out] mnt Pointer to the dfs_mnt structure for which flags are to be set.
- * The structure's `flags` member will be modified if necessary.
- * @param[in] flags The flags to be set for the mount point. This includes the MS_RDONLY flag.
- *
- * @return returns 0 to indicate success.
- */
- int dfs_mnt_setflags(struct dfs_mnt *mnt, int flags)
- {
- int error = 0;
- if (flags & MS_RDONLY)
- {
- mnt->flags |= MNT_RDONLY;
- #ifdef RT_USING_PAGECACHE
- dfs_pcache_clean(mnt);
- #endif
- }
- return error;
- }
- /**
- * @brief Destroy a dfs_mnt structure instance and unmount it if necessary.
- *
- * This function attempts to destroy the specified dfs_mnt structure instance.
- * If the mount point is currently mounted, it marks the mount point as unmounted,
- * sets the unmount flag, and removes it from the mount list if it was added.
- * Finally, it decreases the reference count of the mount point and frees the
- * structure if the reference count reaches zero.
- *
- * @param[in,out] mnt Pointer to the dfs_mnt structure to be destroyed.
- *
- * @return Returns RT_EOK to indicate success.
- */
- int dfs_mnt_destroy(struct dfs_mnt* mnt)
- {
- rt_err_t ret = RT_EOK;
- if (mnt)
- {
- if (mnt->flags & MNT_IS_MOUNTED)
- {
- mnt->flags &= ~MNT_IS_MOUNTED;
- mnt->flags |= MNT_IS_UMOUNT;
- /* remote it from mnt list */
- if (mnt->flags & MNT_IS_ADDLIST)
- {
- dfs_mnt_remove(mnt);
- }
- }
- dfs_mnt_unref(mnt);
- }
- return ret;
- }
- /**
- * @brief Recursively traverse the mount point tree and apply a callback function.
- *
- * This function performs a depth-first traversal of the mount point tree starting from the given mount point.
- * It applies the specified callback function to each mount point in the tree. If the callback function returns
- * a non-NULL pointer, the traversal stops and the result is returned immediately.
- *
- * @param[in] mnt Pointer to the root dfs_mnt structure from which the traversal will start.
- * If NULL, the function will return RT_NULL without performing any traversal.
- * @param[in] func Pointer to the callback function to be applied to each mount point.
- * The callback function takes a pointer to a dfs_mnt structure and a generic parameter,
- * and returns a pointer to a dfs_mnt structure or RT_NULL.
- * @param[in] parameter Generic pointer to a parameter that will be passed to the callback function.
- *
- * @return If the callback function returns a non-NULL pointer during the traversal, returns that pointer.
- * Otherwise, returns RT_NULL.
- */
- static struct dfs_mnt* _dfs_mnt_foreach(struct dfs_mnt *mnt, struct dfs_mnt* (*func)(struct dfs_mnt *mnt, void *parameter), void *parameter)
- {
- struct dfs_mnt *iter, *ret = NULL;
- if (mnt)
- {
- ret = func(mnt, parameter);
- if (ret == RT_NULL)
- {
- if (!rt_list_isempty(&mnt->child))
- {
- /* for each in mount point list */
- rt_list_for_each_entry(iter, &mnt->child, sibling)
- {
- ret = _dfs_mnt_foreach(iter, func, parameter);
- if (ret != RT_NULL)
- {
- break;
- }
- }
- }
- }
- }
- else
- {
- ret = RT_NULL;
- }
- return ret;
- }
- /**
- * @brief Compare a mount point's device ID with a given device object.
- *
- * This function checks if the device ID associated with a specified mount point
- * matches the given device object. If a match is found, it returns a pointer to
- * the corresponding dfs_mnt structure; otherwise, it returns RT_NULL.
- *
- * @param[in] mnt Pointer to the dfs_mnt structure representing the mount point to be checked.
- * @param[in] device Pointer to the device object to compare against the mount point's device ID.
- *
- * @return If the device ID of the mount point matches the given device object, returns a pointer to the dfs_mnt structure.
- * Otherwise, returns RT_NULL.
- */
- static struct dfs_mnt* _mnt_cmp_devid(struct dfs_mnt *mnt, void *device)
- {
- struct dfs_mnt *ret = RT_NULL;
- struct rt_device *dev = (struct rt_device*)device;
- if (dev && mnt)
- {
- if (mnt->dev_id == dev)
- {
- ret = mnt;
- }
- }
- return ret;
- }
- /**
- * this function will return the mounted path for specified device.
- *
- * @param[in] device the device object which is mounted.
- *
- * @return the mounted path or NULL if none device mounted.
- */
- const char *dfs_mnt_get_mounted_path(struct rt_device *device)
- {
- const char* path = RT_NULL;
- if (_root_mnt)
- {
- struct dfs_mnt* mnt;
- dfs_lock();
- mnt = _dfs_mnt_foreach(_root_mnt, _mnt_cmp_devid, device);
- dfs_unlock();
- if (mnt) path = mnt->fullpath;
- }
- return path;
- }
- /**
- * @brief Print information about a mount point to the console.
- *
- * This function is designed to be used as a callback in the mount point tree traversal.
- * It prints the file system name, device name (or `(NULL)` if no device is associated),
- * mount path, and reference count of the specified mount point to the console using `rt_kprintf`.
- *
- * @param[in] mnt Pointer to the dfs_mnt structure representing the mount point to be printed.
- * If NULL, the function does nothing.
- * @param[in] parameter A generic pointer to a parameter. This parameter is not used in this function.
- *
- * @return Always returns RT_NULL as it is a callback function mainly used for side - effects (printing).
- */
- static struct dfs_mnt* _mnt_dump(struct dfs_mnt *mnt, void *parameter)
- {
- if (mnt)
- {
- if (mnt->dev_id)
- {
- rt_kprintf("%-10s %-6s %-10s %d\n",
- mnt->fs_ops->name, mnt->dev_id->parent.name, mnt->fullpath, rt_atomic_load(&(mnt->ref_count)));
- }
- else
- {
- rt_kprintf("%-10s (NULL) %-10s %d\n",
- mnt->fs_ops->name, mnt->fullpath, rt_atomic_load(&(mnt->ref_count)));
- }
- }
- return RT_NULL;
- }
- /**
- * @brief Compare a mount point's full path with a given path.
- *
- * This function is designed to be used as a callback in the mount point tree traversal.
- * It compares the full path of the specified mount point with the given path.
- * If the mount point's full path starts with the given path, it returns a pointer to the dfs_mnt structure;
- * otherwise, it returns RT_NULL.
- *
- * @param[in] mnt Pointer to the dfs_mnt structure representing the mount point to be checked.
- * If NULL, the function will not perform the comparison and return RT_NULL.
- * @param[in] parameter A generic pointer to a parameter, which should be cast to a `const char*`
- * representing the path to compare against the mount point's full path.
- *
- * @return If the mount point's full path starts with the given path, returns a pointer to the dfs_mnt structure.
- * Otherwise, returns RT_NULL.
- */
- static struct dfs_mnt* _mnt_cmp_path(struct dfs_mnt* mnt, void *parameter)
- {
- const char* fullpath = (const char*)parameter;
- struct dfs_mnt *ret = RT_NULL;
- if (strncmp(mnt->fullpath, fullpath, rt_strlen(fullpath)) == 0)
- {
- ret = mnt;
- }
- return ret;
- }
- /**
- * @brief Check if a mount point has a child mount point matching the given path.
- *
- * This function checks whether the specified mount point has a child mount point
- * whose full path starts with the given path. It uses a depth-first traversal of
- * the mount point tree starting from the provided mount point and applies the
- * `_mnt_cmp_path` callback function to each mount point.
- *
- * @param[in] mnt Pointer to the root dfs_mnt structure from which the search will start.
- * If NULL, the function will return RT_FALSE without performing any search.
- * @param[in] fullpath The full path string to compare against the child mount points' paths.
- * If NULL, the function will return RT_FALSE without performing any search.
- *
- * @return Returns RT_TRUE if a child mount point with a matching path is found.
- * Returns RT_FALSE if no matching child mount point is found, or if either input parameter is NULL.
- */
- rt_bool_t dfs_mnt_has_child_mnt(struct dfs_mnt *mnt, const char* fullpath)
- {
- int ret = RT_FALSE;
- if (mnt && fullpath)
- {
- struct dfs_mnt *m = RT_NULL;
- dfs_lock();
- m = _dfs_mnt_foreach(mnt, _mnt_cmp_path, (void*)fullpath);
- dfs_unlock();
- if (m)
- {
- ret = RT_TRUE;
- }
- }
- return ret;
- }
- /**
- * @brief List all mount points starting from a specified mount point.
- *
- * This function lists information about all mount points in the mount point tree,
- * starting from the specified mount point. If the input mount point is NULL,
- * it starts from the root mount point. It uses the `_dfs_mnt_foreach` function
- * with the `_mnt_dump` callback to print mount point information.
- *
- * @param[in] mnt Pointer to the dfs_mnt structure from which to start listing mount points.
- * If NULL, the function will start from the root mount point.
- *
- * @return Always returns 0 to indicate success.
- */
- int dfs_mnt_list(struct dfs_mnt *mnt)
- {
- if (!mnt) mnt = _root_mnt;
- /* lock file system */
- dfs_lock();
- _dfs_mnt_foreach(mnt, _mnt_dump, RT_NULL);
- /* unlock file system */
- dfs_unlock();
- return 0;
- }
- /**
- * @brief Traverse all mount points in the mount tree and apply a callback function.
- *
- * @param[in] func Pointer to the callback function to be applied to each mount point.
- * The callback function takes a pointer to a `dfs_mnt` structure and a generic parameter,
- * and returns a pointer to a `dfs_mnt` structure or `RT_NULL`.
- * @param[in] parameter Generic pointer to a parameter that will be passed to the callback function.
- *
- * @return Always returns 0.
- */
- int dfs_mnt_foreach(struct dfs_mnt* (*func)(struct dfs_mnt *mnt, void *parameter), void *parameter)
- {
- /* lock file system */
- dfs_lock();
- _dfs_mnt_foreach(_root_mnt, func, parameter);
- /* unlock file system */
- dfs_unlock();
- return 0;
- }
|