The full name of Fuse is "Filesystem in userspace", or "file system for User space", a kernel module that allows users to implement file systems in user space and mount them to a directory, just like a file system implemented in the kernel. There are several benefits of using fuse: one is because in the user space implementation, development and debugging are more convenient, second, can be used in a number of common services in the form of file system, convenient operation, such as FTPFS,SSHFS,MAILFS, etc. can also avoid some copyright issues, such as Linux on NTFS, The operations of ZFS are implemented through fuse. Of course, the implementation of user space also has shortcomings, the most obvious is the user-state/kernel-state switching caused by multiple performance degradation.
According to reference [1], the communication process of the user through fuse and the kernel is as follows:
+----------------+
| myfs/tmp/fuse |
+----------------+
| ^
+--------------+ v |
| ls/tmp/fuse | +--------------+
+--------------+ | Libfuse |
^ | +--------------+
| V | |
+--------------+ +--------------+
| glibc | | glibc |
+--------------+ +--------------+
^ | | ^
~.~.~.|. ~|~.~.~.~.~.~.~.~.|. ~.|. ~.~.~.~.~.~.~.~.
| V v |
+--------------+ +--------------+
| | ----| FUSE |
| | +--------------+
| VFS | ...
| | +--------------+
| | ----| Ext3 |
+--------------+ +--------------+
As you can see from the diagram, fuse and ext3 are a file system module in the kernel. For example, we use fuse to implement a filesystem and mount it in/tmp/fuse, when we execute LS on the directory, the kernel fuse obtains parameters from the VFS, and then invokes the corresponding function in our own implementation of the MYFS, and then returns the result to LS by the VFS.
The following lab environment is Debian 6, which requires installation of libfuse-dev,fuse-utils and other dependent dependencies.
Hello, world The following is a simple file system oufs, only the LS operation is supported:
#define Fuse_use_version
#include <stdio.h>
#include <string.h>
#include <fuse.h>
static int Ou_readdir (const char* Path, void* buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info* f i)
{
return filler (buf, "Hello-world", NULL, 0);
}
static int ou_getattr (const char* Path, struct stat* st)
{
memset (St, 0, sizeof (struct stat));
if (strcmp (Path, "/") = = 0)
St->st_mode = 0755 | S_ifdir;
else
St->st_mode = 0644 | S_ifreg;
return 0;
}
static struct Fuse_operations Oufs_ops = {
. readdir = ou_readdir,
. GetAttr = ou_ GetAttr,
};
int main (int argc, char* argv[])
{
return Fuse_main (argc, argv, &oufs_ops, NULL);
}
And the makefile of the compiled program:
CC: = gcc
CFLAGS: =-g-wall-d_file_offset_bits=64
OBJS: = $ (Patsubst%.c,%.O, $ (wildcard *.c))
LIBS: =-lfu Se
TARGET: = Oufs
. Phony:all Clean all: $ (target) $ (target): $ (
objs) $ (
cc) $ (CFLAGS)-O $@ $^ $ (LIBS)
. C.O:
$ (CC) $ ( CFLAGS)-C $< Clean
:
rm-f $ (TARGET) $ (OBJS)
After the compilation succeeds, you will see the resulting executable file OUFS. Set up a mount point/tmp/mnt, and then run
After successful try "ls/tmp/mnt", you can see a file hello-world. To debug, you can add the-D option so that you can see the debug output of fuse and your own printf.
The first line of code specifies the version of the fuse API to use. The 2.6 version is used here.
To implement the function of LS, fuse needs to provide two functions: Readdir () and GetAttr (), which are the two function pointers in the struct fuse_operations:
/usr/include/fuse/fuse.h/** Function To add an entry in a readdir () operation * * @param buf The buffer passed to the R Eaddir () operation * @param name the file name of the directory entry * @param stat file attributes, can be NULL * @par Am off offset of the next entry or zero * @return 1 if buffer is full, zero otherwise */typedef int (*fuse_fill_dir_t)
(void *buf, const char *name, const struct STAT *stbuf, off_t off);
struct Fuse_operations {/** Get file attributes. * * Similar to stat (). The ' St_dev ' and ' st_blksize ' fields is * ignored.
The ' St_ino ' field is ignored except if the ' Use_ino ' * mount option is given.
*/INT (*getattr) (const char *, struct stat *); /** Read Directory * * This supersedes the old Getdir () interface.
NEW applications * should use this. * * The filesystem may choose between-modes of OPEration: * * 1) The READDIR implementation ignores the offset parameter, and * passes zero to t He filler function ' s offset. The filler * function would not return ' 1 ' (unless a error happens), so the * whole directory was read in A single readdir operation.
This * works just like the Old Getdir () method. * * 2) The Readdir implementation keeps track of the offsets of the * directory entries. It uses the offset parameter and always * passes Non-zero offset to the filler function.
When the buffer was full (or an error happens) The filler function would return * ' 1 '.
* * Introduced in version 2.3 */int (*READDIR) (const char *, void *, fuse_fill_dir_t, off_t,
struct fuse_file_info *); ......
};
This is to implement GetAttr () because we want to traverse the root directory and get information such as root permissions through GetAttr (). GetAttr () implements a function similar to stat (), returning relevant information such as file permissions, type, UID, etc. The path in the parameter may be a file or directory or a soft link or other, so the type information is populated in addition to the permissions. There is only one file Hello-world in the program except the root directory, so only the directory (S_ifdir) and the normal file (S_ifreg). Using "ls-l" to see what is under/tmp/mnt, the link number of the Hello-world, the modification time, and so on are all wrong, because this information is not populated in the ou_getattr () implementation. See Manpage to know which fields are available for stat.
The parameter fuse_fill_dir_t of Readdir () is a function pointer that fills the BUF with a entry of information each time. The Ou_readdir () in the program uses the first pattern in the annotation, which is to put all the entry into buffer at once. If more entry, the Readdir () in the offset to filler can, buffer full filler return 1.
Finally, the Fuse_main () is called in the main function, and the operation functions and parameters of the file system (such as the mount point/tmp/mnt) are passed to fuse.
Create/delete Files
Next, let's expand the program above to add the ability to create/delete normal files:
#define Fuse_use_version #include <stdio.h> #include <stdlib.h> #include <string.h> #include <er
rno.h> #include <fuse.h> #include "list.h" #define Max_namelen 255 struct Ou_entry {mode_t mode;
struct List_node node;
Char Name[max_namelen + 1];
};
static struct List_node entries; static int Ou_readdir (const char* Path, void* buf, fuse_fill_dir_t filler, off_t offset, struct fuse
_file_info* fi) {struct list_node* n;
Filler (buf, ".", NULL, 0);
Filler (buf, "..", NULL, 0);
List_for_each (n, &entries) {struct ou_entry* o = list_entry (n, struct ou_entry, node);
Filler (buf, o->name, NULL, 0);
} return 0;
} static int ou_getattr (const char* Path, struct stat* st) {struct list_node* n;
memset (St, 0, sizeof (struct stat)); if (strcmp (Path, "/") = = 0) {St->st_mode = 0755 |
S_ifdir;
St->st_nlink = 2; St->st_size =0;
List_for_each (n, &entries) {struct ou_entry* o = list_entry (n, struct ou_entry, node);
++st->st_nlink;
St->st_size + = strlen (o->name);
} return 0;
} list_for_each (n, &entries) {struct ou_entry* o = list_entry (n, struct ou_entry, node);
if (strcmp (path + 1, o->name) = = 0) {St->st_mode = o->mode;
St->st_nlink = 1;
return 0;
}} return-enoent;
} static int ou_create (const char* Path, mode_t mode, struct fuse_file_info* fi) {struct ou_entry* o;
struct list_node* n;
if (strlen (path + 1) > Max_namelen) return-enametoolong;
List_for_each (n, &entries) {o = List_entry (n, struct ou_entry, node);
if (strcmp (path + 1, o->name) = = 0) return-eexist;
} o = malloc (sizeof (struct ou_entry)); strcpy (o->name, path + 1); /* Skip Leading '/' */o->mode = Mode |
S_ifreg;
List_add_prev (&o->node, &entries);
return 0;
} static int Ou_unlink (const char* path) {struct List_node *n, *p;
List_for_each_safe (n, p, &entries) {struct ou_entry* o = list_entry (n, struct ou_entry, node);
if (strcmp (path + 1, o->name) = = 0) {__list_del (n);
Free (o);
return 0;
}} return-enoent; } static struct Fuse_operations oufs_ops = {. GetAttr = ou_getattr,. Readdir = Ou_readdir,. Create
= Ou_create,. Unlink = Ou_unlink,};
int main (int argc, char* argv[]) {list_init (&entries);
Return Fuse_main (argc, argv, &oufs_ops, NULL); }
The list operation list.h in the code is shown in the appendix. Originally want to use C + + list, but with g++ compile error, search error message when found a GCC bug. In a word, clang3 very useful, error prompt very humane, who use who know.
Added two functions ou_create () and Ou_unlink (), respectively, to create and delete files, add a global variable entries for saving all Entry;ou_readdir () and ou_getattr () also made corresponding changes. Since these functions are equivalent to implementing the interfaces in the VFS, it is not possible to return 1 on an error, but instead to return different values based on different error types. With regard to the incorrect generation of dock files for errno.h, the values and meanings are defined in/usr/include/asm-generic/errno-base.h and/usr/include/asm-generic/errno.h.
After compiling the mount, try executing "touch/tmp/mnt/abc" and then LS to see if a file is more than ABC. The list.h used in the program (the relevant principle can be referred to here):
#ifndef __list_h__ #define __LIST_H__/* Doubly linked LIST implementation from Linux kernel */#define OFFSET_OF (type , member) \ ((unsigned long) (& ((type*) 0)->member)) #define CONTAINER_OF (PTR, type, member) \ ((type*) ((
unsigned long) (PTR)-offset_of (type, member))) #define List_entry (PTR, type, member) container_of (PTR, type, member)
struct List_node {struct List_node *prev, *next;};
static inline void List_init (struct list_node* list) {list->prev = list;
List->next = list;
} static inline void List_add_prev (struct list_node* node, struct list_node* next) {
Node->next = Next;
Node->prev = next->prev;
Next->prev = node;
Node->prev->next = node;
} static inline void __list_del (struct list_node* node) {Node->prev->next = node->next;
Node->next->prev = node->prev; } static inline void List_del (struct list_node* node) {__list_del (node);
List_init (node); } #define List_for_each (p, head) \ for (p) = (head)->next; (p)! = (head);
(p) = (p)->next) #define LIST_FOR_EACH_SAFE (P, N, head) \ for (p) = (head)->next, (n) = (p)->next; \ (p)! = (head); \ (p) = (n), (n) = (p)->next) #endif
Original address: Http://ouonline.net/building-your-own-fs-with-fuse-1