原文:http://www.linux.org/threads/fuse.6211/
譯文:
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擴充包:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISM2kDO0cTMzEzMykDM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
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 Szeredi <[email protected]>
This 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 <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
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);
}