自定义FUSE文件系统执行ls命令报I/O错误原因排查求助
错误根因排查
你遇到的getdents64返回EIO错误由多个代码缺陷共同导致,最直接的触发原因有两个:
ll_readdir函数只处理了ino=1的根目录请求,其他情况直接返回没有调用任何FUSE回复接口,FUSE框架收不到响应会默认返回EIO- 填充完目录项后调用
fuse_reply_buf时传入了完整的buf长度size,而不是实际写入的有效字节数,内核解析到无效的目录项结构直接返回I/O错误
其他必须修复的代码缺陷
1. sendmailfs_stat根目录权限被覆盖
根目录(ino=1)的st_mode赋值逻辑有重复覆盖问题:
// 先赋值了带权限的模式 stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; stbuf->st_uid = uid; // 这里又覆盖成了只有S_IFDIR标记,清空了权限位,直接删掉这行即可 stbuf->st_mode = S_IFDIR;
2. ll_readdir逻辑缺陷
2.1 缺少非根目录请求处理
你的文件系统不允许创建子目录,所以非ino=1的目录请求直接返回ENOTDIR错误即可,必须调用FUSE回复接口:
if (ino != 1) { fuse_reply_err(req, ENOTDIR); return; }
2.2 未处理内核传入的偏移参数off
内核调用readdir时会传入当前读取的偏移,你完全忽略该参数每次都从0开始填充,多次读取时会返回重复内容,需要跳过偏移之前的目录项。
2.3 混用两种目录项填充API
fuse_add_direntry和fuse_add_direntry_plus生成的目录项结构完全不同,不能混着用:
- 如果你没有开启FUSE的
FUSE_CAP_READDIR_PLUS能力,全部改用fuse_add_direntry即可 - 如果要使用plus接口,必须在fuse初始化时声明对应能力,且全部条目都用plus接口填充
2.4 缺少..目录项
标准目录必须包含.和..两个默认条目,你只加了.,需要补充..的条目,对应ino也为1(根目录的父目录指向自己)。
2.5 未判断填充返回值+回复长度错误
每次调用fuse_add_direntry后要判断返回值:返回0说明buf剩余空间不足,要停止填充。最终回复的长度是size - rest,不是完整的size,用完的buf要记得释放避免内存泄漏:
// 填充结束后调用 fuse_reply_buf(req, buf, size - rest); free(buf);
2.6 非法访问私有成员+无锁遍历
mutex_map的_data是私有成员,你在外部直接遍历属于非法访问,要在mutex_map中加公共的遍历接口,且遍历的时候必须加锁,避免多线程读写冲突。
内容的提问来源于stack exchange,提问作者KiYugadgeter
相关产品推荐
相关产品推荐

