XX 大学计算机学院课程设计报告
XX 大学计算机学院
课程设计报告
课程名称 基于 Fuse 的用户态文件系统
实验学期 2017 至 2018 学年,第 1 学期
学生姓名
专业班级
学
号
指导教师
开 课 系
计算机
操作系统课程组制
XX 大学计算机学院课程设计报告
《操作系统》课程设计报告
课程设计题目:基于 Fuse 的用户态
设
计
时
间
:
文件系统
2018.1.10-2018.1.14
一、 课程设计目的与要求
1. 参考 Fuse 的安装说明,有可能需要重新编译 Linux 内核
2. 利用 Fuse 实现一个自己的文件系统
3. 可以实现文件内容的保密保存(可以使用一种加密算法)
4. 至少实现文件内容的保密读写,可考虑如何对不同文件保存不同的
key。
二、 课程设计内容
利用用户空间文件系统(Filesystem in Userspace)设计自己的文件系统。
要求至少实现文件内容的保密读写。
三、 课程设计设备与环境
CentOS7, VMware_player.
四、 报告正文
4.1 分析与设计思路
看到完全陌生的题目,首先想到的是从网上查找资料。但是网上有关 fuse
的资料比较零散,没有比较全面的教程,于是选同一个题目的同学一起分享思路,
依次解决一个个错误。
首先了解 fuse 的组成:
./doc 包含 FUSE 相关文档
XX 大学计算机学院课程设计报告
./include 包含了 FUSE API 头,对创建文件系统有用,主要用 fuse.h
./lib 存放 FUSE 库的源代码
./util 包含了 FUSE 工具库的源代码
./example 参考的例子
然后要明确使用 fuse 的要求:
1) 导入头文件:所有的 C/C++头文件,以及其它语言的支持库;
2) 声明变量:任何被大量使用的变量应该放在源程序的显眼位置,让开发
者容易找到并修改;
3) 系统调用声明:以“fuse_operations"声明的系统调用及相关参数;
4) 系统调用函数:实现需要的系统调用,如 open(), read(), write()及
其它特性;
5) Main 函数:显然,如果基于 C/C++实现,main 函数必不可少。
6) 一旦文件系统”运行“起来,FUSE 会作为中介,与 Kernel 进行通信。
最后在实际使用中遇到一些问题,例如:
1)开始测试挂载时出现错误#error Please add -D_FILE_OFFSET_BITS=64 to
your compile flags!
解 决 方 法 : 加 入 参 数 重 新 编 译 gcc -Wall `pkg-config fuse --cflags
--libs` -D_FILE_OFFSET_BITS=64 fusexmp.c -o fusexmp
2)出现错误 undefined reference to `fuse_main_real'
解决方法:修改参数重新编译 FUSE。
i.卸载上面安装的 fuse:“rmmod fuse”,“make uninstall”,“make
clean”;
ii.指定“/usr”为安装目录重新安装:“./configure --prefix=/usr”,
XX 大学计算机学院课程设计报告
“make”,“make install”;
iii.加载 fuse.ko 模块“modprobe fuse”,然后在切换到 example 目录下编译
fusexmp.c。
iv.导入 PKG_CONFIG_PATH
export
PKG_CONFIG_PATH=/usr/lib/pkgconfig/:/usr/local/lib/pkgconf
ig/
3)由于没有及时卸载挂载,于是出现了挂载点冲突
fuse: mountpoint is not empty
fuse: if you are sure this is safe, use the 'nonempty' mount option
产生这种问题的原因是因为如果挂载目录下的文件名和挂载后的产生的文
件名如果相同的话,系统会产生困扰,所以最好避免这种情况的发生。在确定安
全的情况下可以在挂载时加上 nonempty 命令。
./fusexmp -o nonempty tmp
4)出现下面的错误提示
fuse: bad mount point `/mnt/fuse': Transport endpoint is not
connected
执行 umount -l /mnt/fuse 即可
4.2 详细设计(各模块流程图)
1.Fuse 使用原理
用户通过 FUSE 和内核的通信过程如下:
XX 大学计算机学院课程设计报告
从图中可以看到,FUSE 和 ext3 一样,是内核里的一个文件系统模块。例如
用 FUSE 实现一个文件系统并挂载在 /tmp/fuse,当对该目录执行 ls 时,内核
里的 FUSE 从 VFS 获得参数,然后调用实现的 myfs 中相应的函数,得到结果
后再通过 VFS 返回给 ls。
2.文件系统中需要实现的接口
要使用 FUSE 来创建一个文件系统,要声明一个 fuse_operations 类型的
结构变量,并将其传递给 fuse_main 函数。fuse_operations 结构中有一个指
针,指向在执行适当操作时需要调用的函数。下面给出了 fuse_operations 结
构中需要的函数:
struct fuse_operations {
int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t);
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*symlink) (const char *, const char *);
int (*rename) (const char *, const char *);
int (*link) (const char *, const char *);
int (*chmod) (const char *, mode_t);
int (*chown) (const char *, uid_t, gid_t);
XX 大学计算机学院课程设计报告
int (*truncate) (const char *, off_t);
int (*utime) (const char *, struct utimbuf *);
int (*open) (const char *, struct fuse_file_info *);
int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
int (*write) (const char *, const char *, size_t, off_t,struct fuse_file_info *);
int (*statfs) (const char *, struct statfs *);
int (*flush) (const char *, struct fuse_file_info *);
int (*release) (const char *, struct fuse_file_info *);
int (*fsync) (const char *, int, struct fuse_file_info *);
int (*setxattr) (const char *, const char *, const char *, size_t, int);
int (*getxattr) (const char *, const char *, char *, size_t);
int (*listxattr) (const char *, char *, size_t);
int (*removexattr) (const char *, const char *);
};
这些函数根据功能并不需要全部实现,故本实验核心代码参考 fusexmp_fh
进行简化和优化。以下这些函数为主要实现函数:
getattr: int (*getattr) (const char *, struct stat *);
这个函数与 stat() 类似。st_dev 和 st_blksize 域都可以忽略。st_ino
域也会被忽略,除非在执行 mount 时指定了 use_ino 选项。
getdir: int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
这个函数会读取一个目录中的内容。这个操作实际上是在一次调用中执行
opendir()、readdir()、...、closedir() 序列。对于每个目录项来说,都应该
XX 大学计算机学院课程设计报告
调用 filldir() 函数。
mkdir: int (*mkdir) (const char *, mode_t);
用来创建一个目录。
rmdir: int (*rmdir) (const char *);
用来删除一个目录。
unlink: int (*unlink) (const char *);
删除文件。
rename: int (*rename) (const char *, const char *);
重命名文件。
chmod: int (*chmod) (const char *, mode_t);
修改文件的权限位。
chown: int (*chown) (const char *, uid_t, gid_t);
修改文件的属主。
truncate: int (*truncate) (const char *, off_t);
修改文件的大小。
utime: int (*utime) (const char *, struct utimbuf *);
修改文件访问/修改时间。
* open: int (*open) (const char *, struct fuse_file_info *);
这是文件的打开操作。对 open() 函数不能传递创建或截断标记(O_CREAT、
O_EXCL、O_TRUNC)。这个函数应该检查是否允许执行给定的标记的操作。另外,
open() 也可能在 fuse_file_info 结构中返回任意的文件句柄,这会传递给所
有的文件操作。
XX 大学计算机学院课程设计报告
read: int (*read) (const char *, char *, size_t, off_t, struct
fuse_file_info *);
这个函数从一个打开文件中读取数据。除非碰到 EOF 或出现错误,否则
read() 应该返回所请求的字节数的数据;否则,其余数据都会被替换成 0。一
个例外是在执行 mount 命令时指定了 direct_io 选项,在这种情况中 read()
系统调用的返回值会影响这个操作的返回值。
write: int (*write) (const char *, const char *, size_t, off_t, struct
fuse_file_info *);
这个函数将数据写入一个打开的文件中。除非碰到 EOF 或出现错误,否则
write() 应该返回所请求的字节数的数据。一个例外是在执行 mount 命令时指
定了 direct_io 选项(这于 read() 操作的情况类似)。
对于没有实现的接口可以不做处理,也可以返回 fuse。
3.加密原理
GnuPG 的全称是 GNU 隐私保护(GNU Privacy Guard),常常被称为 GPG,它结
合了一组加密软件。它是由 GNU 项目用 C 编程语言编写的。可以使用 apt 或 yum
从软件库来安装它。
$ sudo apt-get install gnupg
# yum install gnupg
支持的算法:
公钥:RSA, RSA-E, RSA-S, ELG-E, DSA
对称加密:3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH
散列:MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512
压缩:不压缩, ZIP, ZLIB, BZIP2
语法:gpg [选项] [文件名]
本实验采用-c, --symmetric 仅使用对称加密
4.3 编程实现(带注释的主要算法源码、内核编译过程以及
动态模块加载过程等)