天天看點

FUSE簡介(譯)

原文: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擴充包:

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 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);
}
           

繼續閱讀