科大睿客网 (rec) 云盘的灵车 FUSE 实现。
Forked from myl7/recfs. Old README. 请勿用于生产环境。
因本程序导致的数据丢失或损坏开发者不负任何责任:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
目前还处于 Alpha 阶段
已实现的功能(对应函数可能不符合 POSIX 语义):
- getattr: 获取文件或文件夹的信息
- opendir: 打开文件夹
- readdir: 读取文件夹文件列表
- statfs: 读取可用空间与总空间信息
- create: 在本地缓存创建新文件
- open: 打开远程的文件,或在本地缓存创建新文件
- read: 读取本地缓存的文件,若文件无本地缓存则下载
- write: 写入数据至本地缓存
- mkdir: 创建文件夹
- unlink: 移动文件至回收站
- rmdir: 移动文件夹至回收站
- rename: 不更名移动文件或文件夹至其他文件夹下,或原地更名(扩展名不变)
- link: 服务端复制文件(不是创建硬链接)
- release: 如果是新建的文件,上传至服务器
目前的程序限制:
- 写入:仅支持创建新文件写入;修改已有文件内容的行为未定义;由于接口限制,无法新建 0 bytes 的文件。
- 回收站(
?Recycle
)仅支持查看文件夹内容。rm
删除操作的行为是将文件移动至回收站。 - 备份文件夹(
?Backup
)的行为未测试。 - 由于操作系统限制,
link()
/ln
无法发送复制文件夹的命令。 - 程序不会清理临时文件夹的内容。
- 进行读取操作时会先从远程下载完整的文件至临时文件夹,然后再响应读取请求。
框架是 fuse_mt
,类似于 High-level FUSE API,但其实是一个 Rust 重写的版本而不是 API binding。fuse_mt
内部维护了 inode 列表。在打开文件/文件夹的时候,用户实现的函数会返回对应的 file handle (fh
),这个 file handle 可以在接下来的操作中被其他的文件操作函数用到。所有可以实现的文件系统函数参照 https://docs.rs/fuse_mt/latest/fuse_mt/trait.FilesystemMT.html。本项目的文件系统函数实现参考 fs.rs。
程序假设只有它一个写者,好处是可以省下很多文件夹列举时的开销。Rec 的 API 会返回文件夹(节点)下每个文件的信息。原始的 recfs 实现甚至每次 getattr()
都会去 list 一次,结果 ls
的开销的 HTTP 请求数是 O(n) 的,特别卡。
fidmap.rs 是结构树缓存。FidMap
的作用注释很清楚了:
pub struct FidMap {
fhmap: BiBTreeMap<u64, Fid>, // a bidirectional map of "file handle" and Fid
listing_map: HashMap<Fid, FidCachedList>, // a map from Fid to the HTTP cache of listing
parent_map: HashMap<Fid, Option<Fid>>, // a map from Fid to its parent
}
cache.rs 是文件缓存,能够处理从远程获取缓存到本地的文件和新创建的文件。写入文件的部分,目前只处理了添加新文件的逻辑,修改已有文件的逻辑很麻烦(感觉肯定很难写好而且很大可能会丢数据),并且和 Rec 的 API 不搭:没有原地更新文件的 API(其实对象存储都是这样的?)。
rec.ustc.edu.cn authentication 用得是 token,位于 header x-auth-token
field,TTL 似乎较小(应该 < 1d)。
支持两种登录方式:CAS 登录(此时 CAS 用户名和密码会被发送到 rec 的接口,而非统一身份认证的接口,这是参考 Windows 客户端的实现做的);第二种是根据浏览器登录后的 cookie 登录(如果从安全性考虑,我更推荐这种方式)。尽管 auth token 的 TTL 很小,但是登录同时也提供了 refresh token,在 get/post 的时候,如果发现返回 status code 为 "401",那么就用 refresh token 更新 auth token 之后再试一次。
别用,因为很可能会出问题。如果要程序化批量处理,参考 reccli 来做。
MIT