452 lines
10 KiB
C
Raw Normal View History

2024-08-05 20:57:09 +08:00
/*
* 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;
}