直接用 fuse_lowlevel_init 是因为内存虚拟加密盘需完全控制每个读写请求的加解密逻辑,而 fuse_main 封装过深,无法拦截原始 buffer、统一管理密钥派生与 IV;只有 fuse_lowlevel_init 配合 fuse_session_loop 才能获取 struct fuse_req 和 fuse_in_header,这是透明加密的必要前提。

为什么直接用 fuse_lowlevel_init 而不是 fuse_main
因为内存虚拟加密盘需要完全控制每个读写请求的加解密逻辑,而 fuse_main 封装太深,会绕过关键路径(比如无法拦截原始 buffer、难以统一处理密钥派生和 IV 管理)。fuse_lowlevel_init 配合 fuse_session_loop 才能拿到 struct fuse_req 和原始 struct fuse_in_header,这是做透明加密的必要前提。
实操建议:
- 必须用
libfuse3(不是 fuse2),否则ll接口不完整,fuse_reply_write等函数缺失 - 初始化时传入
FUSE_OPT_KEY_OPT+FUSE_OPT_KEY_NONOPT手动解析挂载参数,避免fuse_parse_cmdline自动覆盖你自定义的密钥选项 - 不要在
fuse_lowlevel_new里传nullptr的op—— 即使只实现read/write,也得填满所有非空函数指针,否则内核会返回-ENOSYS
read 和 write 回调里怎么安全做 AES-GCM 加解密
核心是:不能把明文缓存在用户态内存里超过必要时间,且每次操作必须绑定唯一 nonce(IV)。FUSE 不保证 read 和 write 的 offset 对齐或长度一致,所以不能简单按文件切块预加密 —— 必须按请求实时加解密。
实操建议:
- 用
openssl EVP_AEAD_CTX(OpenSSL 3.0+)或libsodium crypto_aead_aes256gcm_encrypt,别手写 AES 模式 —— GCM 的 tag 验证必须在解密后立即完成,失败就fuse_reply_err(req, EIO) - nonce 构造方式:用文件 inode(
ino_t) + 请求 offset 的高位(如offset >> 12)做 salt,再用 HKDF 派生出 12 字节 nonce,避免重复 - 密文存储结构:内存中用
std::unordered_map<ino_t std::vector>></ino_t>存密文 blob,但write时先解密旧内容(如果存在)、合并新数据、再整块重加密 —— 否则随机写会导致密文碎片化和验证失败
如何让 mount 命令传入密码并安全派生密钥
FUSE 本身不提供密码输入机制,mount -o password=xxx 会把密码暴露在 /proc/mounts 和进程参数里。必须绕过这个路径。
实操建议:
- 挂载时不带密码参数,改用环境变量:
ENCRYPTED_DISK_PASSPHRASE="..." ./myfuse /mnt,启动时立刻memset清零环境变量内存 - 密钥派生必须用
PBKDF2_HMAC_SHA256(至少 100 万轮)或Argon2id(推荐 libsodium),盐值硬编码进二进制或从 /dev/urandom 一次性生成后持久化到 config 文件(注意 config 权限设为 0600) - 绝对不要用
std::string存密码或密钥 —— 改用std::vector<uint8_t></uint8_t>并在析构前显式std::fill清零
为什么 getattr 和 readdir 必须返回一致的 inode 和 size
Linux VFS 层依赖 st_ino 和 st_size 做缓存和一致性校验。如果你在 getattr 返回明文大小,但密文实际更大(GCM tag + padding),或者不同请求返回不同 inode,会导致 ls -l 显示异常、cp 失败、甚至内核 panic(尤其在 ext4+dax 场景下)。
实操建议:
-
getattr中st_size必须返回「明文大小」,不是密文长度;st_ino用文件名哈希(如std::hash<:string>{}(path)</:string>)固定映射,不能用rand() -
readdir返回的每个dirent的d_ino必须和对应路径getattr返回的st_ino完全一致,否则find或 shell tab 补全会乱序或跳过条目 - 内存盘无持久存储,所以所有元数据(mtime/atime/ctime)都应设为当前时间,且
getattr不要 cache,避免 stat 结果滞后
最易被忽略的是密钥生命周期管理:进程 fork 后子进程会继承密钥内存副本,如果没做 pthread_atfork 清理,可能在崩溃 dump 或 core 文件里泄露密钥。别只盯着加解密逻辑,忘了内存安全边界。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











