522 lines
16 KiB
C
522 lines
16 KiB
C
/*
|
|
* Copyright (c) 2006-2023, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2005-02-22 Bernard The first version.
|
|
* 2010-06-30 Bernard Optimize for RT-Thread RTOS
|
|
* 2011-03-12 Bernard fix the filesystem lookup issue.
|
|
* 2017-11-30 Bernard fix the filesystem_operation_table issue.
|
|
* 2017-12-05 Bernard fix the fs type search issue in mkfs.
|
|
* 2023-05-05 Bernard change to dfs v2.0
|
|
*/
|
|
|
|
#include <dfs_fs.h>
|
|
#include <dfs_file.h>
|
|
#include <dfs_dentry.h>
|
|
#include <dfs_mnt.h>
|
|
#include "dfs_private.h"
|
|
|
|
#ifdef RT_USING_PAGECACHE
|
|
#include "dfs_pcache.h"
|
|
#endif
|
|
|
|
#define DBG_TAG "DFS.fs"
|
|
#define DBG_LVL DBG_INFO
|
|
#include <rtdbg.h>
|
|
|
|
static struct dfs_filesystem_type *file_systems = NULL;
|
|
extern rt_list_t _mnt_list;
|
|
|
|
/**
|
|
* @addtogroup FsApi
|
|
*/
|
|
/*@{*/
|
|
|
|
static struct dfs_filesystem_type **_find_filesystem(const char *name)
|
|
{
|
|
struct dfs_filesystem_type **type;
|
|
for (type = &file_systems; *type; type = &(*type)->next)
|
|
{
|
|
if (strcmp((*type)->fs_ops->name, name) == 0)
|
|
break;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
struct dfs_filesystem_type *dfs_filesystems(void)
|
|
{
|
|
return file_systems;
|
|
}
|
|
|
|
int dfs_register(struct dfs_filesystem_type *fs)
|
|
{
|
|
int ret = 0;
|
|
struct dfs_filesystem_type **type = _find_filesystem(fs->fs_ops->name);
|
|
|
|
LOG_D("register %s file system.", fs->fs_ops->name);
|
|
|
|
if (*type)
|
|
{
|
|
ret = -EBUSY;
|
|
}
|
|
else
|
|
{
|
|
*type = fs;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int dfs_unregister(struct dfs_filesystem_type *fs)
|
|
{
|
|
int ret = 0;
|
|
struct dfs_filesystem_type **type;
|
|
|
|
if (fs)
|
|
{
|
|
LOG_D("unregister %s file system.", fs->fs_ops->name);
|
|
|
|
for (type = &file_systems; *type; type = &(*type)->next)
|
|
{
|
|
if (strcmp((*type)->fs_ops->name, fs->fs_ops->name) == 0)
|
|
{
|
|
*type = (*type)->next;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!*type) ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* parent(mount path)
|
|
* mnt_parent <- - - - - - - +
|
|
* | |
|
|
* |- mnt_child <- - - - - -+ (1 refcount)
|
|
* | |
|
|
* |- parent - - + (1 refcount)
|
|
*/
|
|
int dfs_mount(const char *device_name,
|
|
const char *path,
|
|
const char *filesystemtype,
|
|
unsigned long rwflag,
|
|
const void *data)
|
|
{
|
|
int ret = RT_EOK;
|
|
char *fullpath = RT_NULL;
|
|
rt_device_t dev_id = RT_NULL;
|
|
struct dfs_mnt *mnt_parent = RT_NULL, *mnt_child = RT_NULL;
|
|
struct dfs_dentry *mntpoint_dentry = RT_NULL;
|
|
struct dfs_filesystem_type *type = *_find_filesystem(filesystemtype);
|
|
|
|
if (type)
|
|
{
|
|
fullpath = dfs_normalize_path(RT_NULL, path);
|
|
if (!fullpath)
|
|
{
|
|
rt_set_errno(EPERM);
|
|
ret = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rt_set_errno(ENOENT);
|
|
ret = -1;
|
|
}
|
|
|
|
if (fullpath)
|
|
{
|
|
DLOG(note, "mnt", "mount %s(%s) on path: %s", device_name, filesystemtype, fullpath);
|
|
|
|
/* open specific device */
|
|
if (device_name) dev_id = rt_device_find(device_name);
|
|
|
|
if (!(type->fs_ops->flags & FS_NEED_DEVICE) ||
|
|
((type->fs_ops->flags & FS_NEED_DEVICE) && dev_id))
|
|
{
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "mnt_parent = dfs_mnt_lookup(%s)", fullpath);
|
|
mnt_parent = dfs_mnt_lookup(fullpath);
|
|
if ((!mnt_parent && (strcmp(fullpath, "/") == 0 || strcmp(fullpath, "/dev") == 0))
|
|
|| (mnt_parent && strcmp(fullpath, "/") == 0 && strcmp(mnt_parent->fullpath, fullpath) != 0))
|
|
{
|
|
LOG_D("no mnt found @ mount point %s, should be root.", fullpath);
|
|
DLOG(msg, "mnt", "dfs", DLOG_MSG_RET, "no mnt");
|
|
|
|
/* it's the root file system */
|
|
/* the mount point dentry is the same as root dentry. */
|
|
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "mnt_parent = dfs_mnt_create(path)");
|
|
mnt_parent = dfs_mnt_create(fullpath); /* mnt->ref_count should be 1. */
|
|
if (mnt_parent)
|
|
{
|
|
DLOG(msg, "mnt", "dfs", DLOG_MSG_RET, "return mnt, ref_count=1");
|
|
|
|
mnt_parent->fs_ops = type->fs_ops;
|
|
mnt_parent->dev_id = dev_id;
|
|
if (mnt_parent->fs_ops->mount)
|
|
{
|
|
DLOG(msg, "dfs", type->fs_ops->name, DLOG_MSG, "fs_ops->mount(mnt_parent, rwflag, data)");
|
|
ret = mnt_parent->fs_ops->mount(mnt_parent, rwflag, data);
|
|
if (ret == RT_EOK)
|
|
{
|
|
DLOG(msg, type->fs_ops->name, "dfs", DLOG_MSG_RET, "mount OK, ret root_dentry");
|
|
|
|
mnt_child = mnt_parent;
|
|
mnt_child->flags |= MNT_IS_MOUNTED;
|
|
|
|
DLOG(note_right, "mnt", "mount sucessfully");
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "dfs_mnt_insert(, mnt_child)");
|
|
dfs_mnt_insert(RT_NULL, mnt_child);
|
|
|
|
/* unref it, because the ref_count = 1 when create */
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "dfs_mnt_unref(mnt_parent)");
|
|
dfs_mnt_unref(mnt_parent);
|
|
|
|
/*
|
|
* About root mnt:
|
|
* There are two ref_count:
|
|
* 1. the gobal root reference.
|
|
* 1. the mnt->parent reference.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
LOG_W("mount %s failed with file system type: %s", fullpath, type->fs_ops->name);
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "dfs_mnt_destroy(mnt_parent)");
|
|
dfs_mnt_destroy(mnt_parent);
|
|
mnt_parent = RT_NULL;
|
|
rt_set_errno(EPERM);
|
|
ret = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_W("no mount method on file system type: %s", type->fs_ops->name);
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "dfs_mnt_destroy(mnt_parent), no mount method");
|
|
dfs_mnt_destroy(mnt_parent);
|
|
mnt_parent = RT_NULL;
|
|
rt_set_errno(EIO);
|
|
ret = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_E("create a mnt point failed.");
|
|
rt_set_errno(ENOMEM);
|
|
ret = -1;
|
|
}
|
|
}
|
|
else if (mnt_parent && (strcmp(mnt_parent->fullpath, fullpath) != 0))
|
|
{
|
|
DLOG(msg, "dfs", "dentry", DLOG_MSG, "mntpoint_dentry = dfs_dentry_lookup(mnt_parent, %s, 0)", fullpath);
|
|
mntpoint_dentry = dfs_dentry_lookup(mnt_parent, fullpath, 0);
|
|
if (mntpoint_dentry)
|
|
{
|
|
DLOG(msg, "dentry", "dfs", DLOG_MSG_RET, "dentry exist");
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "mnt_child = dfs_mnt_create(path)");
|
|
mnt_child = dfs_mnt_create(fullpath);
|
|
if (mnt_child)
|
|
{
|
|
LOG_D("create mnt point %p", mnt_child);
|
|
|
|
mnt_child->fs_ops = type->fs_ops;
|
|
mnt_child->dev_id = dev_id;
|
|
|
|
if (mnt_child->fs_ops->mount)
|
|
{
|
|
DLOG(msg, "dfs", type->fs_ops->name, DLOG_MSG, "root_dentry = fs_ops->mount(mnt_child, rwflag, data)");
|
|
ret = mnt_child->fs_ops->mount(mnt_child, rwflag, data);
|
|
if (ret == RT_EOK)
|
|
{
|
|
mnt_child->flags |= MNT_IS_MOUNTED;
|
|
|
|
LOG_D("mount %s sucessfully", fullpath);
|
|
DLOG(msg, mnt_child->fs_ops->name, "dfs", DLOG_MSG_RET, "mount OK");
|
|
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "dfs_mnt_insert(mnt_parent, mnt_child)");
|
|
dfs_mnt_insert(mnt_parent, mnt_child);
|
|
|
|
/* unref it, because the ref_count = 1 when create */
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "dfs_mnt_unref(mnt_child)");
|
|
dfs_mnt_unref(mnt_child);
|
|
}
|
|
else
|
|
{
|
|
LOG_W("mount %s failed with file system type: %s", fullpath, type->fs_ops->name);
|
|
DLOG(msg, mnt_child->fs_ops->name, "dfs", DLOG_MSG_RET, "mount failed");
|
|
dfs_mnt_destroy(mnt_child);
|
|
rt_set_errno(EPERM);
|
|
ret = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_W("no mount method on file system type: %s", type->fs_ops->name);
|
|
dfs_mnt_destroy(mnt_child);
|
|
rt_set_errno(EIO);
|
|
ret = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_E("create a mnt point failed.");
|
|
rt_set_errno(ENOMEM);
|
|
ret = -1;
|
|
}
|
|
dfs_dentry_unref(mntpoint_dentry);
|
|
}
|
|
else
|
|
{
|
|
LOG_W("no mount point (%s) in file system: %s", fullpath, mnt_parent->fullpath);
|
|
rt_set_errno(ENOTDIR);
|
|
ret = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_E("mount point (%s) already mounted!", fullpath);
|
|
rt_set_errno(EEXIST);
|
|
ret = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_E("No device found for this file system.");
|
|
rt_set_errno(ENODEV);
|
|
ret = -1;
|
|
}
|
|
rt_free(fullpath);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int dfs_umount(const char *specialfile, int flags)
|
|
{
|
|
int ret = -RT_ERROR;
|
|
char *fullpath = RT_NULL;
|
|
struct dfs_mnt *mnt = RT_NULL;
|
|
|
|
fullpath = dfs_normalize_path(NULL, specialfile);
|
|
if (fullpath)
|
|
{
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "mnt = dfs_mnt_lookup(%s)", fullpath);
|
|
mnt = dfs_mnt_lookup(fullpath);
|
|
if (mnt)
|
|
{
|
|
if (strcmp(mnt->fullpath, fullpath) == 0)
|
|
{
|
|
/* is the mount point */
|
|
rt_atomic_t ref_count = rt_atomic_load(&(mnt->ref_count));
|
|
|
|
if (!(mnt->flags & MNT_IS_LOCKED) && rt_list_isempty(&mnt->child) && (ref_count == 1 || (flags & MNT_FORCE)))
|
|
{
|
|
#ifdef RT_USING_PAGECACHE
|
|
dfs_pcache_unmount(mnt);
|
|
#endif
|
|
/* destroy this mount point */
|
|
DLOG(msg, "dfs", "mnt", DLOG_MSG, "dfs_mnt_destroy(mnt)");
|
|
ret = dfs_mnt_destroy(mnt);
|
|
}
|
|
else
|
|
{
|
|
LOG_E("the file system is busy!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_E("the path:%s is not a mountpoint!", fullpath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_E("no filesystem found.");
|
|
}
|
|
rt_free(fullpath);
|
|
}
|
|
else
|
|
{
|
|
rt_set_errno(-ENOTDIR);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* for compatibility */
|
|
int dfs_unmount(const char *specialfile)
|
|
{
|
|
return dfs_umount(specialfile, 0);
|
|
}
|
|
|
|
int dfs_is_mounted(struct dfs_mnt *mnt)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (mnt && !(mnt->flags & MNT_IS_MOUNTED))
|
|
{
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int dfs_mkfs(const char *fs_name, const char *device_name)
|
|
{
|
|
rt_device_t dev_id = NULL;
|
|
struct dfs_filesystem_type *type;
|
|
int ret = -RT_ERROR;
|
|
|
|
type = *_find_filesystem(fs_name);
|
|
if (!type)
|
|
{
|
|
rt_kprintf("no file system: %s found!\n", fs_name);
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
if (type->fs_ops->flags & FS_NEED_DEVICE)
|
|
{
|
|
/* check device name, and it should not be NULL */
|
|
if (device_name != NULL)
|
|
dev_id = rt_device_find(device_name);
|
|
|
|
if (dev_id == NULL)
|
|
{
|
|
rt_set_errno(-ENODEV);
|
|
rt_kprintf("Device (%s) was not found", device_name);
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dev_id = RT_NULL;
|
|
}
|
|
}
|
|
|
|
if (type->fs_ops->mkfs)
|
|
{
|
|
ret = type->fs_ops->mkfs(dev_id, type->fs_ops->name);
|
|
#ifdef RT_USING_PAGECACHE
|
|
if (ret == RT_EOK)
|
|
{
|
|
struct dfs_mnt *mnt = RT_NULL;
|
|
|
|
mnt = dfs_mnt_dev_lookup(dev_id);
|
|
if (mnt)
|
|
{
|
|
dfs_pcache_unmount(mnt);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int dfs_statfs(const char *path, struct statfs *buffer)
|
|
{
|
|
struct dfs_mnt *mnt;
|
|
char *fullpath;
|
|
int ret = -RT_ERROR;
|
|
|
|
fullpath = dfs_normalize_path(NULL, path);
|
|
if (!fullpath)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath);
|
|
mnt = dfs_mnt_lookup(fullpath);
|
|
if (mnt)
|
|
{
|
|
if (mnt->fs_ops->statfs)
|
|
{
|
|
if (dfs_is_mounted(mnt) == 0)
|
|
{
|
|
ret = mnt->fs_ops->statfs(mnt, buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
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_filesystem_get_mounted_path(struct rt_device *device)
|
|
{
|
|
const char *path = NULL;
|
|
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* this function will fetch the partition table on specified buffer.
|
|
*
|
|
* @param part the returned partition structure.
|
|
* @param buf the buffer contains partition table.
|
|
* @param pindex the index of partition table to fetch.
|
|
*
|
|
* @return RT_EOK on successful or -RT_ERROR on failed.
|
|
*/
|
|
int dfs_filesystem_get_partition(struct dfs_partition *part,
|
|
uint8_t *buf,
|
|
uint32_t pindex)
|
|
{
|
|
#define DPT_ADDRESS 0x1be /* device partition offset in Boot Sector */
|
|
#define DPT_ITEM_SIZE 16 /* partition item size */
|
|
|
|
uint8_t *dpt;
|
|
uint8_t type;
|
|
|
|
RT_ASSERT(part != NULL);
|
|
RT_ASSERT(buf != NULL);
|
|
|
|
dpt = buf + DPT_ADDRESS + pindex * DPT_ITEM_SIZE;
|
|
|
|
/* check if it is a valid partition table */
|
|
if ((*dpt != 0x80) && (*dpt != 0x00))
|
|
return -EIO;
|
|
|
|
/* get partition type */
|
|
type = *(dpt + 4);
|
|
if (type == 0)
|
|
return -EIO;
|
|
|
|
/* set partition information
|
|
* size is the number of 512-Byte */
|
|
part->type = type;
|
|
part->offset = *(dpt + 8) | *(dpt + 9) << 8 | *(dpt + 10) << 16 | *(dpt + 11) << 24;
|
|
part->size = *(dpt + 12) | *(dpt + 13) << 8 | *(dpt + 14) << 16 | *(dpt + 15) << 24;
|
|
|
|
rt_kprintf("found part[%d], begin: %ld, size: ",
|
|
pindex, part->offset * 512);
|
|
if ((part->size >> 11) == 0)
|
|
rt_kprintf("%ld%s", part->size >> 1, "KB\n"); /* KB */
|
|
else
|
|
{
|
|
unsigned int part_size;
|
|
part_size = part->size >> 11; /* MB */
|
|
if ((part_size >> 10) == 0)
|
|
rt_kprintf("%d.%ld%s", part_size, (part->size >> 1) & 0x3FF, "MB\n");
|
|
else
|
|
rt_kprintf("%d.%d%s", part_size >> 10, part_size & 0x3FF, "GB\n");
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
/* @} */
|