天天看点

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

继续阅读