/* * Copyright (c) 2006-2023, 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 #include "dfs.h" #include "dfs_mnt.h" #include "dfs_dentry.h" #include "dfs_private.h" #define DBG_TAG "DFS.mnt" #define DBG_LVL DBG_WARNING #include static struct dfs_mnt *_root_mnt = RT_NULL; /* * mnt tree structure * * mnt_root <----------------------------------------+ * | (child) +----------+ | * v (sibling) v | | * mnt_child0 -> mnt_child1 | | * | (child) | | * v / (parent) | (root) * mnt_child10 ---/ * */ 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; } 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 = 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; } /* remove mnt from mnt_tree */ 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; } 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; } 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; } /** * this function will return the file system mounted on specified path. * * @param path the specified path string. * * @return the found file system or NULL if no file system mounted on * specified path */ 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; } 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; } int dfs_mnt_unref(struct dfs_mnt* mnt) { rt_err_t ret = RT_EOK; if (mnt) { rt_atomic_sub(&(mnt->ref_count), 1); if (rt_atomic_load(&(mnt->ref_count)) == 0) { dfs_lock(); if (mnt->flags & MNT_IS_UMOUNT) { mnt->fs_ops->umount(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; } 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; } 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; } 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 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; } 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; } 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; } 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; } 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; } 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; }