FUSE(The Filesystem in Userspace)在Kernel中是一个奇特的部分,它允许通常的用户不用修改Kernel或取得Root权限,就能制作或使用他们自己的文件系统。在FUSE中使用的文件系统是虚文件系统(Virtual Filesystems),但并非所有的虚文件系统使用的都是FUSE。FUSE的代码Kernel中,但文件系统位于User Space。不过,典型的文件系统还是存在于Kernel中。
FUSE允许用户制作、修改或者临时使用一个特殊的文件系统。FUSE还可以被用于给系统或软件增加一些额外的特性与能力,如,GVFS(GNOME Virtual Filesystem)允许程序以类似存取本地文件的方式存取远程文件系统,而FUSE使之成为可能。并且,在写这篇文章的时候,Linux Kernel还没有原生支持exFat(又叫做FAT64),但用户可以使用FUSE挂载exFAT/FAT64文件系统。不过, FUSE可能需要安装一些额外的支持包来运行这些文件系统。例如,如果用户需要使用上面提到的exFAT,则需要安装扩展了FUSE的“exfat-fuse"包,该包是一个FUSE的驱动包。下图是更多的FUSE扩展包:
FUSE项目位于http://fuse.sourceforge.net/,是一个开源项目,任何人都可以获取和使用,且稳定、安全与可靠。不过,如果使用一个“真”的文件系统,效率会更好。FUSE与Solaris,FreeBSD,Darwin,GNU/Hurd,OS X,OpenSolaris等OS兼容。有些OS不支持FUSE,但可以使用一些源自FUSE的替代品,如NetBSD使用PUFFS,而Windows使用“Fuse4Win"。
FUSE不仅仅用于挂载虚文件系统,还用于”真“的文件系统,如ZFS和exFAT。FUSE还可以挂载ISO,CD以及压缩的文件(zip,gzip,tar等等)。FUSE还扩展到了网络文件系统,如HTTP-FS,Aptfs(apt repos)等等。FUSE可以被用于传输Apple设备(如iPod和iPhone)上的文件。更神奇的是,FUSE还可以通过mp3fs将FLAC转换为mp3。
用户可以实现他们自己的文件系统,而不用修改Kernel。用户文件系统以一种类似的方式来开发。如果用户希望使用Ruby实现一种虚文件系统,则需要”ruby-fusefs"包。许多语言提供了FUSE的绑定,如Perl,Python,C/C++,C#等等。完整的语言绑定列表:http://sourceforge.net/apps/mediawiki/fuse/index.php?title=LanguageBindings.
使用FUSE,这些虚文件系统并未被格式化。相反,用户需要初始化这些文件系统,并将它挂载到用户有权访问的空目录上。
现在我们已经知道了FUSE的很多知识,该解释其如何工作了。FUSE是一个Linux的中介者(Mediaor)模块,位于FUSE文件系统与Linux Kernel的VFS之间。VFS只能被特权用户或进程访问。由于用户可以存取FUSE,而FUSE可以存取VFS,这就是权限系统为何允许任何用户使用FUSE。对于FUSE自身来说,代码中包含一个结构体变量,其中包含了许多进程可能会到的FUSE的函数指针。
简单的FUSE结构体变量代码:
struct fuse_operations { int (*mknod) (const char *, mode_t, dev_t); int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t); int (*mkdir) (const char *, mode_t); int (*rmdir) (const char *); int (*readlink) (const char *, char *, size_t); int (*symlink) (const char *, const char *); int (*unlink) (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); 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 (*getattr) (const char *, struct stat *); 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 *); };
所以,如果一个进程使用FUSE执行Write操作,则文件系统执行write()的代码。
下面列一下使用FUSE需要的工作:
导入头文件:所有的C/C++头文件,以及其它语言的支持库;
声明变量:任何被大量使用的变量应该放在源程序的显眼位置,让开发者容易找到并修改;
系统调用声明:以“fuse_operations"声明的系统调用及相关参数;
系统调用函数:实现需要的系统调用,如open(), read(), write()及其它特性;
Main函数:显然,如果基于C/C++实现,main函数必不可少。
一旦文件系统”运行“起来,FUSE会作为中介,与Kernel进行通信。
下面的例子来自于http://fuse.sourceforge.net/helloworld.html:
/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos SzerediThis program can be distributed under the terms of the GNU GPL. See the file COPYING. gcc -Wall hello.c `pkg-config fuse --cflags --libs` -o hello */ #define FUSE_USE_VERSION 26 #include #include #include #include #include static const char *hello_str = "Hello World!\n"; static const char *hello_path = "/hello"; static int hello_getattr(const char *path, struct stat *stbuf) { int res = 0; memset(stbuf, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; } else if (strcmp(path, hello_path) == 0) { stbuf->st_mode = S_IFREG | 0444; stbuf->st_nlink = 1; stbuf->st_size = strlen(hello_str); } else res = -ENOENT; return res; } static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { (void) offset; (void) fi; if (strcmp(path, "/") != 0) return -ENOENT; filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); filler(buf, hello_path + 1, NULL, 0); return 0; } static int hello_open(const char *path, struct fuse_file_info *fi) { if (strcmp(path, hello_path) != 0) return -ENOENT; if ((fi->flags & 3) != O_RDONLY) return -EACCES; return 0; } static int hello_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { size_t len; (void) fi; if(strcmp(path, hello_path) != 0) return -ENOENT; len = strlen(hello_str); if (offset < len) { if (offset + size > len) size = len - offset; memcpy(buf, hello_str + offset, size); } else size = 0; return size; } static struct fuse_operations hello_oper = { .getattr = hello_getattr, .readdir = hello_readdir, .open = hello_open, .read = hello_read, }; int main(int argc, char *argv[]) { return fuse_main(argc, argv, &hello_oper, NULL); }