452 lines
10 KiB
C
452 lines
10 KiB
C
/*
|
|
* 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 <rtthread.h>
|
|
|
|
#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 <rtdbg.h>
|
|
|
|
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;
|
|
}
|