first commit for chrg
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-10-10 Bernard The first version of rewrite dfs
|
||||
*/
|
||||
#include <rtthread.h>
|
||||
|
||||
#include "dfs.h"
|
||||
#include "dfs_file.h"
|
||||
#include "dfs_private.h"
|
||||
#include "dfs_dentry.h"
|
||||
#include "dfs_mnt.h"
|
||||
|
||||
#define DBG_TAG "DFS.dentry"
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#include <rtdbg.h>
|
||||
|
||||
#define DFS_DENTRY_HASH_NR 32
|
||||
struct dentry_hash_head
|
||||
{
|
||||
rt_list_t head[DFS_DENTRY_HASH_NR];
|
||||
};
|
||||
static struct dentry_hash_head hash_head;
|
||||
|
||||
/**
|
||||
* @brief Calculate hash value for a dentry based on mount point and path
|
||||
*
|
||||
* @param[in] mnt Pointer to the mount point structure
|
||||
* @param[in] path Path string to be hashed (can be NULL)
|
||||
*
|
||||
* @return uint32_t Calculated hash value within range [0, DFS_DENTRY_HASH_NR-1]
|
||||
*/
|
||||
static uint32_t _dentry_hash(struct dfs_mnt *mnt, const char *path)
|
||||
{
|
||||
uint32_t val = 0;
|
||||
|
||||
if (path)
|
||||
{
|
||||
while (*path)
|
||||
{
|
||||
val = ((val << 5) + val) + *path++;
|
||||
}
|
||||
}
|
||||
return (val ^ (unsigned long) mnt) & (DFS_DENTRY_HASH_NR - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new directory entry (dentry) structure
|
||||
*
|
||||
* @param[in] mnt Pointer to the mount point structure
|
||||
* @param[in] path Path string for the dentry (absolute or relative)
|
||||
* @param[in] is_rela_path Flag indicating if path is relative (RT_TRUE) or absolute (RT_FALSE)
|
||||
*
|
||||
* @return struct dfs_dentry* Pointer to newly created dentry, or NULL if creation failed
|
||||
*
|
||||
* @note The created dentry will have its ref_count initialized to 1 and DENTRY_IS_ALLOCED flag set
|
||||
*/
|
||||
static struct dfs_dentry *_dentry_create(struct dfs_mnt *mnt, char *path, rt_bool_t is_rela_path)
|
||||
{
|
||||
struct dfs_dentry *dentry = RT_NULL;
|
||||
|
||||
if (mnt == RT_NULL || path == RT_NULL)
|
||||
{
|
||||
return dentry;
|
||||
}
|
||||
|
||||
dentry = (struct dfs_dentry *)rt_calloc(1, sizeof(struct dfs_dentry));
|
||||
if (dentry)
|
||||
{
|
||||
char *dentry_path = path;
|
||||
if (!is_rela_path)
|
||||
{
|
||||
int mntpoint_len = strlen(mnt->fullpath);
|
||||
|
||||
if (rt_strncmp(mnt->fullpath, dentry_path, mntpoint_len) == 0)
|
||||
{
|
||||
dentry_path += mntpoint_len;
|
||||
}
|
||||
}
|
||||
|
||||
dentry->pathname = strlen(dentry_path) ? rt_strdup(dentry_path) : rt_strdup(path);
|
||||
dentry->mnt = dfs_mnt_ref(mnt);
|
||||
|
||||
rt_atomic_store(&(dentry->ref_count), 1);
|
||||
dentry->flags |= DENTRY_IS_ALLOCED;
|
||||
|
||||
LOG_I("create a dentry:%p for %s", dentry, mnt->fullpath);
|
||||
}
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new directory entry (dentry) with absolute path
|
||||
*
|
||||
* @param[in] mnt Pointer to the mount point structure
|
||||
* @param[in] fullpath Absolute path string for the dentry
|
||||
*
|
||||
* @return struct dfs_dentry* Pointer to newly created dentry, or NULL if creation failed
|
||||
*
|
||||
* @note This is a wrapper for _dentry_create() with is_rela_path set to RT_FALSE
|
||||
* @see _dentry_create()
|
||||
*/
|
||||
struct dfs_dentry *dfs_dentry_create(struct dfs_mnt *mnt, char *fullpath)
|
||||
{
|
||||
return _dentry_create(mnt, fullpath, RT_FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new directory entry (dentry) with relative path
|
||||
*
|
||||
* @param[in] mnt Pointer to the mount point structure
|
||||
* @param[in] rela_path Relative path string for the dentry
|
||||
*
|
||||
* @return struct dfs_dentry* Pointer to newly created dentry, or NULL if creation failed
|
||||
*
|
||||
* @note This is a wrapper for _dentry_create() with is_rela_path set to RT_TRUE
|
||||
* @see _dentry_create()
|
||||
*/
|
||||
struct dfs_dentry *dfs_dentry_create_rela(struct dfs_mnt *mnt, char *rela_path)
|
||||
{
|
||||
return _dentry_create(mnt, rela_path, RT_TRUE);;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increase reference count for a directory entry (dentry)
|
||||
*
|
||||
* @param[in,out] dentry Pointer to the directory entry structure to be referenced
|
||||
*
|
||||
* @return struct dfs_dentry* The same dentry pointer that was passed in
|
||||
*
|
||||
* @note This function will also increase reference count for associated vnode if exists
|
||||
*/
|
||||
struct dfs_dentry * dfs_dentry_ref(struct dfs_dentry *dentry)
|
||||
{
|
||||
if (dentry)
|
||||
{
|
||||
int ret = dfs_file_lock();
|
||||
if (ret == RT_EOK)
|
||||
{
|
||||
rt_atomic_add(&(dentry->ref_count), 1);
|
||||
if (dentry->vnode)
|
||||
{
|
||||
rt_atomic_add(&(dentry->vnode->ref_count), 1);
|
||||
}
|
||||
dfs_file_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decrease reference count for a directory entry (dentry) and free if count reaches zero
|
||||
*
|
||||
* @param[in,out] dentry Pointer to the directory entry structure to be unreferenced
|
||||
*
|
||||
* @return struct dfs_dentry* The same dentry pointer if ref_count > 0, NULL if freed
|
||||
*/
|
||||
struct dfs_dentry *dfs_dentry_unref(struct dfs_dentry *dentry)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (dentry)
|
||||
{
|
||||
ret = dfs_file_lock();
|
||||
if (ret == RT_EOK)
|
||||
{
|
||||
if (dentry->flags & DENTRY_IS_ALLOCED)
|
||||
{
|
||||
rt_atomic_sub(&(dentry->ref_count), 1);
|
||||
}
|
||||
|
||||
if (rt_atomic_load(&(dentry->ref_count)) == 0)
|
||||
{
|
||||
DLOG(msg, "dentry", "dentry", DLOG_MSG, "free dentry, ref_count=0");
|
||||
if (dentry->flags & DENTRY_IS_ADDHASH)
|
||||
{
|
||||
rt_list_remove(&dentry->hashlist);
|
||||
}
|
||||
|
||||
/* release vnode */
|
||||
if (dentry->vnode)
|
||||
{
|
||||
dfs_vnode_unref(dentry->vnode);
|
||||
}
|
||||
|
||||
/* release mnt */
|
||||
DLOG(msg, "dentry", "mnt", DLOG_MSG, "dfs_mnt_unref(dentry->mnt)");
|
||||
if (dentry->mnt)
|
||||
{
|
||||
dfs_mnt_unref(dentry->mnt);
|
||||
}
|
||||
|
||||
dfs_file_unlock();
|
||||
|
||||
LOG_I("free a dentry: %p", dentry);
|
||||
rt_free(dentry->pathname);
|
||||
rt_free(dentry);
|
||||
dentry = RT_NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dentry->vnode)
|
||||
{
|
||||
rt_atomic_sub(&(dentry->vnode->ref_count), 1);
|
||||
}
|
||||
dfs_file_unlock();
|
||||
DLOG(note, "dentry", "dentry ref_count=%d", rt_atomic_load(&(dentry->ref_count)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Look up a directory entry (dentry) in hash table by mount point and path
|
||||
*
|
||||
* @param[in] mnt Pointer to the mount point structure to search for
|
||||
* @param[in] path Path string to search for
|
||||
*
|
||||
* @return struct dfs_dentry* Pointer to found dentry (with increased ref_count), or NULL if not found
|
||||
*/
|
||||
static struct dfs_dentry *_dentry_hash_lookup(struct dfs_mnt *mnt, const char *path)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
struct dfs_dentry *entry = RT_NULL;
|
||||
|
||||
ret = dfs_file_lock();
|
||||
if (ret == RT_EOK)
|
||||
{
|
||||
rt_list_for_each_entry(entry, &hash_head.head[_dentry_hash(mnt, path)], hashlist)
|
||||
{
|
||||
if (entry->mnt == mnt && !strcmp(entry->pathname, path))
|
||||
{
|
||||
dfs_dentry_ref(entry);
|
||||
dfs_file_unlock();
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
dfs_file_unlock();
|
||||
}
|
||||
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert a directory entry (dentry) into the hash table
|
||||
*
|
||||
* @param[in,out] dentry Pointer to the directory entry to be inserted
|
||||
*/
|
||||
void dfs_dentry_insert(struct dfs_dentry *dentry)
|
||||
{
|
||||
dfs_file_lock();
|
||||
rt_list_insert_after(&hash_head.head[_dentry_hash(dentry->mnt, dentry->pathname)], &dentry->hashlist);
|
||||
dentry->flags |= DENTRY_IS_ADDHASH;
|
||||
dfs_file_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Look up a directory entry (dentry) in the filesystem
|
||||
*
|
||||
* @param[in] mnt Pointer to the mount point structure
|
||||
* @param[in] path Path string to look up
|
||||
* @param[in] flags Additional lookup flags (currently unused)
|
||||
*
|
||||
* @return struct dfs_dentry* Pointer to found/created dentry (with increased ref_count), or NULL if not found
|
||||
*
|
||||
* @note This function first searches for dentry in hash table,
|
||||
* If not found and filesystem supports lookup operation:
|
||||
* - Creates new dentry
|
||||
* - Calls filesystem's lookup operation to get vnode
|
||||
* - If vnode is successfully obtained, adds dentry to hash table
|
||||
*/
|
||||
struct dfs_dentry *dfs_dentry_lookup(struct dfs_mnt *mnt, const char *path, uint32_t flags)
|
||||
{
|
||||
struct dfs_dentry *dentry;
|
||||
struct dfs_vnode *vnode = RT_NULL;
|
||||
int mntpoint_len = strlen(mnt->fullpath);
|
||||
|
||||
if (rt_strncmp(mnt->fullpath, path, mntpoint_len) == 0)
|
||||
{
|
||||
path += mntpoint_len;
|
||||
if ((*path) == '\0')
|
||||
{
|
||||
/* root */
|
||||
path = "/";
|
||||
}
|
||||
}
|
||||
dfs_file_lock();
|
||||
dentry = _dentry_hash_lookup(mnt, path);
|
||||
if (!dentry)
|
||||
{
|
||||
if (mnt->fs_ops->lookup)
|
||||
{
|
||||
DLOG(activate, "dentry");
|
||||
/* not in hash table, create it */
|
||||
DLOG(msg, "dentry", "dentry", DLOG_MSG, "dfs_dentry_create_rela(mnt=%s, path=%s)", mnt->fullpath, path);
|
||||
dentry = dfs_dentry_create_rela(mnt, (char*)path);
|
||||
if (dentry)
|
||||
{
|
||||
DLOG(msg, "dentry", mnt->fs_ops->name, DLOG_MSG, "vnode=fs_ops->lookup(dentry)");
|
||||
|
||||
if (dfs_is_mounted(mnt) == 0)
|
||||
{
|
||||
vnode = mnt->fs_ops->lookup(dentry);
|
||||
}
|
||||
|
||||
if (vnode)
|
||||
{
|
||||
DLOG(msg, mnt->fs_ops->name, "dentry", DLOG_MSG_RET, "return vnode");
|
||||
dentry->vnode = vnode; /* the refcount of created vnode is 1. no need to reference */
|
||||
dfs_file_lock();
|
||||
rt_list_insert_after(&hash_head.head[_dentry_hash(mnt, path)], &dentry->hashlist);
|
||||
dentry->flags |= DENTRY_IS_ADDHASH;
|
||||
dfs_file_unlock();
|
||||
|
||||
if (dentry->flags & (DENTRY_IS_ALLOCED | DENTRY_IS_ADDHASH)
|
||||
&& !(dentry->flags & DENTRY_IS_OPENED))
|
||||
{
|
||||
rt_err_t ret = dfs_file_lock();
|
||||
if (ret == RT_EOK)
|
||||
{
|
||||
dentry->flags |= DENTRY_IS_OPENED;
|
||||
dfs_file_unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DLOG(msg, mnt->fs_ops->name, "dentry", DLOG_MSG_RET, "no dentry");
|
||||
|
||||
DLOG(msg, "dentry", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry)");
|
||||
dfs_dentry_unref(dentry);
|
||||
dentry = RT_NULL;
|
||||
}
|
||||
}
|
||||
DLOG(deactivate, "dentry");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DLOG(note, "dentry", "found dentry");
|
||||
}
|
||||
dfs_file_unlock();
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the full path of a directory entry by combining mount point and relative path
|
||||
*
|
||||
* @param[in] dentry Pointer to the directory entry structure
|
||||
*
|
||||
* @return char* Newly allocated string containing full path, or NULL if allocation failed
|
||||
*
|
||||
* @note The caller is responsible for freeing the returned string using rt_free()
|
||||
* @note Handles path concatenation with or without additional '/' separator
|
||||
*/
|
||||
char* dfs_dentry_full_path(struct dfs_dentry* dentry)
|
||||
{
|
||||
char *path = NULL;
|
||||
|
||||
if (dentry && dentry->mnt)
|
||||
{
|
||||
int mnt_len = strlen(dentry->mnt->fullpath);
|
||||
int path_len = strlen(dentry->pathname);
|
||||
|
||||
path = (char *) rt_malloc(mnt_len + path_len + 3);
|
||||
if (path)
|
||||
{
|
||||
if (dentry->pathname[0] == '/' || dentry->mnt->fullpath[mnt_len - 1] == '/')
|
||||
{
|
||||
rt_snprintf(path, mnt_len + path_len + 2, "%s%s", dentry->mnt->fullpath,
|
||||
dentry->pathname);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_snprintf(path, mnt_len + path_len + 2, "%s/%s", dentry->mnt->fullpath,
|
||||
dentry->pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the parent directory path of a dentry by combining mount point and path
|
||||
*
|
||||
* @param[in] dentry Pointer to the directory entry structure
|
||||
*
|
||||
* @return char* Newly allocated string containing parent path, or NULL if allocation failed
|
||||
*
|
||||
* @note The caller is responsible for freeing the returned string using rt_free()
|
||||
* @note Handles both absolute and relative paths correctly
|
||||
* @note Returns mount point path if dentry is at root directory
|
||||
*/
|
||||
char* dfs_dentry_pathname(struct dfs_dentry* dentry)
|
||||
{
|
||||
char *pathname = RT_NULL;
|
||||
char *index = RT_NULL;
|
||||
|
||||
index = strrchr(dentry->pathname, '/');
|
||||
if (index)
|
||||
{
|
||||
int length = index - dentry->pathname;
|
||||
int path_length = strlen(dentry->mnt->fullpath) + length + 3;
|
||||
|
||||
pathname = (char*) rt_malloc(path_length);
|
||||
if (pathname)
|
||||
{
|
||||
if (dentry->pathname[0] == '/')
|
||||
{
|
||||
rt_snprintf(pathname, path_length - 1, "%s%.*s", dentry->mnt->fullpath,
|
||||
length, dentry->pathname);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_snprintf(pathname, path_length - 1, "%s/%.*s", dentry->mnt->fullpath,
|
||||
length, dentry->pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pathname = rt_strdup(dentry->mnt->fullpath);
|
||||
}
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate CRC32 checksum for the full path of a directory entry
|
||||
*
|
||||
* @param[in] dentry Pointer to the directory entry structure
|
||||
*
|
||||
* @return uint32_t CRC32 checksum value of the full path
|
||||
*
|
||||
* @note Uses standard CRC32 polynomial 0xEDB88320
|
||||
*/
|
||||
uint32_t dfs_dentry_full_path_crc32(struct dfs_dentry* dentry)
|
||||
{
|
||||
uint32_t crc32 = 0xFFFFFFFF;
|
||||
char *fullpath = dfs_dentry_full_path(dentry);
|
||||
if (fullpath)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while(fullpath[i] != '\0')
|
||||
{
|
||||
for (uint8_t b = 1; b; b <<= 1)
|
||||
{
|
||||
crc32 ^= (fullpath[i] & b) ? 1 : 0;
|
||||
crc32 = (crc32 & 1) ? crc32 >> 1 ^ 0xEDB88320 : crc32 >> 1;
|
||||
}
|
||||
i ++;
|
||||
}
|
||||
rt_free(fullpath);
|
||||
}
|
||||
return crc32;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the dentry hash table
|
||||
*
|
||||
* @return int Always returns 0 indicating success
|
||||
*
|
||||
* @note Initializes all hash buckets in the dentry hash table
|
||||
*/
|
||||
int dfs_dentry_init(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for(i = 0; i < DFS_DENTRY_HASH_NR; i++)
|
||||
{
|
||||
rt_list_init(&hash_head.head[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dump all directory entries in the hash table for debugging
|
||||
*
|
||||
* @param[in] argc Number of command line arguments (unused)
|
||||
* @param[in] argv Array of command line arguments (unused)
|
||||
*
|
||||
* @return int Always returns 0 indicating success
|
||||
*
|
||||
* @note Prints each dentry's full path, memory address and reference count
|
||||
*/
|
||||
int dfs_dentry_dump(int argc, char** argv)
|
||||
{
|
||||
int index = 0;
|
||||
struct dfs_dentry *entry = RT_NULL;
|
||||
|
||||
dfs_lock();
|
||||
for (index = 0; index < DFS_DENTRY_HASH_NR; index ++)
|
||||
{
|
||||
rt_list_for_each_entry(entry, &hash_head.head[index], hashlist)
|
||||
{
|
||||
printf("dentry: %s%s @ %p, ref_count = %zd\n", entry->mnt->fullpath, entry->pathname, entry, (size_t)rt_atomic_load(&entry->ref_count));
|
||||
}
|
||||
}
|
||||
dfs_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(dfs_dentry_dump, dentry_dump, dump dentry in the system);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,693 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "dfs_file.h"
|
||||
#include "dfs_dentry.h"
|
||||
#include "dfs_mnt.h"
|
||||
|
||||
#define DBG_TAG "dfs.mmap"
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#include <rtdbg.h>
|
||||
|
||||
#if defined(RT_USING_SMART) && defined(ARCH_MM_MMU) && defined(RT_USING_PAGECACHE)
|
||||
|
||||
#include "dfs_pcache.h"
|
||||
|
||||
#include <lwp.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <lwp_user_mm.h>
|
||||
#include <mm_aspace.h>
|
||||
#include <mm_fault.h>
|
||||
#include <mm_flag.h>
|
||||
#include <mm_page.h>
|
||||
#include <mmu.h>
|
||||
#include <page.h>
|
||||
#include <tlb.h>
|
||||
|
||||
static rt_mem_obj_t dfs_get_mem_obj(struct dfs_file *file);
|
||||
static void *dfs_mem_obj_get_file(rt_mem_obj_t mem_obj);
|
||||
|
||||
/**
|
||||
* @brief Perform memory mapping operation
|
||||
*
|
||||
* @param[in] lwp Pointer to the lightweight process structure
|
||||
* @param[in] map_vaddr Requested virtual address for mapping (may be NULL)
|
||||
* @param[in] map_size Size of the memory region to map
|
||||
* @param[in] attr Memory attributes for the mapping
|
||||
* @param[in] flags Memory mapping flags
|
||||
* @param[in] pgoffset Offset in pages from the start of the memory object
|
||||
* @param[in] data Pointer to the file descriptor to be mapped
|
||||
* @param[out] code Pointer to store the operation result code
|
||||
*
|
||||
* @return void* The mapped virtual address on success, NULL on failure
|
||||
*
|
||||
* @note This is a low-level mapping function that interacts directly with the address space manager.
|
||||
* The actual mapping is performed by rt_aspace_map().
|
||||
*/
|
||||
static void *_do_mmap(struct rt_lwp *lwp, void *map_vaddr, size_t map_size, size_t attr,
|
||||
mm_flag_t flags, off_t pgoffset, void *data, rt_err_t *code)
|
||||
{
|
||||
int ret = 0;
|
||||
void *vaddr = map_vaddr;
|
||||
rt_mem_obj_t mem_obj = dfs_get_mem_obj(data);
|
||||
|
||||
ret = rt_aspace_map(lwp->aspace, &vaddr, map_size,
|
||||
attr, flags, mem_obj, pgoffset);
|
||||
if (ret != RT_EOK)
|
||||
{
|
||||
vaddr = RT_NULL;
|
||||
LOG_E("failed to map %lx with size %lx with errno %d", map_vaddr,
|
||||
map_size, ret);
|
||||
}
|
||||
|
||||
if (code)
|
||||
{
|
||||
*code = ret;
|
||||
}
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Map data to user space address
|
||||
*
|
||||
* @param[in,out] mmap2 Pointer to memory mapping arguments structure
|
||||
* - Input: Contains mapping parameters (addr, length, etc.)
|
||||
* - Output: Contains the mapped address in ret field if successful
|
||||
* @param[in] data Pointer to the file descriptor to be mapped
|
||||
* @param[out] code Pointer to store the error code if mapping fails
|
||||
*
|
||||
* @return void* The mapped virtual address on success, NULL on failure
|
||||
*
|
||||
* @note This function performs page alignment on the mapping parameters and
|
||||
* converts user-space flags/attributes to kernel-space before mapping.
|
||||
*/
|
||||
static void *_map_data_to_uspace(struct dfs_mmap2_args *mmap2, void *data, rt_err_t *code)
|
||||
{
|
||||
size_t offset = 0;
|
||||
void *map_vaddr = mmap2->addr;
|
||||
size_t map_size = mmap2->length;
|
||||
struct rt_lwp *lwp = mmap2->lwp;
|
||||
rt_size_t k_attr;
|
||||
rt_size_t k_flags;
|
||||
|
||||
if (map_size)
|
||||
{
|
||||
offset = (size_t)map_vaddr & ARCH_PAGE_MASK;
|
||||
map_size += (offset + ARCH_PAGE_SIZE - 1);
|
||||
map_size &= ~ARCH_PAGE_MASK;
|
||||
map_vaddr = (void *)((size_t)map_vaddr & ~ARCH_PAGE_MASK);
|
||||
|
||||
k_flags = lwp_user_mm_flag_to_kernel(mmap2->flags);
|
||||
k_flags = MMF_CREATE(k_flags, mmap2->min_align_size);
|
||||
k_attr = lwp_user_mm_attr_to_kernel(mmap2->prot);
|
||||
|
||||
map_vaddr = _do_mmap(lwp, map_vaddr, map_size, k_attr, k_flags, mmap2->pgoffset, data, code);
|
||||
}
|
||||
|
||||
return map_vaddr;
|
||||
}
|
||||
|
||||
static void hint_free(rt_mm_va_hint_t hint)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle page fault for memory mapped file
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
* @param[in,out] msg Pointer to the page fault message structure
|
||||
* - Input: Contains fault information (fault_vaddr, etc.)
|
||||
* - Output: Contains response status and mapped page address
|
||||
*
|
||||
* @note This function is called when a page fault occurs in a memory mapped file region.
|
||||
* It attempts to map the faulting page and updates the response accordingly.
|
||||
*/
|
||||
static void on_page_fault(struct rt_varea *varea, struct rt_aspace_fault_msg *msg)
|
||||
{
|
||||
void *page;
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj);
|
||||
|
||||
if (file)
|
||||
{
|
||||
LOG_I("%s varea: %p", __func__, varea);
|
||||
LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x",
|
||||
varea->start, varea->size, varea->offset, varea->attr, varea->flag);
|
||||
LOG_I("fault vaddr: %p", msg->fault_vaddr);
|
||||
|
||||
if (file->dentry)
|
||||
{
|
||||
LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname);
|
||||
}
|
||||
|
||||
page = dfs_aspace_mmap(file, varea, msg->fault_vaddr);
|
||||
if (page)
|
||||
{
|
||||
msg->response.status = MM_FAULT_STATUS_OK_MAPPED;
|
||||
msg->response.size = ARCH_PAGE_SIZE;
|
||||
msg->response.vaddr = page;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("%s varea %p mmap failed at vaddr %p", __func__, varea, msg->fault_vaddr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle virtual memory area opening event
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
*
|
||||
* @note This function is called when a virtual memory area is opened.
|
||||
* It increments the reference count of the associated file and
|
||||
* initializes varea->data to NULL.
|
||||
*/
|
||||
static void on_varea_open(struct rt_varea *varea)
|
||||
{
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj);
|
||||
varea->data = RT_NULL;
|
||||
rt_atomic_add(&(file->ref_count), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle virtual memory area closing event
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
*
|
||||
* @note This function is called when a virtual memory area is closed.
|
||||
* It performs cleanup operations including:
|
||||
* - Unmapping the file from memory
|
||||
* - Decrementing file reference count
|
||||
* - Closing and destroying file if reference count reaches zero
|
||||
*/
|
||||
static void on_varea_close(struct rt_varea *varea)
|
||||
{
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj);
|
||||
|
||||
if (file)
|
||||
{
|
||||
LOG_I("%s varea: %p", __func__, varea);
|
||||
LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x",
|
||||
varea->start, varea->size, varea->offset, varea->attr, varea->flag);
|
||||
|
||||
if (file->dentry)
|
||||
{
|
||||
LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname);
|
||||
}
|
||||
|
||||
dfs_aspace_unmap(file, varea);
|
||||
dfs_file_lock();
|
||||
if (rt_atomic_load(&(file->ref_count)) == 1)
|
||||
{
|
||||
dfs_file_close(file);
|
||||
dfs_file_destroy(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_atomic_sub(&(file->ref_count), 1);
|
||||
}
|
||||
dfs_file_unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the name of the memory mapped file
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
*
|
||||
* @return const char* The name of the mapped file if available,
|
||||
* otherwise returns "file-mapper" as default name
|
||||
*
|
||||
* @note This function retrieves the file name from the dentry structure
|
||||
* associated with the memory mapped file.
|
||||
*/
|
||||
static const char *get_name(rt_varea_t varea)
|
||||
{
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj);
|
||||
|
||||
return (file && file->dentry) ? file->dentry->pathname : "file-mapper";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read data from memory mapped file page
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
* @param[in,out] msg Pointer to the I/O message structure
|
||||
* - Input: Contains read request information
|
||||
* - Output: Contains response status and read data
|
||||
*
|
||||
* @note This function handles page read operations for memory mapped files.
|
||||
* If the read size is less than page size, it zero-fills the remaining space.
|
||||
*/
|
||||
void page_read(struct rt_varea *varea, struct rt_aspace_io_msg *msg)
|
||||
{
|
||||
rt_ubase_t ret;
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj);
|
||||
|
||||
if (file)
|
||||
{
|
||||
LOG_I("%s varea: %p", __func__, varea);
|
||||
LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x",
|
||||
varea->start, varea->size, varea->offset, varea->attr, varea->flag);
|
||||
|
||||
ret = dfs_aspace_mmap_read(file, varea, msg);
|
||||
if (ret >= 0)
|
||||
{
|
||||
msg->response.status = MM_FAULT_STATUS_OK;
|
||||
if (ret < ARCH_PAGE_SIZE)
|
||||
{
|
||||
memset((char *)msg->buffer_vaddr + ret, 0, ARCH_PAGE_SIZE - ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write data to memory mapped file page
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
* @param[in,out] msg Pointer to the I/O message structure
|
||||
* - Input: Contains write request information
|
||||
* - Output: Contains response status and write result
|
||||
*
|
||||
* @note This function handles page write operations for memory mapped files.
|
||||
* If the write size is less than page size, it zero-fills the remaining space.
|
||||
*/
|
||||
void page_write(struct rt_varea *varea, struct rt_aspace_io_msg *msg)
|
||||
{
|
||||
rt_ubase_t ret;
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj);
|
||||
|
||||
if (file)
|
||||
{
|
||||
LOG_I("%s varea: %p", __func__, varea);
|
||||
LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x",
|
||||
varea->start, varea->size, varea->offset, varea->attr, varea->flag);
|
||||
|
||||
ret = dfs_aspace_mmap_write(file, varea, msg);
|
||||
if (ret > 0)
|
||||
{
|
||||
msg->response.status = MM_FAULT_STATUS_OK;
|
||||
if (ret < ARCH_PAGE_SIZE)
|
||||
{
|
||||
memset((char *)msg->buffer_vaddr + ret, 0, ARCH_PAGE_SIZE - ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unmap pages from virtual memory area
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
* @param[in] rm_start Starting address of the range to unmap (must be page aligned)
|
||||
* @param[in] rm_end Ending address of the range to unmap (must be page aligned)
|
||||
*
|
||||
* @return rt_err_t Error code:
|
||||
* - RT_EOK: Success
|
||||
* - -RT_ERROR: Failure (varea not associated with a file)
|
||||
*
|
||||
* @note This function performs page-by-page unmapping.
|
||||
* Both rm_start and rm_end must be page-aligned (checked by RT_ASSERT).
|
||||
*/
|
||||
static rt_err_t unmap_pages(rt_varea_t varea, void *rm_start, void *rm_end)
|
||||
{
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj);
|
||||
|
||||
if (file)
|
||||
{
|
||||
LOG_I("%s varea: %p start: %p end: %p", __func__, varea, rm_start, rm_end);
|
||||
|
||||
RT_ASSERT(!((rt_ubase_t)rm_start & ARCH_PAGE_MASK));
|
||||
RT_ASSERT(!((rt_ubase_t)rm_end & ARCH_PAGE_MASK));
|
||||
while (rm_start != rm_end)
|
||||
{
|
||||
dfs_aspace_page_unmap(file, varea, rm_start);
|
||||
rm_start += ARCH_PAGE_SIZE;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start);
|
||||
}
|
||||
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle virtual memory area shrinking operation
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
* @param[in] new_vaddr New starting address after shrinking
|
||||
* @param[in] size New size of the virtual memory area
|
||||
*
|
||||
* @return rt_err_t Error code:
|
||||
* - RT_EOK: Success
|
||||
* - Other errors from unmap_pages()
|
||||
*
|
||||
* @note This function determines the range of pages to unmap based on whether
|
||||
* the varea is shrinking from the start or end.
|
||||
*/
|
||||
rt_err_t on_varea_shrink(struct rt_varea *varea, void *new_vaddr, rt_size_t size)
|
||||
{
|
||||
char *varea_start = varea->start;
|
||||
void *rm_start;
|
||||
void *rm_end;
|
||||
|
||||
LOG_I("%s varea: %p", __func__, varea);
|
||||
LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x",
|
||||
varea->start, varea->size, varea->offset, varea->attr, varea->flag);
|
||||
LOG_I("new_vaddr: %p size: %p", new_vaddr, size);
|
||||
|
||||
if (varea_start == (char *)new_vaddr)
|
||||
{
|
||||
rm_start = varea_start + size;
|
||||
rm_end = varea_start + varea->size;
|
||||
}
|
||||
else
|
||||
{
|
||||
rm_start = varea_start;
|
||||
rm_end = new_vaddr;
|
||||
}
|
||||
|
||||
return unmap_pages(varea, rm_start, rm_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle virtual memory area expansion operation
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
* @param[in] new_vaddr New starting address after expansion
|
||||
* @param[in] size New size of the expanded virtual memory area
|
||||
*
|
||||
* @return rt_err_t returns RT_EOK (success).
|
||||
*
|
||||
* @note This function is currently not implemented.
|
||||
*/
|
||||
rt_err_t on_varea_expand(struct rt_varea *varea, void *new_vaddr, rt_size_t size)
|
||||
{
|
||||
LOG_I("%s varea: %p", __func__, varea);
|
||||
LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x",
|
||||
varea->start, varea->size, varea->offset, varea->attr, varea->flag);
|
||||
LOG_I("new_vaddr: %p size: %p", new_vaddr, size);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle virtual memory area splitting operation
|
||||
*
|
||||
* @param[in] existed Pointer to the existing virtual memory area to be split
|
||||
* @param[in] unmap_start Starting address of the range to unmap
|
||||
* @param[in] unmap_len Length of the range to unmap
|
||||
* @param[in,out] subset Pointer to the new subset virtual memory area
|
||||
* - Input: Contains new varea parameters
|
||||
* - Output: Contains initialized varea after splitting
|
||||
*
|
||||
* @return rt_err_t Error code:
|
||||
* - RT_EOK: Success
|
||||
* - -RT_ERROR: Failure (varea not associated with a file)
|
||||
*
|
||||
* @note This function splits an existing virtual memory area into two parts.
|
||||
* It unmaps the specified range and initializes the new subset area.
|
||||
*/
|
||||
rt_err_t on_varea_split(struct rt_varea *existed, void *unmap_start, rt_size_t unmap_len, struct rt_varea *subset)
|
||||
{
|
||||
rt_err_t rc;
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(existed->mem_obj);
|
||||
|
||||
if (file)
|
||||
{
|
||||
LOG_I("%s varea: %p", __func__, existed);
|
||||
LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x",
|
||||
existed->start, existed->size, existed->offset, existed->attr, existed->flag);
|
||||
LOG_I("unmap_start: %p unmap_len: %p", unmap_start, unmap_len);
|
||||
|
||||
if (file->dentry)
|
||||
{
|
||||
LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname);
|
||||
}
|
||||
|
||||
rc = unmap_pages(existed, unmap_start, (char *)unmap_start + unmap_len);
|
||||
if (!rc)
|
||||
{
|
||||
rc = unmap_pages(existed, subset->start, (char *)subset->start + subset->size);
|
||||
if (!rc)
|
||||
on_varea_open(subset);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("%s varea %p not a file, vaddr %p", __func__, existed, existed->start);
|
||||
}
|
||||
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle virtual memory area merging operation
|
||||
*
|
||||
* @param[in] merge_to Pointer to the target virtual memory area that will receive the merge
|
||||
* @param[in] merge_from Pointer to the source virtual memory area to be merged
|
||||
*
|
||||
* @return rt_err_t Error code:
|
||||
* - RT_EOK: Success
|
||||
* - -RT_ERROR: Failure (varea not associated with a file)
|
||||
*/
|
||||
rt_err_t on_varea_merge(struct rt_varea *merge_to, struct rt_varea *merge_from)
|
||||
{
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(merge_from->mem_obj);
|
||||
|
||||
if (file)
|
||||
{
|
||||
LOG_I("%s varea: %p", __func__, merge_from);
|
||||
LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x",
|
||||
merge_from->start, merge_from->size, merge_from->offset, merge_from->attr, merge_from->flag);
|
||||
|
||||
if (file->dentry)
|
||||
{
|
||||
LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname);
|
||||
}
|
||||
|
||||
dfs_aspace_unmap(file, merge_from);
|
||||
on_varea_close(merge_from);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("%s varea %p not a file, vaddr %p", __func__, merge_from, merge_from->start);
|
||||
}
|
||||
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle virtual memory area remapping operation
|
||||
*
|
||||
* @param[in] varea Pointer to the virtual memory area structure
|
||||
* @param[in] new_size New size of the virtual memory area after remapping
|
||||
* @param[in] flags Remapping flags (e.g., MREMAP_MAYMOVE)
|
||||
* @param[in] new_address New starting address after remapping (optional)
|
||||
*
|
||||
* @return void* Pointer to the new virtual memory area after remapping
|
||||
* - Returns RT_NULL if remapping fails
|
||||
*
|
||||
* @note This function remaps a virtual memory area to a new address or size.
|
||||
* It currently supports the MREMAP_MAYMOVE flag.
|
||||
*/
|
||||
void *on_varea_mremap(struct rt_varea *varea, rt_size_t new_size, int flags, void *new_address)
|
||||
{
|
||||
void *vaddr = RT_NULL;
|
||||
struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj);
|
||||
|
||||
#ifndef MREMAP_MAYMOVE
|
||||
#define MREMAP_MAYMOVE 1
|
||||
#endif
|
||||
|
||||
if (file && flags == MREMAP_MAYMOVE)
|
||||
{
|
||||
int ret;
|
||||
rt_mem_obj_t mem_obj = dfs_get_mem_obj(file);
|
||||
|
||||
vaddr = new_address ? new_address : varea->start;
|
||||
new_size = (new_size + ARCH_PAGE_SIZE - 1);
|
||||
new_size &= ~ARCH_PAGE_MASK;
|
||||
ret = rt_aspace_map(varea->aspace, &vaddr, new_size, varea->attr, varea->flag, mem_obj, varea->offset);
|
||||
if (ret != RT_EOK)
|
||||
{
|
||||
LOG_E("failed to map %lx with size %lx with errno %d", vaddr, new_size, ret);
|
||||
vaddr = RT_NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_I("old: %p size: %p new: %p size: %p", varea->start, varea->size, vaddr, new_size);
|
||||
}
|
||||
}
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Memory object operations structure
|
||||
*
|
||||
* Defines function pointers for various virtual memory area (varea) operations,
|
||||
* including memory management, page fault handling, and lifecycle callbacks.
|
||||
*/
|
||||
static struct rt_mem_obj _mem_obj =
|
||||
{
|
||||
.hint_free = hint_free, /* Free memory hint function */
|
||||
.on_page_fault = on_page_fault, /* Page fault handler */
|
||||
.on_varea_open = on_varea_open, /* Varea open callback */
|
||||
.on_varea_close = on_varea_close, /* Varea close callback */
|
||||
.get_name = get_name, /* Get mapped file name */
|
||||
|
||||
.page_read = page_read, /* Page read operation */
|
||||
.page_write = page_write, /* Page write operation */
|
||||
|
||||
.on_varea_shrink = on_varea_shrink, /* Varea shrink handler */
|
||||
.on_varea_expand = on_varea_expand, /* Varea expand handler */
|
||||
.on_varea_split = on_varea_split, /* Varea split handler */
|
||||
.on_varea_merge = on_varea_merge, /* Varea merge handler */
|
||||
|
||||
.on_varea_mremap = on_varea_mremap, /* Varea remap handler */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief DFS memory object structure
|
||||
*
|
||||
* Contains a standard memory object and an associated file pointer,
|
||||
* used to maintain the relationship between memory mappings and files.
|
||||
*/
|
||||
struct dfs_mem_obj {
|
||||
struct rt_mem_obj mem_obj; /* Base memory object */
|
||||
void *file; /* Associated file pointer */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get or create memory mapping object for a file
|
||||
*
|
||||
* @param[in] file Pointer to the file descriptor structure
|
||||
*
|
||||
* @return rt_mem_obj_t Memory mapping object associated with the file
|
||||
* - Returns existing object if already created
|
||||
* - Creates and initializes new object if not exists
|
||||
*/
|
||||
static rt_mem_obj_t dfs_get_mem_obj(struct dfs_file *file)
|
||||
{
|
||||
rt_mem_obj_t mobj = file->mmap_context;
|
||||
if (!mobj)
|
||||
{
|
||||
struct dfs_mem_obj *dfs_mobj;
|
||||
dfs_file_lock();
|
||||
dfs_mobj = rt_malloc(sizeof(*dfs_mobj));
|
||||
if (dfs_mobj)
|
||||
{
|
||||
dfs_mobj->file = file;
|
||||
mobj = &dfs_mobj->mem_obj;
|
||||
memcpy(mobj, &_mem_obj, sizeof(*mobj));
|
||||
file->mmap_context = mobj;
|
||||
}
|
||||
dfs_file_unlock();
|
||||
}
|
||||
return mobj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the file descriptor from memory mapping object
|
||||
*
|
||||
* @param[in] mem_obj Pointer to the memory mapping object
|
||||
*
|
||||
* @return void* Pointer to the associated file descriptor structure
|
||||
*
|
||||
* @note This function uses rt_container_of macro to get the containing
|
||||
* dfs_mem_obj structure from its mem_obj member.
|
||||
*/
|
||||
static void *dfs_mem_obj_get_file(rt_mem_obj_t mem_obj)
|
||||
{
|
||||
struct dfs_mem_obj *dfs_mobj;
|
||||
dfs_mobj = rt_container_of(mem_obj, struct dfs_mem_obj, mem_obj);
|
||||
return dfs_mobj->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Map a file into memory
|
||||
*
|
||||
* @param[in] file Pointer to the file descriptor structure
|
||||
* @param[in,out] mmap2 Pointer to memory mapping arguments structure
|
||||
* - Input: Contains mapping parameters (addr, length, etc.)
|
||||
* - Output: Contains the mapped address in ret field if successful
|
||||
*
|
||||
* @return int Error code:
|
||||
* - EINVAL: Invalid parameters
|
||||
* - Other errors from underlying mapping operations
|
||||
*
|
||||
* @note This function creates a virtual address area in user space (lwp) for the file mapping.
|
||||
* The actual mapping is performed by _map_data_to_uspace().
|
||||
*/
|
||||
int dfs_file_mmap(struct dfs_file *file, struct dfs_mmap2_args *mmap2)
|
||||
{
|
||||
rt_err_t ret = -EINVAL;
|
||||
void *map_vaddr;
|
||||
|
||||
LOG_I("mmap2 args addr: %p length: 0x%x prot: %d flags: 0x%x pgoffset: 0x%x",
|
||||
mmap2->addr, mmap2->length, mmap2->prot, mmap2->flags, mmap2->pgoffset);
|
||||
if (file && file->vnode)
|
||||
{
|
||||
if (file->vnode->aspace)
|
||||
{
|
||||
/* create a va area in user space (lwp) */
|
||||
map_vaddr = _map_data_to_uspace(mmap2, file, &ret);
|
||||
if (map_vaddr)
|
||||
{
|
||||
mmap2->ret = map_vaddr;
|
||||
LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("File mapping is not supported, file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
int dfs_file_mmap(struct dfs_file *file, struct dfs_mmap2_args *mmap2)
|
||||
{
|
||||
LOG_E("File mapping support is not enabled, file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname);
|
||||
LOG_E("mmap2 args addr: %p length: 0x%x prot: %d flags: 0x%x pgoffset: 0x%x",
|
||||
mmap2->addr, mmap2->length, mmap2->prot, mmap2->flags, mmap2->pgoffset);
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,726 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2025 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 group_fs_api
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* @brief Find a filesystem type by name
|
||||
*
|
||||
* This function searches the global filesystem type list for a filesystem
|
||||
* matching the given name. It returns a pointer to the pointer that holds
|
||||
* the matching filesystem type (or the end-of-list pointer if not found).
|
||||
*
|
||||
* @param[in] name The name of the filesystem type to find
|
||||
* @return struct dfs_filesystem_type** Pointer to the pointer containing
|
||||
* the matching filesystem type, or the end-of-list pointer if not found
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the list of registered filesystem types
|
||||
*
|
||||
* This function returns a pointer to the head of the global filesystem type list.
|
||||
*
|
||||
* @return struct dfs_filesystem_type* Pointer to the head of the filesystem type list
|
||||
*/
|
||||
struct dfs_filesystem_type *dfs_filesystems(void)
|
||||
{
|
||||
return file_systems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a filesystem type
|
||||
*
|
||||
* This function registers a new filesystem type with the global filesystem type list.
|
||||
*
|
||||
* @param[in] fs Pointer to the filesystem type to register
|
||||
* @return int 0 on success, or a negative error code on failure
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister a filesystem type
|
||||
*
|
||||
* This function unregisters a filesystem type from the global filesystem type list.
|
||||
*
|
||||
* @param[in] fs Pointer to the filesystem type to unregister
|
||||
* @return int 0 on success, or a negative error code on failure
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
#define REMNT_UNSUPP_FLAGS (~(MS_REMOUNT | MS_RMT_MASK)) /* remount unsupported flags */
|
||||
|
||||
/**
|
||||
* @brief Remount a filesystem
|
||||
*
|
||||
* This function remounts a filesystem at the specified path with the given flags.
|
||||
*
|
||||
* @param[in] path The path of the filesystem to remount
|
||||
* @param[in] flags The remount flags (see MS_REMOUNT and MS_RMT_MASK)
|
||||
* @param[in] data Pointer to additional data required for remounting
|
||||
* @return int 0 on success, or a negative error code on failure
|
||||
*/
|
||||
int dfs_remount(const char *path, rt_ubase_t flags, void *data)
|
||||
{
|
||||
int rc = 0;
|
||||
char *fullpath = RT_NULL;
|
||||
struct dfs_mnt *mnt = RT_NULL;
|
||||
|
||||
if (flags & REMNT_UNSUPP_FLAGS)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fullpath = dfs_normalize_path(RT_NULL, path);
|
||||
if (!fullpath)
|
||||
{
|
||||
rc = -ENOENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
DLOG(msg, "dfs", "mnt", DLOG_MSG, "mnt = dfs_mnt_lookup(%s)", fullpath);
|
||||
mnt = dfs_mnt_lookup(fullpath);
|
||||
if (mnt)
|
||||
{
|
||||
dfs_lock();
|
||||
dfs_mnt_setflags(mnt, flags);
|
||||
dfs_unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat buf = {0};
|
||||
if (dfs_file_stat(fullpath, &buf) == 0 && S_ISBLK(buf.st_mode))
|
||||
{
|
||||
/* path was not already mounted on target */
|
||||
rc = -EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* path is not a directory */
|
||||
rc = -ENOTDIR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* parent(mount path)
|
||||
* mnt_parent <- - - - - - - +
|
||||
* | |
|
||||
* |- mnt_child <- - - - - -+ (1 refcount)
|
||||
* | |
|
||||
* |- parent - - + (1 refcount)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Mount a filesystem at the specified path
|
||||
*
|
||||
* This function mounts a filesystem of the specified type at the given path with optional device.
|
||||
* It handles both root filesystem mounting and regular filesystem mounting scenarios.
|
||||
*
|
||||
* @param[in] device_name The name of the device to mount (optional)
|
||||
* @param[in] path The path of the mount point
|
||||
* @param[in] filesystemtype The type of the filesystem to mount
|
||||
* @param[in] rwflag The read/write flags (see MS_RDONLY, MS_RDWR, etc.)
|
||||
* @param[in] data Pointer to additional data required for mounting
|
||||
*
|
||||
* @return int RT_EOK on success, negative error code on failure:
|
||||
* - EPERM: Path normalization failed or mount operation failed
|
||||
* - ENODEV: Filesystem type not found or device not available
|
||||
* - ENOMEM: Memory allocation failure
|
||||
* - EIO: Filesystem lacks mount method
|
||||
* - ENOTDIR: Mount point doesn't exist
|
||||
* - EEXIST: Mount point already mounted
|
||||
*
|
||||
* @note Special handling for root filesystem ("/")
|
||||
* @note Automatic reference counting management for mount points
|
||||
*/
|
||||
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);
|
||||
|
||||
/* normalize the mount path */
|
||||
if (type)
|
||||
{
|
||||
fullpath = dfs_normalize_path(RT_NULL, path);
|
||||
if (!fullpath)
|
||||
{
|
||||
rt_set_errno(EPERM);
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_set_errno(ENODEV);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
/* Main mounting procedure */
|
||||
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);
|
||||
|
||||
/* Check device requirements */
|
||||
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); /* Find parent mount point */
|
||||
|
||||
/* Handle root filesystem mounting */
|
||||
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. */
|
||||
/* Create root filesystem mount point */
|
||||
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");
|
||||
|
||||
/* Mark as mounted and insert into mount table */
|
||||
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)) /* Handle regular filesystem mounting */
|
||||
{
|
||||
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); /* Find mount point directory entry */
|
||||
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); /* Create child mount point */
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unmount a filesystem from the specified path
|
||||
*
|
||||
* This function unmounts a filesystem from the given path. It performs the following operations:
|
||||
* 1. Normalizes the target path
|
||||
* 2. Looks up the mount point
|
||||
* 3. Checks if the filesystem can be safely unmounted
|
||||
* 4. Performs cleanup operations if unmounting is successful
|
||||
*
|
||||
* @param[in] specialfile The path of the filesystem to unmount
|
||||
* @param[in] flags Unmount flags (MNT_FORCE for forced unmount)
|
||||
*
|
||||
* @return int RT_EOK on success, negative error code on failure:
|
||||
* - EBUSY: Filesystem is busy (in use or has child mounts)
|
||||
* - EINVAL: Path is not a mount point
|
||||
* - ENOTDIR: Invalid path format
|
||||
*
|
||||
* @note Forced unmount (MNT_FORCE) can unmount even if reference count > 1
|
||||
* @note Automatically handles page cache cleanup if RT_USING_PAGECACHE is enabled
|
||||
* @note The function will fail if:
|
||||
* - The mount point is locked (MNT_IS_LOCKED)
|
||||
* - There are child mounts present
|
||||
* - Reference count > 1 and MNT_FORCE not specified
|
||||
*/
|
||||
int dfs_umount(const char *specialfile, int flags)
|
||||
{
|
||||
int ret = -1;
|
||||
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_base_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_I("the file system is busy!");
|
||||
ret = -EBUSY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_I("the path:%s is not a mountpoint!", fullpath);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_I("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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a mount point is mounted
|
||||
*
|
||||
* This function checks if the given mount point is mounted. It returns 0 if the mount point is mounted,
|
||||
* and -1 otherwise.
|
||||
*
|
||||
* @param[in] mnt The mount point to check
|
||||
*
|
||||
* @return int 0 if mounted, -1 otherwise
|
||||
*/
|
||||
int dfs_is_mounted(struct dfs_mnt *mnt)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (mnt && !(mnt->flags & MNT_IS_MOUNTED))
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a filesystem on the specified device
|
||||
*
|
||||
* This function creates a filesystem of the specified type on the given device.
|
||||
* It performs the following operations:
|
||||
* 1. Looks up the filesystem type
|
||||
* 2. Validates device requirements
|
||||
* 3. Calls the filesystem-specific mkfs operation
|
||||
* 4. Handles page cache cleanup if successful (when RT_USING_PAGECACHE is enabled)
|
||||
*
|
||||
* @param[in] fs_name Name of the filesystem type to create (e.g., "elm", "romfs")
|
||||
* @param[in] device_name Name of the device to create filesystem on (optional)
|
||||
*
|
||||
* @return int RT_EOK on success, negative error code on failure:
|
||||
* - RT_ERROR: General error
|
||||
* - ENODEV: Filesystem type not found or device not available
|
||||
*
|
||||
* @note For filesystems that don't require a device (FS_NEED_DEVICE not set),
|
||||
* the device_name parameter can be NULL
|
||||
* @note Automatically unmounts any existing filesystem on the device
|
||||
* when RT_USING_PAGECACHE is enabled
|
||||
* @note The function will fail if:
|
||||
* - The filesystem type is not found
|
||||
* - Device is required but not found
|
||||
* - The filesystem doesn't implement mkfs operation
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get filesystem statistics for the specified path
|
||||
*
|
||||
* This function retrieves filesystem statistics (like total/available space)
|
||||
* for the filesystem containing the given path. It performs the following operations:
|
||||
* 1. Normalizes the input path
|
||||
* 2. Looks up the mount point for the path
|
||||
* 3. Calls the filesystem-specific statfs operation if available
|
||||
*
|
||||
* @param[in] path The path to query filesystem statistics for
|
||||
* @param[out] buffer Pointer to statfs structure to store the results
|
||||
*
|
||||
* @return int RT_EOK on success, negative error code on failure:
|
||||
* - RT_ERROR: General error (invalid path or filesystem not found)
|
||||
*
|
||||
* @note The function will fail if:
|
||||
* - The path cannot be normalized
|
||||
* - No mount point is found for the path
|
||||
* - The filesystem doesn't implement statfs operation
|
||||
* - The filesystem is not currently mounted
|
||||
* @note The buffer parameter must point to valid memory allocated by the caller
|
||||
*/
|
||||
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[in] 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[out] part the returned partition structure.
|
||||
* @param[in] buf the buffer contains partition table.
|
||||
* @param[in] 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;
|
||||
}
|
||||
|
||||
/* @} */
|
||||
@@ -0,0 +1,686 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#ifndef DFS_PRIVATE_H__
|
||||
#define DFS_PRIVATE_H__
|
||||
|
||||
#include <dfs.h>
|
||||
|
||||
#define NO_WORKING_DIR "system does not support working directory\n"
|
||||
|
||||
extern char working_directory[];
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include <dfs_seq_file.h>
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
#define DBG_TAG "DFS.seq"
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#include <rtdbg.h>
|
||||
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Handle buffer overflow condition in sequence file
|
||||
*
|
||||
* @param[in,out] seq Pointer to sequence file structure
|
||||
*
|
||||
* @details Sets the count to size to indicate buffer is full
|
||||
*/
|
||||
static void dfs_seq_overflow(struct dfs_seq_file *seq)
|
||||
{
|
||||
seq->count = seq->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate memory for sequence file operations
|
||||
*
|
||||
* @param[in] size Size of memory to allocate in bytes
|
||||
*
|
||||
* @return void* Pointer to allocated memory, or NULL if allocation fails
|
||||
*/
|
||||
static void *dfs_seq_alloc(unsigned long size)
|
||||
{
|
||||
return rt_calloc(1, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize and open a sequence file
|
||||
*
|
||||
* @param[in] file Pointer to the file structure to be initialized
|
||||
* @param[in] ops Pointer to sequence operations structure containing callback functions
|
||||
*
|
||||
* @return int 0 on success, negative error code on failure:
|
||||
* -EINVAL if ops is NULL
|
||||
* -ENOMEM if memory allocation fails
|
||||
*/
|
||||
int dfs_seq_open(struct dfs_file *file, const struct dfs_seq_ops *ops)
|
||||
{
|
||||
struct dfs_seq_file *seq;
|
||||
|
||||
if (!ops)
|
||||
{
|
||||
LOG_E("dfs_seq_open: ops = null, pathname: %s\n", file->dentry->pathname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (file->data)
|
||||
{
|
||||
LOG_W("dfs_seq_open: file->data != null\n");
|
||||
}
|
||||
|
||||
seq = rt_calloc(1, sizeof(struct dfs_seq_file));
|
||||
if (!seq)
|
||||
return -ENOMEM;
|
||||
|
||||
file->data = seq;
|
||||
|
||||
rt_mutex_init(&seq->lock, "dfs_seq", RT_IPC_FLAG_PRIO);
|
||||
|
||||
seq->ops = ops;
|
||||
seq->file = file;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Traverse sequence file data with specified offset
|
||||
*
|
||||
* This function traverses the sequence file data starting from the specified offset.
|
||||
* It handles buffer overflow conditions by dynamically resizing the buffer when needed.
|
||||
*
|
||||
* @param[in,out] seq Pointer to sequence file structure
|
||||
* @param[in] offset Position to start traversing from
|
||||
*
|
||||
* @return int 0 on success, negative error code on failure:
|
||||
* -ENOMEM if memory allocation fails
|
||||
* -EAGAIN if buffer needs to be resized
|
||||
*
|
||||
* @note Data output loop: start() -> show() -> next() -> show() -> ... -> next() -> stop()
|
||||
*/
|
||||
static int dfs_seq_traverse(struct dfs_seq_file *seq, off_t offset)
|
||||
{
|
||||
off_t pos = 0;
|
||||
int error = 0;
|
||||
void *p;
|
||||
|
||||
seq->index = 0;
|
||||
seq->count = seq->from = 0;
|
||||
if (!offset)
|
||||
return 0;
|
||||
|
||||
if (!seq->buf)
|
||||
{
|
||||
seq->buf = dfs_seq_alloc(seq->size = PAGE_SIZE);
|
||||
if (!seq->buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
p = seq->ops->start(seq, &seq->index);
|
||||
while (p)
|
||||
{
|
||||
error = seq->ops->show(seq, p);
|
||||
if (error < 0)
|
||||
break;
|
||||
if (error)
|
||||
{
|
||||
error = 0;
|
||||
seq->count = 0;
|
||||
}
|
||||
if (dfs_seq_is_full(seq))
|
||||
goto Eoverflow;
|
||||
|
||||
p = seq->ops->next(seq, p, &seq->index);
|
||||
if (pos + seq->count > offset)
|
||||
{
|
||||
seq->from = offset - pos;
|
||||
seq->count -= seq->from;
|
||||
break;
|
||||
}
|
||||
pos += seq->count;
|
||||
seq->count = 0;
|
||||
if (pos == offset)
|
||||
break;
|
||||
}
|
||||
seq->ops->stop(seq, p);
|
||||
return error;
|
||||
|
||||
Eoverflow:
|
||||
seq->ops->stop(seq, p);
|
||||
rt_free(seq->buf);
|
||||
seq->count = 0;
|
||||
seq->buf = dfs_seq_alloc(seq->size <<= 1);
|
||||
return !seq->buf ? -ENOMEM : -EAGAIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read data from sequence file
|
||||
*
|
||||
* @param[in] file Pointer to the file structure
|
||||
* @param[out] buf Buffer to store the read data
|
||||
* @param[in] size Size of the buffer in bytes
|
||||
* @param[in,out] pos Current file position (updated after read)
|
||||
*
|
||||
* @return ssize_t Number of bytes read on success, negative error code on failure:
|
||||
* -EFAULT if buffer error occurs
|
||||
* -ENOMEM if memory allocation fails
|
||||
* 0 if size is 0
|
||||
*
|
||||
* @details This function implements the core sequence file reading logic with following steps:
|
||||
* 1. Reset iterator if reading from start
|
||||
* 2. Synchronize position if needed
|
||||
* 3. Allocate buffer if not exists
|
||||
* 4. Copy remaining data from previous read
|
||||
* 5. Start iteration and fill buffer with new data
|
||||
* 6. Handle buffer overflow by doubling size
|
||||
* 7. Copy data to user buffer and update positions
|
||||
*/
|
||||
ssize_t dfs_seq_read(struct dfs_file *file, void *buf, size_t size, off_t *pos)
|
||||
{
|
||||
struct dfs_seq_file *seq = file->data;
|
||||
size_t copied = 0;
|
||||
size_t n;
|
||||
void *p;
|
||||
int err = 0;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
rt_mutex_take(&seq->lock, RT_WAITING_FOREVER);
|
||||
|
||||
/*
|
||||
* if request is to read from zero offset, reset iterator to first
|
||||
* record as it might have been already advanced by previous requests
|
||||
*/
|
||||
if (*pos == 0)
|
||||
{
|
||||
seq->index = 0;
|
||||
seq->count = 0;
|
||||
}
|
||||
|
||||
/* Don't assume ki_pos is where we left it */
|
||||
if (*pos != seq->read_pos)
|
||||
{
|
||||
while ((err = dfs_seq_traverse(seq, *pos)) == -EAGAIN)
|
||||
;
|
||||
if (err)
|
||||
{
|
||||
/* With prejudice... */
|
||||
seq->read_pos = 0;
|
||||
seq->index = 0;
|
||||
seq->count = 0;
|
||||
goto Done;
|
||||
}
|
||||
else
|
||||
{
|
||||
seq->read_pos = *pos;
|
||||
}
|
||||
}
|
||||
|
||||
/* grab buffer if we didn't have one */
|
||||
if (!seq->buf)
|
||||
{
|
||||
seq->buf = dfs_seq_alloc(seq->size = PAGE_SIZE);
|
||||
if (!seq->buf)
|
||||
goto Enomem;
|
||||
}
|
||||
/* something left in the buffer - copy it out first */
|
||||
if (seq->count)
|
||||
{
|
||||
n = seq->count > size ? size : seq->count;
|
||||
rt_memcpy((char *)buf + copied, seq->buf + seq->from, n);
|
||||
size -= n;
|
||||
seq->count -= n;
|
||||
seq->from += n;
|
||||
copied += n;
|
||||
if (seq->count) /* hadn't managed to copy everything */
|
||||
goto Done;
|
||||
}
|
||||
/* get a non-empty record in the buffer */
|
||||
seq->from = 0;
|
||||
p = seq->ops->start(seq, &seq->index);
|
||||
while (p)
|
||||
{
|
||||
err = seq->ops->show(seq, p);
|
||||
if (err < 0) /* hard error */
|
||||
break;
|
||||
if (err) /* ->show() says "skip it" */
|
||||
seq->count = 0;
|
||||
if (!seq->count)
|
||||
{ /* empty record */
|
||||
p = seq->ops->next(seq, p, &seq->index);
|
||||
continue;
|
||||
}
|
||||
if (!dfs_seq_is_full(seq)) /* got it */
|
||||
goto Fill;
|
||||
/* need a bigger buffer */
|
||||
seq->ops->stop(seq, p);
|
||||
rt_free(seq->buf);
|
||||
seq->count = 0;
|
||||
seq->buf = dfs_seq_alloc(seq->size <<= 1);
|
||||
if (!seq->buf)
|
||||
goto Enomem;
|
||||
p = seq->ops->start(seq, &seq->index);
|
||||
}
|
||||
/* EOF or an error */
|
||||
seq->ops->stop(seq, p);
|
||||
seq->count = 0;
|
||||
goto Done;
|
||||
Fill:
|
||||
/* one non-empty record is in the buffer; if they want more, */
|
||||
/* try to fit more in, but in any case we need to advance */
|
||||
/* the iterator once for every record shown. */
|
||||
while (1)
|
||||
{
|
||||
size_t offs = seq->count;
|
||||
off_t pos = seq->index;
|
||||
|
||||
p = seq->ops->next(seq, p, &seq->index);
|
||||
if (pos == seq->index)
|
||||
{
|
||||
LOG_W(".next function %p did not update position index\n", seq->ops->next);
|
||||
seq->index++;
|
||||
}
|
||||
if (!p) /* no next record for us */
|
||||
break;
|
||||
if (seq->count >= size)
|
||||
break;
|
||||
err = seq->ops->show(seq, p);
|
||||
if (err > 0)
|
||||
{ /* ->show() says "skip it" */
|
||||
seq->count = offs;
|
||||
}
|
||||
else if (err || dfs_seq_is_full(seq))
|
||||
{
|
||||
seq->count = offs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
seq->ops->stop(seq, p);
|
||||
n = seq->count > size ? size : seq->count;
|
||||
rt_memcpy((char *)buf + copied, seq->buf, n);
|
||||
size -= n;
|
||||
copied += n;
|
||||
seq->count -= n;
|
||||
seq->from = n;
|
||||
Done:
|
||||
if (!copied)
|
||||
{
|
||||
copied = seq->count ? -EFAULT : err;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pos += copied;
|
||||
seq->read_pos += copied;
|
||||
}
|
||||
rt_mutex_release(&seq->lock);
|
||||
return copied;
|
||||
Enomem:
|
||||
err = -ENOMEM;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reposition the file offset for sequence file
|
||||
*
|
||||
* @param[in] file Pointer to the file structure
|
||||
* @param[in] offset Offset value according to whence
|
||||
* @param[in] whence Reference position for offset:
|
||||
* - SEEK_SET: from file beginning
|
||||
* - SEEK_CUR: from current position
|
||||
* @return off_t New file offset on success, negative error code on failure:
|
||||
* -EINVAL for invalid parameters
|
||||
*/
|
||||
off_t dfs_seq_lseek(struct dfs_file *file, off_t offset, int whence)
|
||||
{
|
||||
struct dfs_seq_file *seq = file->data;
|
||||
off_t retval = -EINVAL;
|
||||
|
||||
rt_mutex_take(&seq->lock, RT_WAITING_FOREVER);
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_CUR:
|
||||
offset += file->fpos;
|
||||
case SEEK_SET:
|
||||
if (offset < 0)
|
||||
break;
|
||||
retval = offset;
|
||||
if (offset != seq->read_pos)
|
||||
{
|
||||
while ((retval = dfs_seq_traverse(seq, offset)) == -EAGAIN);
|
||||
if (retval)
|
||||
{
|
||||
/* with extreme prejudice... */
|
||||
retval = 0;
|
||||
seq->read_pos = 0;
|
||||
seq->index = 0;
|
||||
seq->count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
seq->read_pos = offset;
|
||||
retval = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rt_mutex_release(&seq->lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Release resources associated with a sequence file
|
||||
*
|
||||
* @param[in] file Pointer to the file structure to be released
|
||||
*
|
||||
* @return int Always returns 0 indicating success
|
||||
*/
|
||||
int dfs_seq_release(struct dfs_file *file)
|
||||
{
|
||||
struct dfs_seq_file *seq = file->data;
|
||||
|
||||
if (seq)
|
||||
{
|
||||
rt_mutex_detach(&seq->lock);
|
||||
if (seq->buf)
|
||||
{
|
||||
rt_free(seq->buf);
|
||||
}
|
||||
rt_free(seq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Format and write data to sequence file buffer using variable arguments
|
||||
*
|
||||
* @param[in,out] seq Pointer to sequence file structure
|
||||
* @param[in] f Format string (printf-style)
|
||||
* @param[in] args Variable arguments list
|
||||
*
|
||||
* @details This function:
|
||||
* - Formats data using vsnprintf
|
||||
* - Triggers overflow if buffer is full
|
||||
*/
|
||||
void dfs_seq_vprintf(struct dfs_seq_file *seq, const char *f, va_list args)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (seq->count < seq->size)
|
||||
{
|
||||
len = vsnprintf(seq->buf + seq->count, seq->size - seq->count, f, args);
|
||||
if (seq->count + len < seq->size)
|
||||
{
|
||||
seq->count += len;
|
||||
return;
|
||||
}
|
||||
}
|
||||
dfs_seq_overflow(seq);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Format and print data to sequence file buffer (printf-style)
|
||||
*
|
||||
* @param[in,out] seq Pointer to sequence file structure
|
||||
* @param[in] f Format string (printf-style)
|
||||
* @param[in] ... Variable arguments matching format string
|
||||
*/
|
||||
void dfs_seq_printf(struct dfs_seq_file *seq, const char *f, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, f);
|
||||
dfs_seq_vprintf(seq, f, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a single character to sequence file buffer
|
||||
*
|
||||
* @param[in,out] seq Pointer to sequence file structure
|
||||
* @param[in] c Character to be written
|
||||
*/
|
||||
void dfs_seq_putc(struct dfs_seq_file *seq, char c)
|
||||
{
|
||||
if (seq->count < seq->size)
|
||||
{
|
||||
seq->buf[seq->count++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a string to sequence file buffer
|
||||
*
|
||||
* @param[in,out] seq Pointer to sequence file structure
|
||||
* @param[in] s Null-terminated string to be written
|
||||
*/
|
||||
void dfs_seq_puts(struct dfs_seq_file *seq, const char *s)
|
||||
{
|
||||
int len = strlen(s);
|
||||
|
||||
if (seq->count + len >= seq->size)
|
||||
{
|
||||
dfs_seq_overflow(seq);
|
||||
return;
|
||||
}
|
||||
rt_memcpy(seq->buf + seq->count, s, len);
|
||||
seq->count += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write arbitrary binary data to sequence file buffer
|
||||
*
|
||||
* @param[in,out] seq Pointer to sequence file structure
|
||||
* @param[in] data Pointer to data to be written
|
||||
* @param[in] len Length of data in bytes
|
||||
*
|
||||
* @return int 0 on success, -1 if buffer overflow occurs
|
||||
*/
|
||||
int dfs_seq_write(struct dfs_seq_file *seq, const void *data, size_t len)
|
||||
{
|
||||
if (seq->count + len < seq->size)
|
||||
{
|
||||
rt_memcpy(seq->buf + seq->count, data, len);
|
||||
seq->count += len;
|
||||
return 0;
|
||||
}
|
||||
dfs_seq_overflow(seq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pad the sequence file buffer with spaces and optionally append a character
|
||||
*
|
||||
* @param[in,out] seq Pointer to sequence file structure
|
||||
* @param[in] c Optional character to append after padding (if not '\0')
|
||||
*/
|
||||
void dfs_seq_pad(struct dfs_seq_file *seq, char c)
|
||||
{
|
||||
int size = seq->pad_until - seq->count;
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
if (size + seq->count > seq->size)
|
||||
{
|
||||
dfs_seq_overflow(seq);
|
||||
return;
|
||||
}
|
||||
rt_memset(seq->buf + seq->count, ' ', size);
|
||||
seq->count += size;
|
||||
}
|
||||
|
||||
if (c)
|
||||
{
|
||||
dfs_seq_putc(seq, c);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2023-05-05 Bernard Implement vnode in dfs v2.0
|
||||
*/
|
||||
|
||||
#include <dfs_file.h>
|
||||
#include <dfs_mnt.h>
|
||||
#ifdef RT_USING_PAGECACHE
|
||||
#include "dfs_pcache.h"
|
||||
#endif
|
||||
|
||||
#define DBG_TAG "DFS.vnode"
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#include <rtdbg.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize a virtual node (vnode) structure
|
||||
*
|
||||
* @param[in,out] vnode Pointer to the vnode to be initialized
|
||||
* @param[in] type Type of the vnode
|
||||
* @param[in] fops Pointer to file operations structure
|
||||
*
|
||||
* @return int Always returns 0 indicating success
|
||||
*/
|
||||
int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops *fops)
|
||||
{
|
||||
if (vnode)
|
||||
{
|
||||
rt_memset(vnode, 0, sizeof(struct dfs_vnode));
|
||||
|
||||
vnode->type = type;
|
||||
rt_atomic_store(&(vnode->ref_count), 1);
|
||||
vnode->mnt = RT_NULL;
|
||||
vnode->fops = fops;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create and initialize a new virtual node (vnode)
|
||||
*
|
||||
* @return struct dfs_vnode* Pointer to the newly created vnode, or NULL if creation failed
|
||||
*/
|
||||
struct dfs_vnode *dfs_vnode_create(void)
|
||||
{
|
||||
struct dfs_vnode *vnode = rt_calloc(1, sizeof(struct dfs_vnode));
|
||||
if (!vnode)
|
||||
{
|
||||
LOG_E("create a vnode failed.");
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
rt_atomic_store(&(vnode->ref_count), 1);
|
||||
|
||||
LOG_I("create a vnode: %p", vnode);
|
||||
|
||||
return vnode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy a virtual node (vnode) and free its resources
|
||||
*
|
||||
* @param[in] vnode Pointer to the vnode to be destroyed
|
||||
*
|
||||
* @return int Always returns 0. Note that this does not guarantee success, as errors may occur internally.
|
||||
*/
|
||||
int dfs_vnode_destroy(struct dfs_vnode* vnode)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (vnode)
|
||||
{
|
||||
ret = dfs_file_lock();
|
||||
if (ret == RT_EOK)
|
||||
{
|
||||
if (rt_atomic_load(&(vnode->ref_count)) == 1)
|
||||
{
|
||||
LOG_I("free a vnode: %p", vnode);
|
||||
#ifdef RT_USING_PAGECACHE
|
||||
if (vnode->aspace)
|
||||
{
|
||||
dfs_aspace_destroy(vnode->aspace);
|
||||
}
|
||||
#endif
|
||||
if (vnode->mnt)
|
||||
{
|
||||
DLOG(msg, "vnode", vnode->mnt->fs_ops->name, DLOG_MSG, "fs_ops->free_vnode");
|
||||
vnode->mnt->fs_ops->free_vnode(vnode);
|
||||
}
|
||||
else
|
||||
{
|
||||
DLOG(msg, "vnode", "vnode", DLOG_MSG, "destroy vnode(mnt=NULL)");
|
||||
}
|
||||
|
||||
dfs_file_unlock();
|
||||
|
||||
rt_free(vnode);
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_file_unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increase reference count of a virtual node (vnode)
|
||||
*
|
||||
* @param[in,out] vnode Pointer to the vnode to be referenced
|
||||
*
|
||||
* @return struct dfs_vnode* The same vnode pointer that was passed in
|
||||
*/
|
||||
struct dfs_vnode *dfs_vnode_ref(struct dfs_vnode *vnode)
|
||||
{
|
||||
if (vnode)
|
||||
{
|
||||
rt_atomic_add(&(vnode->ref_count), 1);
|
||||
|
||||
DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
|
||||
}
|
||||
|
||||
return vnode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decrease reference count of a virtual node (vnode) and potentially free it
|
||||
*
|
||||
* @param[in,out] vnode Pointer to the vnode to be unreferenced
|
||||
*/
|
||||
void dfs_vnode_unref(struct dfs_vnode *vnode)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (vnode)
|
||||
{
|
||||
ret = dfs_file_lock();
|
||||
if (ret == RT_EOK)
|
||||
{
|
||||
rt_atomic_sub(&(vnode->ref_count), 1);
|
||||
DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
|
||||
#ifdef RT_USING_PAGECACHE
|
||||
if (vnode->aspace)
|
||||
{
|
||||
dfs_aspace_destroy(vnode->aspace);
|
||||
}
|
||||
#endif
|
||||
if (rt_atomic_load(&(vnode->ref_count)) == 0)
|
||||
{
|
||||
LOG_I("free a vnode: %p", vnode);
|
||||
DLOG(msg, "vnode", "vnode", DLOG_MSG, "free vnode, ref_count=0");
|
||||
|
||||
if (vnode->mnt)
|
||||
{
|
||||
DLOG(msg, "vnode", vnode->mnt->fs_ops->name, DLOG_MSG, "fs_ops->free_vnode");
|
||||
vnode->mnt->fs_ops->free_vnode(vnode);
|
||||
}
|
||||
|
||||
dfs_file_unlock();
|
||||
|
||||
rt_free(vnode);
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_file_unlock();
|
||||
DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
Reference in New Issue
Block a user