You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于libfuse的C语言只读文件系统实现问题咨询

基于libfuse实现只读ext2文件系统的read函数解决方案

要实现读取ext2镜像中的实际文件,核心是直接解析ext2文件系统结构,通过e2fslibs库操作ext2镜像的inode、数据块等关键组件。以下是完整的实现代码及说明:

完整实现代码

#define FUSE_USE_VERSION 30

#include <fuse.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <ext2fs/ext2fs.h>

static ext2_filsys fs; // 全局ext2文件系统句柄

// 转换FUSE路径为ext2兼容路径(去掉开头的/)
static char* get_ext2_path(const char* fuse_path) {
    if (strcmp(fuse_path, "/") == 0) {
        return strdup(".");
    }
    return strdup(fuse_path + 1);
}

static int do_getattr(const char *path, struct stat *st) {
    char* ext2_path = get_ext2_path(path);
    ext2_ino_t ino;
    struct ext2_inode inode;
    int ret;

    // 查找路径对应的inode编号
    ret = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, ext2_path, &ino);
    if (ret) {
        free(ext2_path);
        return -ret;
    }

    // 读取inode信息
    ret = ext2fs_read_inode(fs, ino, &inode);
    if (ret) {
        free(ext2_path);
        return -ret;
    }

    // 填充stat结构
    st->st_uid = inode.i_uid;
    st->st_gid = inode.i_gid;
    st->st_atime = inode.i_atime;
    st->st_mtime = inode.i_mtime;
    st->st_ctime = inode.i_ctime;
    st->st_nlink = inode.i_links_count;
    st->st_size = inode.i_size;

    if (LINUX_S_ISDIR(inode.i_mode)) {
        st->st_mode = S_IFDIR | (inode.i_mode & 0777);
    } else if (LINUX_S_ISREG(inode.i_mode)) {
        st->st_mode = S_IFREG | (inode.i_mode & 0777);
    } else {
        st->st_mode = inode.i_mode & 0777;
    }

    free(ext2_path);
    return 0;
}

static int do_readdir(const char *path, void *buffer, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
    char* ext2_path = get_ext2_path(path);
    ext2_ino_t ino;
    struct ext2_dir_entry *de;
    struct ext2_dir_entry_2 *de2;
    char *buf;
    int ret, i;
    int blocksize = fs->blocksize;

    // 查找目录对应的inode
    ret = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, ext2_path, &ino);
    if (ret) {
        free(ext2_path);
        return -ret;
    }

    buf = malloc(blocksize);
    if (!buf) {
        free(ext2_path);
        return -ENOMEM;
    }

    // 遍历目录的所有直接数据块
    for (i = 0; i < EXT2_NDIR_BLOCKS; i++) {
        if (fs->inode_table[ino-1].i_block[i] == 0) break;

        ret = ext2fs_read_block(fs, fs->inode_table[ino-1].i_block[i], buf);
        if (ret) {
            free(buf);
            free(ext2_path);
            return -ret;
        }

        // 遍历目录条目
        de = (struct ext2_dir_entry *)buf;
        while ((char *)de < buf + blocksize) {
            if (de->inode == 0) {
                de = (struct ext2_dir_entry *)((char *)de + de->rec_len);
                continue;
            }

            // 兼容ext2两种目录条目格式
            if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) {
                de2 = (struct ext2_dir_entry_2 *)de;
                filler(buffer, de2->name, NULL, 0);
            } else {
                filler(buffer, de->name, NULL, 0);
            }

            de = (struct ext2_dir_entry *)((char *)de + de->rec_len);
        }
    }

    free(buf);
    free(ext2_path);
    return 0;
}

