FUSE is a Linux kernel extension that allows for a user space program to provide the implementations for the various file-related syscalls
WSL(Windows Subsystem for Linux)现在并不支持FUSE
From a user interface perspective, our file system will be a two level directory system, with the following restrictions/simplifications:
The file system will keep data on “disk” via a linked allocation strategy, outlined below
这里推荐 VMware Workstation 15 Player,非商业用途免费,想要高级功能也可以升级到Pro版(官网)
请参考 https://blog.csdn.net/woainishifu/article/details/78967384
$ sudo apt-get install git gcc vim lrzsz openssh-server meson pkg-config make unity-tweak-tool libtool m4 autoconf pkg-config
libfuse github地址 https://github.com/libfuse/libfuse
你也可以点这里下载,下载完成后解压
$ tar -xvf libfuse-fuse-3.3.0.tar.gz
To build and install, we recommend to use Meson and Ninja. After extracting the libfuse tarball, create a (temporary) build directory and run Meson
在之前软件包安装的时候$ sudo apt-get install meson
其实就已经装了好了meson和ninja,这里就可以直接使用啦
$ cd source/root
$ meson builddir
$ cd builddir
$ ninja
$ sudo ninja install
如果sudo ninja install
出现问题
出现以下报错
update-rc.d: error: unable to read /etc/init.d/fuse3
解决办法
$ ls -l /etc/init.d/fuse3 #检查文件是否存在,未找到文件
$ ls -l /usr/local/etc/init.d/fuse3 #检查是否在这里,找到文件
$ sudo cp /usr/local/etc/init.d/fuse3 /etc/init.d/fuse3 #copy
$ sudo update-rc.d fuse3 start 34 S . start 41 0 6 .
$ sudo ninja install #重新执行出错的命令
/etc/ld.so.conf
,在最后加上一行include /usr/local/lib/x86_64-linux-gnu
执行
$ ldconfig -v
$ cd builddir/example
$ mkdir testmount
$ ls -al testmount
$ ./hello testmount #将hello文件系统挂载到testmount下
$ ls -al testmount
You should see 3 entries: ., …, and hello. We just created this directory, and thus it was empty, so where did hello come from? Obviously the hello application we just ran could have created it, but what it actually did was lie to the operating system when the OS asked for the contents of that directory. So let’s see what happens when we try to display the contents of the file.
$ cat testmount/hello
You should get the familiar hello world quotation. If we cat a file that doesn’t really exist, how do we get meaningful output? The answer comes from the fact that the hello application also gets notified of the attempt to read and open the fictional file “hello” and thus can return the data as if it was really there.
Examine the contents of hello.c in your favorite text editor, and look at the implementations of readdir and read to see that it is just returning hard coded data back to the system.
The final thing we always need to do is to unmount the file system we just used when we are done or need to make changes to the program. Do so by:
$ fusermount -u testmount
The hello application we ran in the above example is a particular FUSE file system provided as a sample to demonstrate a few of the main ideas behind FUSE. The first thing we did was to create an empty directory to serve as a mount point. A mount point is a location in the UNIX hierarchical file system where a new device or file system is located. As an analogy, in Windows, “My Computer” is the mount point for your hard disks and CD-ROMs, and if you insert a USB drive or MP3 player, it will show up there as well. In UNIX, we can have mount points at any location in the tree.
Running the hello application and passing it the location of where we want the new file system mounted initiates FUSE and tells the kernel that any file operations that occur under our now mounted directory will be handled via FUSE and the hello application. When we are done using this file system, we simply tell the OS that it no longer is mounted by issuing the above fusermount -u
command. At that point the OS goes back to managing that directory by itself.
要实现一个用户空间的文件系统,只需用自己的方法实现FUSE的“傻瓜”接口,即可写出一个自己的文件系统,而不需要了解操作系统的内核。这一部分最好阅读hello.c
与passthrough.c
的源码,以入门FUSE的编程规范
另外就是,以下8个调用保证了文件系统最基本的功能:
getattr //获取文件/目录属性
readdir //读取目录
mkdir //创建目录
rmdir //删除目录
mknod //创建文件
write //写文件
read //读文件
unlink //删除文件
Your job is to create the u_fs file system as a FUSE application that provides the interface described in the first section.
The u_fs file system should be implemented using an image file, managed by the real file system in the directory that contains the u_fs application. The layout of file system will be follow. We will consider the disk to have 512 byte blocks.
super block | bitmap block | data block |
---|---|---|
(1 block) | (1280 block) | (all the rest block) |
Super block must be the first block of the file system. It descripts the whole file system. Info containing in super block should be:
struct sb {
long fs_size; //size of file system, in blocks
long first_blk; //first block of root directory
long bitmap; //size of bitmap, in blocks
};
Directories should be also treated as a file. Each directory contains a list of u_fs_directory_entry
structures. There is no limit on how many directories we can have.
struct u_fs_file_directory {
char fname[MAX_FILENAME + 1]; //filename (plus space for nul)
char fext[MAX_EXTENSION + 1]; //extension (plus space for nul)
size_t fsize; //file size
long nStartBlock; //where the first block is on disk
int flag; //indicate type of file. 0:for unused; 1:for file; 2:for directory
};
Each directory entry will contain an 8-character maximum directory name, and then have a list of files that are in the directory.
Each file entry in the directory has a filename in 8.3 format. We also need to record the total size of the file, and the location of the file’s first block on disk.
Files will be stored in a virtual disk that is implemented as a single, pre-sized file called .disk with 512 byte blocks of the format:
struct u_fs_disk_block {
size_t size; // how many bytes are being used in this block
long nNextBlock; //The next disk block, if needed.
//This is the next pointer in the linked allocation list
char data[MAX_DATA_IN_BLOCK]; //And all the rest of the space in the block...
//can be used for actual data storage.
};
In order to manage the free or empty space, you will need to create some bitmap blocks on the disk that record whether a given block has been previously allocated or not. The total number of bitmap blocks should depends on the size of file system. You can do this however you like.
To create a 5MB disk image, execute the following:
$ dd bs=1K count=5K if=/dev/zero of=diskimg
This will create a file initialized to contain all zeros, named diskimg. You only need to do this once, or every time you want to completely destroy the disk.
You should also write a format program to init this file, i.e. write its super block and bitmap blocks data.
To be able to have a simple functioning file system, we need to handle a minimum set of operations on files and directories. The functions are listed here in the order that I suggest you implement them in.
The syscalls need to return success or failure. Success is indicated by 0 and appropriate errors by the negation of the error code, as listed on the corresponding function’s man page.
/** Description:
* This function should look up the input path to determine if it is a directory or a file.
* If it is a directory, return the appropriate permissions.
* If it is a file, return the appropriate permissions as well as the actual size.
* This size must be accurate since it is used to determine EOF and thus read may not be called.
* Return values:
* 0 on success, with a correctly set structure
* -ENOENT if the file is not found
*/
static int u_fs_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi);
/** DESCIPTION:
* This function should look up the input path,
* ensuring that it is a directory, and then list the contents.
* To list the contents, you need to use the filler() function.
* For example: filler(buf, ".", NULL, 0);
* adds the current directory to the listing generated by ls -a
* In general, you will only need to change the second parameter...
* to be the name of the file or directory you want to add to the listing.
* Return values:
* 0 on success
* -ENOENT if the directory is not valid or found
*/
static int u_fs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags);
/** DESCIPTION:
* This function should add the new directory to the root level,
* and should update the .directories file
* Return values:
* 0 on success
* -ENAMETOOLONG if the name is beyond 8 chars
* -EPERM if the directory is not under the root dir only
* -EEXIST if the directory already exists
*/
static int u_fs_mkdir(const char *path, mode_t mode);
/** DESCIPTION:
* Deletes an empty directory
* Return values:
* 0 read on success
* -ENOTEMPTY if the directory is not empty
* -ENOENT if the directory is not found
* -ENOTDIR if the path is not a directory
*/
static int u_fs_rmdir(const char *path);
/** DESCIPTION:
* This function should add a new file to a subdirectory,
* and should update the .directories file appropriately
* with the modified directory entry structure.
* Return values:
* 0 on success
* -ENAMETOOLONG if the name is beyond 8.3 chars
* -EPERM if the file is trying to be created in the root dir
* -EEXIST if the file already exists
*/
static int u_fs_mknod(const char *path, mode_t mode, dev_t rdev);
/** DESCIPTION:
* This function should read the data in the file...
* denoted by path into buf, starting at offset.
* Return values:
* size read on success
* -EISDIR if the path is a directory
*/
static int u_fs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi);
/** DESCIPTION:
* This function should write the data in buf into the file...
* denoted by path, starting at offset.
* Return values:
* size on success
* -EFBIG if the offset is beyond the file size (but handle appends)
*/
static int u_fs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi);
/** DESCIPTION:
* Delete a file
* Return values:
* 0 read on success
* -EISDIR if the path is a directory
* -ENOENT if the file is not found
*/
static int u_fs_unlink(const char *path);
如果你已经花了10分钟完全了解了FUSE和u_fs的设计要求,那么GJ,不过这里还是简单总结一下
mknod
的要求稍微与前面一条有冲突,请各位自行斟酌,取一即可sscanf(path, "/%[^/]/%[^.].%s", directory, filename, extension)
,但是要注意输入路径某些地方为空的情况,请自行测试malloc
之后记得free
,小心操作指针,小心内存泄露具体的结构根据下面几张图片来理解吧
这里注意一下super_block
的数据结构实际上只有24bytes
的,但也要占用一个块,因为文件系统要求是这样,所以初始化文件的时候,位图起始块offset = 512bytes
超级块中有个frist_blk
他应该指向根目录的起始块,一般认为是1281
,disk_block
与u_fs_file_directory
的关系是这样的:
我的实现比较鸡肋,都是各种顺序查找,各位大佬看看就好。关于那八个接口的实现,先简单分个组(mkdir
与mknod
)(rmdir
与unlink
)(read
与write
)(getattr
与readdir
)共四组,每组的实现都基本没有太大区别,这里先介绍一下我的mkdir
和rmdir
的实现吧
简单来说,就是往目录最后插item,我的设计要求就是目录从头读到尾中间是不能出现空item的,你可能会问,那删目录的时候怎么办?rmdir
利用了一个简单的办法,“回填”(我是这么称呼的)
希望你看懂了这张图,我也不多解释了,结合“回填”自己理解吧
想了想,别的其实也没有什么好讲的了,实现起来都大同小异,有不明白直接阅读代码吧,附上源码,仅供大家参考,切勿复制粘贴。
待补充