环境搭建
# 拉起centos8容器:
IMAGE_ID='5d0da3dc9764'
NAME=centos8_demo
docker run --privileged -idt \
--name $NAME \
-v /data:/data \
--net host \
${IMAGE_ID} \
/usr/sbin/init
docker exec -it $NAME /bin/bash
# 配置源:
mkdir /etc/yum.repos.d/orig && mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/orig && curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo
# 安装编译工具:
yum install automake gcc-c++ cmake libfuse-devel libfuse3-devel curl libcurl -y -q
# 编译:
# 按照具体按照的automake版本重新配置,否则会报错找不到automake-1.15:
autoreconf -f -i
./configure
make
make install
# 配置:
vim /opt/gdfs/gdfs.conf
# 日志位置:
tail -f /opt/gdfs/gdfs.log
# 启动:
/opt/gdfs/gdfs.sh start
# 或
/usr/local/bin/mount.gdfs -m /tmp/robin -f -s --log_path /opt/gdfs --log_level DEBUG -o allow_other -o allow_root -o direct_io
b mount_fs
b get_node
b gdfs_read
b fuse_ll_process_buf
b LRUCache::get
b File::get
b File::read_file
#--
cat /tmp/robin/_opt_gdfs_/haha
架构分析
google auth
该项目使用google OAuth2.0进行鉴权。API示例如下:
# google api示例:
curl 'https://www.googleapis.com/drive/v3/about?fields=storageQuota(limit%2CusageInDrive)&key=[YOUR_API_KEY]' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--header 'Accept: application/json' \
--compressed
api模拟:https://developers.google.com/drive/api/reference/rest/v3/about/get?hl=zh-cn&apix_params=%7B%22fields%22%3A%22storageQuota(limit%2CusageInDrive)%22%7D
google drive/v3/files api
需要了解如何使用google api来访问google drive的文件。
● 每个文件或文件夹都有一个file_id作为唯一标识。例如可以通过以下接口来获取某个file_id的修改时间:
https://www.googleapis.com/drive/v3/files/<file_id>?fields=modifiedTime
gdfs使用该接口来查看某个文件的修改时间是否与本地相同。若不同,则意味着远端有修改,需要获取这个目录所有的子目录(否则无法知道子目录是否被修改),然后查看其子项是否被修改。若有修改,需要GET下来。
需要完整的新子项列表才能准确识别出被删除的文件(存在于旧列表但不在新列表中的项)
需要完整列表来检测和处理文件名冲突(当多个客户端并发创建同名文件时)
若父目录没有被修改,则也意味着其子目录下的文件也没有被修改。
一个包含5000个文件的目录需要分5次请求获取当云端删除10个文件时,仅通过修改时间无法知道具体删除了哪些文件当其他客户端新增文件时,需要通过完整列表同步新增项
// Construct the URL to send the request.
url = GDFS_FILE_URL_ + std::string("?pageSize=1000&q='") + parent_file_id;
url += "'+in+parents+and+trashed+%3D+false&orderBy=name&spaces=drive";
url += "&fields=files(id%2CmimeType%2CmodifiedTime%2Cname%2Csize%2CviewedByMeTime)%2CnextPageToken";
数据结构
公共功能
//一个全局静态变量file_id_node,用于维护file_name与GDFSNode指针的映射关系:
std::unordered_multimap <std::string, GDFSNode *> file_id_node;
// 这样设计在多客户端挂载的情况下应该会有问题,TODO:测试。
// 一个客户端的挂载点对应一个GDrive,包含了uid/gid,容量信息,挂载目录名称rootDir,用于和google api调用的Auth实例,缓存管理LRUCache实例,线程池实例,以及一个GDFSNode *root指针,在GDrive::get_root()的时候初始化,指向当前文件系统的根目录。并将其加入file_node_id的map中。
class GDrive {
public:
uid_t uid;
gid_t gid;
time_t mounting_time;
uint64_t bytes_used;
uint64_t bytes_free;
uint64_t bytes_total;
std::string rootDir; //根目录地址
std::string change_id; //根目录change_id,可以从远端通过API获取扼杀,用于描述根目录是否被修改
Auth auth; //认证实例
LRUCache cache; //缓存管理实例
Threadpool threadpool; //线程池管理实例
struct GDFSNode * root; //根目录GDFSNode指针
}
// 类比于inode用于描述一个文件的基础数据结构,可以用其找到父节点(parent),子节点(通过map children)。
// (linux是将file_name放到entry中,但本项目是直接将其放到Node中。)
struct GDFSNode {
char link;
std::string file_name;
std::string sym_link;
GDFSEntry * entry; // 该文件到entry信息指针
GDFSNode * parent; // 父节点指针
std::unordered_map <std::string, struct GDFSNode *> children;// 该目录下的子node map
}
//用于描述一个文件在内存中的组织形式,类比于dentry
struct GDFSEntry {
std::string file_id; //google drive中的文件标识,每个文件有唯一的file_id,所有通过api交互,都需要用其来进行标识。
//file_id的生成规则在下面描述。
uint64_t file_size;
time_t ctime;
time_t mtime;
time_t atime;
time_t cached_time;
uid_t uid;
gid_t gid;
mode_t file_mode;
dev_t dev;
bool is_dir; //是否为dir,用于区分处理文件夹与文件
int ref_count;
std::string mime_type;
bool g_doc;
bool dirty;
bool pending_create;//在创建文件的时候,标记是否为
bool file_open;
bool write;
bool pending_get;
}
sem_t req_item_sem;
pthread_mutex_t worker_lock;
std::list <struct req_item> req_queue;
std::queue <std::string> file_id_q; //全局的file_id队列,批量生成
file_id的生成
在GDriv