static int do_read(const char *path, char *buffer, size_t size, off_t offset, struct fuse_file_info *fi) {
    char* ext2_path = get_ext2_path(path);
    ext2_ino_t ino;
    struct ext2_inode inode;
    int ret;
    int blocksize = fs->blocksize;
    char *block_buf = malloc(blocksize);
    size_t bytes_read = 0;
    size_t remaining = size;
    off_t current_offset = offset;

    if (!block_buf) {
        free(ext2_path);
        return -ENOMEM;
    }

    // 查找文件对应的inode
    ret = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, ext2_path, &ino);
    if (ret) {
        free(block_buf);
        free(ext2_path);
        return -ret;
    }

    // 读取inode信息
    ret = ext2fs_read_inode(fs, ino, &inode);
    if (ret) {
        free(block_buf);
        free(ext2_path);
        return -ret;
    }

    // 偏移超出文件大小,直接返回0
    if (offset >= inode.i_size) {
        free(block_buf);
        free(ext2_path);
        return 0;
    }

    // 计算实际可读取的字节数
    if (offset + size > inode.i_size) {
        remaining = inode.i_size - offset;
    }

    // 读取数据块(仅处理直接块,可扩展支持间接块)
    while (remaining > 0) {
        int block_idx = current_offset / blocksize;
        int offset_in_block = current_offset % blocksize;
        size_t read_size = (remaining > blocksize - offset_in_block) ? (blocksize - offset_in_block) : remaining;

        // 超出直接块范围,返回不支持错误
        if (block_idx >= EXT2_NDIR_BLOCKS) {
            free(block_buf);
            free(ext2_path);
            return -ENOSYS;
        }

        blk64_t block_num = inode.i_block[block_idx];
        if (block_num == 0) break;

        // 读取整个数据块
        ret = ext2fs_read_block(fs, block_num, block_buf);
        if (ret) {
            free(block_buf);
            free(ext2_path);
            return -ret;
        }

        // 复制指定内容到FUSE缓冲区
        memcpy(buffer + bytes_read, block_buf + offset_in_block, read_size);

        bytes_read += read_size;
        remaining -= read_size;
        current_offset += read_size;
    }

    free(block_buf);
    free(ext2_path);
    return bytes_read;
}

static struct fuse_operations operations = {
    .getattr    = do_getattr,
    .readdir    = do_readdir,
    .read       = do_read,
};

int main(int argc, char *argv[]) {
    int ret;

    // 参数检查:需要传入ext2镜像路径和挂载点
    if (argc < 3) {
        fprintf(stderr, "Usage: %s <ext2-image> <mount-point>\n", argv[0]);
        return 1;
    }

    // 打开ext2镜像文件
    ret = ext2fs_open(argv[1], EXT2_FLAG_RW, 0, 0, unix_io_manager, &fs);
    if (ret) {
        fprintf(stderr, "Failed to open ext2 image: %s\n", strerror(-ret));
        return 1;
    }

    // 调整参数,让fuse_main处理挂载点
    argv[1] = argv[2];
    argc--;

    // 启动FUSE文件系统
    ret = fuse_main(argc, argv, &operations, NULL);

    // 关闭ext2文件系统句柄
    ext2fs_close(fs);

    return ret;
}

关键实现说明

  1. ext2文件系统初始化

    • 使用ext2fs_open打开ext2镜像,获取全局文件系统句柄fs,后续所有操作基于该句柄。
    • 编译时需链接e2fslibs库:gcc -o ext2ro ext2ro.c -lfuse -le2fs
  2. 路径转换

    • FUSE路径以/开头,而ext2fs的路径解析函数需要相对路径,通过get_ext2_path完成转换。
  3. getattr函数

    • 通过ext2fs_namei查找路径对应的inode编号,读取inode信息后映射到struct stat结构,返回文件/目录的元数据。
  4. readdir函数

    • 读取目录inode的数据块,遍历其中的目录条目,通过filler函数返回给FUSE,实现目录列表展示。
  5. read函数

    • 根据文件inode的直接块信息,计算读取的块索引和块内偏移,读取对应数据块并复制到FUSE缓冲区,返回实际读取的字节数。

使用步骤

  1. 创建并格式化ext2镜像:
    dd if=/dev/zero of=ext2.img bs=1M count=10
    mkfs.ext2 ext2.img
    
  2. 编译代码并挂载:
    gcc -o ext2ro ext2ro.c -lfuse -le2fs
    sudo ./ext2ro ext2.img /mnt/ext2ro
    
  3. 访问挂载点即可查看ext2镜像中的实际文件。

内容的提问来源于stack exchange,提问作者Ususi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 03:23:11