FUSE

Discussion in 'Filesystem' started by DevynCJohnson, Jun 29, 2014.

  1. DevynCJohnson

    DevynCJohnson Well-Known Member Staff Member Staff Writer

    Messages:
    1,336
    Likes Received:
    1,074
    Trophy Points:
    113
    NOTE: For those of you that enjoy learning about filesystems, you may enjoy this link - http://www.linux.org/threads/filesystem-article-index.6028/

    Linux Kernel - http://www.linux.org/threads/linux-kernel-reading-guide.5384/

    The Filesystem in Userspace (FUSE) is a special part of the Linux kernel that allows regular users to make and use their own filesystems without needing to change the kernel or have Root privileges. The filesystems used in FUSE are virtual filesystems. Not all virtual filesystems use FUSE. The code for FUSE itself is in the kernel, but the filesystem is in userspace. However, typical filesystems exist in the kernelspace.

    FUSE allows users to make their own filesystems, modify filesystems, or use a special filesystem temporarily. FUSE can also be used to add extra features and abilities to a system or software. For example, GVFS (GNOME Virtual FileSystem) is a filesystem that allows applications to access remote files as if they were local. FUSE makes this possible. Also, at the time this article was written, the Linux kernel did not natively support exFAT (also called FAT64). If users wish to access a storage unit with a exFAT/FAT64 filesystem, users can mount the filesystem in FUSE. However, FUSE may need some extra packages to be installed to run some filesystems or virtual filesystems. For instance, if a user wanted to use exFAT like in the example above, then the user would need to install the "exfat-fuse" package which extends FUSE's abilities. The "exfat-fuse" package is a FUSE driver. Many drivers/extensions are available for FUSE.

    fuse_packages.png

    FUSE is hosted at http://fuse.sourceforge.net/. FUSE is open-source freeware that anyone may obtain and use. FUSE is stable, secure, and reliable. However, it is more efficient to use a "real" filesystem if possible. FUSE is compatible and works on Solaris, FreeBSD, Darwin, GNU/Hurd, OS X, Opensolaris, and others. Some operating systems do not support FUSE. Such systems can use a fork of FUSE or use an alternative. For example, NetBSD uses PUFFS and Windows uses "fuse4win".

    FUSE not only mounts virtual filesystems, but also "real" filesystems like ZFS and exFAT. FUSE can also mount files like ISOs, CD track files, and compressed files (zip, gzip, tar, etc.). FUSE's abilities extend to network filesystems like HTTP-FS, aptfs (apt repos), and others. FUSE can also be used to transfer files to Apple devices like the ipod and iphone (iFUSE). Amazingly, FUSE can also be used to convert FLAC to MP3 via the MP3 filesystem (mp3fs).

    Users can program their own filesystems and use them in FUSE without changing the Linux kernel or needing Root privileges. Users program filesystems in a similar way programs are coded. If users wished to program a virtual filesystem using Ruby, then the programmer would need the "ruby-fusefs" package. FUSE bindings exist for many programming languages including Perl, Python, C/C++. C#, and many others. The complete list of language bindings are here (http://sourceforge.net/apps/mediawiki/fuse/index.php?title=LanguageBindings).

    With FUSE, these virtual filesystems are not formatted. Rather, all a user needs to do to initiate a FUSE filesystem is to mount the specified FUSE filesystem to an empty directory that a user has permissions to use.

    Now that many of you know a lot about FUSE, it is time to explain how it works. FUSE is a Linux module that acts as a “middle-man” or mediator between the FUSE filesystem and the Linux-kernel's VFS module. The VFS module can only be accessed by privileged users or processes. Since all users may access FUSE and FUSE may access VFS, that is how the permissions work to allow any user to use a FUSE filesystem. As for the FUSE filesystem itself, the code contains a structure variable that contains a pointer to functions in the FUSE filesystem code that respond to various actions that a process may try to use. In simple terms, the structure variable is a set of code that says a “read” syscall must have a particular set of parameters and is handled by a specific function defined in the FUSE filesystem's code.

    Sample FUSE structure variable code -
    Code:
    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 *);
    };
    
    So, if a process using a FUSE filesystem performs some task that uses the write() syscall, then the filesystem code will execute the code for write(). When you write code for a FUSE filesystem, you could program it that when a process writes to a file that a copy of the file is to be made first.

    Here is a table to help summarize the parts of a FUSE filesystem's code -
    Import Headers – all C/C++ code imports headers and other programming language import some kind of library.

    Declare Variables – Any variables that are used a lot in the code should be declared near the topic of the source code so the programmers can easily find and change global variables.

    Syscall Declaration – A variable structure titled “fuse_operations” declares a syscall and needed parameters.

    Syscall Functions – Next, programmers would write code for the action or actions that should occur when a particular syscall is declared. Developers may have a function for open(), write(), read(), and many other syscalls or needed features.

    Main() - Obviously, if the FUSE filesystem is coded in C/C++, you will have a “int main()” which is where the code “starts” after the functions and variables are set.

    When the filesystem “executes”, FUSE will be a mediator and communicate with the kernel on behalf of the FUSE filesystem.

    Below is sample FUSE filesystem code that came from FUSE's Sourceforge page (http://fuse.sourceforge.net/helloworld.html).


    Code:
    /*
      FUSE: Filesystem in Userspace
      Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
    
      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);
    }
    

    Attached Files:

    • slide.JPG
      slide.JPG
      File size:
      52.7 KB
      Views:
      129,747
    Last edited: Aug 3, 2014

Share This Page