[RT-Thread记录]DFS虚拟文件系统文件夹操作异常

项目场景:

系统:RT-Thread 5.0.2

硬件:STM32H743


问题描述

1.文件系统打开文件夹再关闭后,申请的内存没有释放

2.elm-fatFs文件系统重复操作同一个文件夹,如复制,会引起系统崩溃


原因分析:

        DFS虚拟文件系统文件打开关闭逻辑错误,文件系统版本升级更新后,dfs_file结构体改变,使用dfs_file_open()打开文件夹,描述符引用计数使用dfs_vnode的ref_count,但在dfs_file_close()中判断了dfs_file的ref_count的值,导致文件夹打开后无法正常关闭;

        文件类型、设备类型等文件打开时会设置和计算dfs_file的ref_count的值,但文件夹只设置了dfs_vnode的ref_count;

1.文件相关结构体

struct dfs_vnode
{
    uint16_t type;               /* Type (regular or socket) */

    char *path;                  /* Name (below mount point) */
    char *fullpath;              /* Full path is hash key */
    int ref_count;               /* Descriptor reference count */
    rt_list_t list;              /* The node of vnode hash table */

    struct dfs_filesystem *fs;
    const struct dfs_file_ops *fops;
    uint32_t flags;              /* self flags, is dir etc.. */

    size_t   size;               /* Size in bytes */
    void *data;                  /* Specific file system data */
};

struct dfs_file
{
    uint16_t magic;              /* file descriptor magic number */
    uint32_t flags;              /* Descriptor flags */
    int ref_count;               /* Descriptor reference count */
    off_t    pos;                /* Current file position */
    struct dfs_vnode *vnode;     /* file node struct */
    void *data;                  /* Specific fd data */
};

 2.文件打开函数

int dfs_file_open(struct dfs_file *fd, const char *path, int flags)
{
    struct dfs_filesystem *fs;
    char *fullpath;
    int result;
    struct dfs_vnode *vnode = NULL;
    rt_list_t *hash_head;

    /* parameter check */
    if (fd == NULL)
        return -EINVAL;

    /* make sure we have an absolute path */
    fullpath = dfs_normalize_path(NULL, path);
    if (fullpath == NULL)
    {
        return -ENOMEM;
    }

    RT_LOG_D("open file:%s", fullpath);

    dfs_fm_lock();
    /* vnode find */
    vnode = dfs_vnode_find(fullpath, &hash_head);
    if (vnode)
    {
        vnode->ref_count++;
        fd->pos   = 0;
        fd->vnode = vnode;
        dfs_fm_unlock();
        rt_free(fullpath); /* release path */
    }
    else
    {
        /* find filesystem */
        fs = dfs_filesystem_lookup(fullpath);
        if (fs == NULL)
        {
            dfs_fm_unlock();
            rt_free(fullpath); /* release path */
            return -ENOENT;
        }

        vnode = rt_calloc(1, sizeof(struct dfs_vnode));
        if (!vnode)
        {
            dfs_fm_unlock();
            rt_free(fullpath); /* release path */
            return -ENOMEM;
        }
        vnode->ref_count = 1;

        RT_LOG_D("open in filesystem:%s", fs->ops->name);
        vnode->fs    = fs;             /* set file system */
        vnode->fops  = fs->ops->fops;  /* set file ops */

        /* initialize the fd item */
        vnode->type  = FT_REGULAR;
        vnode->flags = 0;

        if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH))
        {
            if (dfs_subdir(fs->path, fullpath) == NULL)
                vnode->path = rt_strdup("/");
            else
                vnode->path = rt_strdup(dfs_subdir(fs->path, fullpath));
            RT_LOG_D("Actual file path: %s", vnode->path);
        }
        else
        {
            vnode->path = fullpath;
        }
        vnode->fullpath = fullpath;

        /* specific file system open routine */
        if (vnode->fops->open == NULL)
        {
            dfs_fm_unlock();
            /* clear fd */
            if (vnode->path != vnode->fullpath)
            {
                rt_free(vnode->fullpath);
            }
            rt_free(vnode->path);
            rt_free(vnode);

            return -ENOSYS;
        }

        fd->pos   = 0;
        fd->vnode = vnode;

        /* insert vnode to hash */
        rt_list_insert_after(hash_head, &vnode->list);
    }

    fd->flags = flags;

    if ((result = vnode->fops->open(fd)) < 0)
    {
        vnode->ref_count--;
        if (vnode->ref_count == 0)
        {
            /* remove from hash */
            rt_list_remove(&vnode->list);
            /* clear fd */
            if (vnode->path != vnode->fullpath)
            {
                rt_free(vnode->fullpath);
            }
            rt_free(vnode->path);
            fd->vnode = NULL;
            rt_free(vnode);
        }

        dfs_fm_unlock();
        RT_LOG_D("%s open failed", fullpath);

        return result;
    }

    fd->flags |= DFS_F_OPEN;
    if (flags & O_DIRECTORY)
    {
        fd->vnode->type = FT_DIRECTORY;
        fd->flags |= DFS_F_DIRECTORY;
    }
    dfs_fm_unlock();

    RT_LOG_D("open successful");
    return 0;
}

 在文件打开函数中,会设置描述符引用计数dfs_vnode的ref_count,打开文件类型和设备类型会相应类型的打开处理中对dfs_file的ref_count进行计数

3.文件关闭函数

int dfs_file_close(struct dfs_file *fd)
{
    struct dfs_vnode *vnode = NULL;
    int result = 0;

    if (fd == NULL)
    {
        return -ENXIO;
    }

    if (fd->ref_count == 1)
    {
        dfs_fm_lock();
        vnode = fd->vnode;
        
        if (vnode->ref_count <= 0)
        {
            dfs_fm_unlock();
            return -ENXIO;
        }
        
        if (vnode->fops->close != NULL)
        {
        result = vnode->fops->close(fd);
        }
        
        if (vnode->ref_count == 1)
        {
            /* remove from hash */
            rt_list_remove(&vnode->list);
            fd->vnode = NULL;
            
            if (vnode->path != vnode->fullpath)
            {
            rt_free(vnode->fullpath);
            }
            rt_free(vnode->path);
            rt_free(vnode);
        }
        dfs_fm_unlock();
    }
    return result;
}

 文件关闭函数判断了dfs_file的ref_count的值,会导致文件夹直接跳过不关闭,dfs_vnode也得不到清理,再次打开会对dfs_vnode的ref_count进行累加;

4. elm-fatFs文件打开错误

打开函数int dfs_elm_open(struct dfs_file *file)会对dfs_vnode的ref_count的值进行判断

if (file->vnode->ref_count > 1)
{
    if (file->vnode->type == FT_DIRECTORY
            && !(file->flags & O_DIRECTORY))
    {
        return -ENOENT;
    }
    file->pos = 0;
    return 0;
}

 文件夹没有正常关闭,再次打开后,会在此处直接return 0,没有给file->data复制,后续的文件夹遍历查找、复制等操作会使用data的值,就会出现访问非法地址引起的崩溃;


解决方案:

方式1:修改DFS虚拟文件系统中文件关闭函数判断逻辑,使文件夹关闭能正常进入关闭释放资源处理;

方式2:增加文件夹打开中对dfs_file的ref_count的处理

 如有错漏,欢迎指正!

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值