Ubuntu 20.04下FUSE挂载Zip时无法创建目录的问题
问题:Ubuntu 20.04下FUSE挂载含目录的Zip文件时,目录无法显示
在Ubuntu 20.04系统中,使用FUSE挂载Zip文件时,仅包含文件的Zip能正常处理,但含目录的Zip里的目录无法在挂载点显示。当前实现试图通过getattr回调标记S_IFDIR来让目录可见,但未生效。
实现流程:
- main函数读取Zip条目名称,将目录和文件分别存入
directoryNames和fileNames数组 - 将Zip文件与挂载点参数传递给FUSE
- 期望通过
readdir回调传递路径至getattr,标记S_IFDIR使目录可见,但未生效
编译命令:
gcc -o programName filename -lzip -lfuse -D_FILE_OFFSET_BITS=64
运行方式:
./programName zipFile.zip mountPoint
完整原始代码
#define FUSE_USE_VERSION 30 #include <fuse.h> #include <zip.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <time.h> #include <string.h> #include <errno.h> #include <stdlib.h> #define MAX_SIZE 100 zip_t *archive; char directoryNames[255][255]; char fileNames[255][255]; int dirIndex = -1; int fileIndex = -1; static int do_getattr(const char *path, struct stat *st) { if (strcmp(path, "/") == 0) { st->st_mode = S_IFDIR | 0777; st->st_nlink = 2; } else if (path[strlen(path) - 1] == '/') { st->st_mode = S_IFDIR | 0777; st->st_nlink = 1; st->st_size = 1024; } else { st->st_mode = S_IFREG | 0777; st->st_nlink = 1; st->st_size = 1024; } return 0; } static int readdir_callback(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { (void)offset; (void)fi; filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); if (strcmp(path, "/") == 0) { int num_directories = sizeof(directoryNames) / sizeof(directoryNames[0]); for (int i = 0; i < num_directories; i++) { if (directoryNames[i][0] != '\0') { printf("FILLED Directories %s\n", directoryNames[i]); filler(buf, directoryNames[i], NULL, 0); } else { break; } } int num_files = sizeof(fileNames) / sizeof(fileNames[0]); for (int k = 0; k < num_files; k++) { if (fileNames[k][0] != '\0') { filler(buf, fileNames[k], NULL, 0); // printf("FILLED Files %s\n", fileNames[k]); } else { break; } } } return 0; } static int do_read(const char *path, char *buffer, size_t size, off_t offset, struct fuse_file_info *fi) { int num_entries = zip_get_num_entries(archive, 0); for (int i = 0; i < num_entries; i++) { const char *entry_name = zip_get_name(archive, i, 0); char name[MAX_SIZE]; snprintf(name, MAX_SIZE, "/%s", entry_name); if (strcmp(path, name) == 0) { struct zip_file *entry_file = zip_fopen_index(archive, i, 0); if (entry_file == NULL) { printf("Failed to open entry file '%s'\n", entry_name); continue; } long fileSize = 1024; char *content = (char *)malloc(fileSize + 1); zip_int64_t bytes_read = zip_fread(entry_file, content, fileSize); memcpy(buffer, content + offset, size); return strlen(content) - offset; } } return 0; } static struct fuse_operations operations = { .getattr = do_getattr, .readdir = readdir_callback, .read = do_read, }; int main(int argc, char **argv) { if (argc < 3) { printf("not enought input arguments"); return 0; } int error; archive = zip_open(argv[1], ZIP_RDONLY, &error); if (archive == NULL) { printf("Failed to open: %s\n", argv[1]); return 0; } memset(directoryNames, 0, sizeof(directoryNames)); memset(fileNames, 0, sizeof(fileNames)); int num_entries = zip_get_num_entries(archive, 0); for (int i = 0; i < num_entries; i++) { const char *entry_name = zip_get_name(archive, i, 0); if (entry_name[strlen(entry_name) - 1] == '/') { char dirName[255]; dirIndex++; strcpy(dirName, entry_name); strcpy(directoryNames[dirIndex], dirName); } else { char fileName[255]; fileIndex++; strcpy(fileName, entry_name); strcpy(fileNames[fileIndex], fileName); } } return fuse_main(argc - 1, argv + 1, &operations, NULL); }
问题根源
getattr路径匹配逻辑错误:FUSE传入的目录路径(如/testdir)不带末尾斜杠,当前代码用path[strlen(path)-1] == '/'判断目录的逻辑完全失效,导致目录被误判为普通文件。- 目录名称存储格式不匹配:从Zip中读取的目录名称带末尾斜杠(如
testdir/),但FUSE传递给getattr的路径是/testdir,两者无法匹配。 readdir仅支持根目录:没有实现子目录的内容读取逻辑,即使目录能显示,进入子目录也会为空。do_read存在内存泄漏和大小硬编码问题:未关闭打开的Zip文件,且固定读取1024字节,无法处理大文件。
修复方案
1. 修正目录名称存储逻辑
在main函数中,存储目录名称时去掉末尾斜杠,确保和FUSE路径格式一致:
if (entry_name[strlen(entry_name) - 1] == '/') { dirIndex++; // 去掉末尾斜杠后存储 strncpy(directoryNames[dirIndex], entry_name, strlen(entry_name)-1); directoryNames[dirIndex][strlen(entry_name)-1] = '\0'; }
2. 重构getattr路径匹配逻辑
遍历存储的目录和文件列表,精确匹配路径并返回正确的stat信息:
static int do_getattr(const char *path, struct stat *st) { memset(st, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { st->st_mode = S_IFDIR | 0777; st->st_nlink = 2; st->st_size = 1024; st->st_mtime = time(NULL); return 0; } // 检查是否为目录 const char *dir_path = path + 1; for (int i = 0; i <= dirIndex; i++) { if (strcmp(directoryNames[i], dir_path) == 0) { st->st_mode = S_IFDIR | 0777; st->st_nlink = 2; st->st_size = 1024; st->st_mtime = time(NULL); return 0; } } // 检查是否为文件 const char *file_path = path + 1; zip_int64_t idx = zip_name_locate(archive, file_path, 0); if (idx != -1) { struct zip_stat stat; zip_stat_init(&stat); zip_stat_index(archive, idx, 0, &stat); st->st_mode = S_IFREG | 0777; st->st_nlink = 1; st->st_size = stat.size; st->st_mtime = stat.mtime; return 0; } return -ENOENT; // 路径不存在时返回标准错误码 }
3. 实现子目录readdir逻辑
修改readdir_callback,支持读取子目录下的文件和子目录:
static int readdir_callback(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { (void)offset; (void)fi; filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); if (strcmp(path, "/") == 0) { // 添加顶层目录 for (int i = 0; i <= dirIndex; i++) { if (directoryNames[i][0] != '\0') { filler(buf, directoryNames[i], NULL, 0); } } // 添加顶层文件 for (int i = 0; i <= fileIndex; i++) { if (fileNames[i][0] != '\0' && strchr(fileNames[i], '/') == NULL) { filler(buf, fileNames[i], NULL, 0); } } } else { // 处理子目录,比如/testdir const char *dir_prefix = path + 1; size_t prefix_len = strlen(dir_prefix); char full_prefix[256]; snprintf(full_prefix, sizeof(full_prefix), "%s/", dir_prefix); int num_entries = zip_get_num_entries(archive, 0); for (int i = 0; i < num_entries; i++) { const char *entry_name = zip_get_name(archive, i, 0); if (strncmp(entry_name, full_prefix, prefix_len + 1) == 0) { const char *relative_path = entry_name + prefix_len + 1; // 处理子目录 if (relative_path[strlen(relative_path)-1] == '/') { char *slash_pos = strchr(relative_path, '/'); if (slash_pos == relative_path) continue; char subdir[256]; if (slash_pos != NULL) { strncpy(subdir, relative_path, slash_pos - relative_path); subdir[slash_pos - relative_path] = '\0'; } else { strcpy(subdir, relative_path); } filler(buf, subdir, NULL, 0); } // 处理当前目录下的文件 else if (strchr(relative_path, '/') == NULL) { filler(buf, relative_path, NULL, 0); } } } } return 0; }
4. 修复do_read的内存泄漏和大小问题
static int do_read(const char *path, char *buffer, size_t size, off_t offset, struct fuse_file_info *fi) { const char *entry_name = path + 1; zip_int64_t idx = zip_name_locate(archive, entry_name, 0); if (idx == -1) { return -ENOENT; } struct zip_stat stat; zip_stat_init(&stat); if (zip_stat_index(archive, idx, 0, &stat) != 0) { return -EIO; } if (offset >= stat.size) { return 0; } size_t read_size = size; if (offset + read_size > stat.size) { read_size = stat.size - offset; } struct zip_file *entry_file = zip_fopen_index(archive, idx, 0); if (entry_file == NULL) { return -EIO; } if (zip_fseek(entry_file, offset, SEEK_SET) != 0) { zip_fclose(entry_file); return -EIO; } zip_int64_t bytes_read = zip_fread(entry_file, buffer, read_size); zip_fclose(entry_file); if (bytes_read < 0) { return -EIO; } return bytes_read; }
内容的提问来源于stack exchange,提问作者Michał Szczepaniak
相关产品推荐
相关产品推荐

