Automounting FUSE filesystems


Author: Ben Martin

One of the main things that gets annoying with FUSE, or Filesystem in Userspace, is that it won’t automatically mount a filesystem when you first attempt to access the filesystem. This means you must manually track mountpoints and specify what program to run in order to mount each FUSE filesystem. Placing the exact commands to mount each FUSE filesystem into shell scripts can make things a little easier, but with afuse, you can mount FUSE filesystems on demand without the need for any explicit mounting.

Afuse is packaged for Ubuntu Gutsy but not for other mainstream distributions. It requires fuse version 2.3 or later. Building afuse from source follows the standard ./configure; make; sudo make install procedure, after which you should find the afuse binary in your path.

The afuse command’s two main options are mount_template and unmount_template. Afuse uses mount_template to specify how to mount a FUSE filesystem. mount_template takes two arguments: the root directory that is being mounted in %r, and the path where the FUSE filesystem should mount itself in %m. A limitation on the mount_template command is that it cannot require any user interaction, such as prompting for a password. The unmount_template is likely to be the same for many applications of afuse, with a good default being fusermount -u -z %m.

The example below shows a test session with the SSH FUSE filesystem, mounting my home directory on another server using sshfs:

$ cd ~ $ mkdir fuse $ cd ./fuse $ mkdir remote-homedir $ sshfs ben@otherserver:/home/ben remote-homedir

The following afuse setup will automatically mount the remote directory with sshfs whenever I access ~/fuse/ben@otherserver, meaning you can avoid typing all the commands above. In the afuse command you must supply both the mount and unmount templates; attempts to leave out the unmount command will fail:

afuse -o mount_template="sshfs %r:/ %m" -o unmount_template="fusermount -u -z %m" ~/fuse

The above setup works well when each FUSE filesystem type has its own top-level directory. For example, you might have ~/fuse/sshfs with all of your SSH FUSE filesystems mounted in it, and ~/fuse/obex with all of your OBEX FUSE filesystems. In that case you would run two different afuse filesystems, each knowing how to mount a single FUSE filesystem type.

If you issue the fusermount -u command to the filesystem that afuse itself is mounted on, that single command will make afuse unmount any FUSE filesystems that it had mounted for you.

The following commands set up automatic mounting of MySQL as a filesystem using MySQLfs and afuse. As detailed in an earlier article, MySQLfs takes the host and database to mount (and possibly the username and password to use when authenticating with MySQL) and mounts a MySQL database as a FUSE filesystem.

The mysqlfs command shown below manually mounts MySQL directly using the MySQLfs FUSE filesystem. The later afuse command allows you to mount any MySQL database servers by DNS name, attempting to mount the MySQLfs database on each server. For example, running ls ~/fuse/localhost would cause MySQLfs to be executed against a MySQL server running on the same machine.

$ mkdir ~/fuse/mysqlfs $ mysqlfs -ohost=localhost -odatabase=mysqlfs ~/fuse/mysqlfs $ ls -l ~/fuse/mysqlfs ... $ fusermount -u ~/fuse/mysqlfs ... $ rmdir ~/fuse/mysqlfs $ afuse -f -o mount_template="mysqlfs -ohost=%r -odatabase=mysqlfs %m" -o unmount_template="fusermount -u -z %m" ~/fuse

Initially I could not get MySQLfs to work without using the -f command-line option to afuse to force it to stay in the foreground. Finding out what is stopping a FUSE auto mount can be difficult. You cannot run strace on a FUSE filesystem, so such brute force attempts to see what system call has failed will not work. The failure in this case was MySQLfs not being happy with its log files and calling exit(1), but afuse will not normally let you know about this error when you are running it in daemon mode (the default). If you run afuse in the foreground so that you can see debug messages, then MySQLfs will actually operate correctly instead of exiting, so you will not be able to see the error message — a Catch-22 situation.

To debug automatic mounts that are not working as expected, place the following code in afuse.c/do_mount() just after the existing system(mount_command) line but before mount_command is passed to free(). This will let you see what has happened to the FUSE mount even when afuse is not running with debug output or in the foreground:

sysret = system(mount_command); { FILE* fp = fopen( "/tmp/afuse.log", "w+" ); fprintf( fp, "template:%sn", user_options.mount_command_template ); fprintf( fp, "mp:%sn", mount_point ); fprintf( fp, "rn:%sn", root_name ); fprintf( fp, "mount_command:%sn", mount_command ); fprintf( fp, "sysret: %.8xn", sysret); fprintf( fp, "exitstat: %.8xn", WEXITSTATUS(sysret)); fclose( fp ); } free(mount_command);

The solution for MySQLfs is to explicitly pass in the path that you would like MySQLfs to use for logging. The afuse command below works as expected:

$ afuse -o mount_template="mysqlfs -ohost=%r -odatabase=mysqlfs -ologfile=/tmp/mysqlfs.log %m" -o unmount_template="fusermount -u -z %m" ~/fuse $ ls -lh ~/fuse/localhost/ drwxrwxr-x 1 ben ben 0 2008-01-24 17:42 bonnie/ drwxrwx--- 1 ben ben 0 2008-01-12 13:24 guten/ -rw-r----- 1 ben ben 44M 2008-01-24 16:35 linux-2.6.23.tar.bz2

If you wish to have a mixture of different FUSE filesystems directly under ~/fuse be managed by a single afuse instance, you must use a shell script for at least the mount_template. For example, you might want some local 9p, Samba, and obex FUSE filesystems to be cobbled together in a single directory tree. This can be convenient when the filesystems offer a similar level of security and when you are less interested in the type of FUSE filesystem than its use. You might be more interested in seeing your smart phone, printer, and iCal files on a WebDAV share with meaningful names and less so in the actual FUSE filesystems that handle each format.

One way to manage afuse when you want different FUSE filesystems mounted is to create a fstab-like file and use a Perl script to manage the actual mounting. This ad hoc afuse fstab file contains lines with the format of top-level directory name and FUSE command to use.

For the example shown below, reading the localhost directory will cause MySQLfs to be mounted on the MySQL server running on localhost. Reading vfedora864prx1 will use sshfs to mount the server with that DNS name. The Perl script that follows afuse-fstab expects the directory name and FUSE mount point to be passed in as the first and second arguments. The logic of the script is simple: find the entry in the afuse-fstab that has the same directory name and use the rest of that line as the template to perform the FUSE mount. The script preserves the meaning of %r and %m in the FUSE auto mount that afuse itself would use, allowing the exact same command to be used in the fstab as would be directly used with afuse. The first substitution cuts off any non-space, non-forward-slash characters followed by optional white space. This removes the first part of the line that describes the directory name that should trigger the auto mount, leaving only the command to execute.

$ mkdir -p ~/etc $ cd ~/etc $ cat afuse-fstab localhost mysqlfs -ohost=%r -odatabase=mysqlfs -ologfile=/tmp/mysqlfs.log %m vfedora864prx1 sshfs %r:/ %m $ cat #!/usr/bin/perl -w $fstab="$ENV{HOME}/etc/afuse-fstab"; $afusedir=$ARGV[0]; $afuse_mountpoint=$ARGV[1]; print "afusedir:$afusedirn"; print "afuse_mountpoint:$afuse_mountpointn"; open(FSTAB, $fstab ) or die("Can not open afuse-fstab at $fstabn"); while( ) { if( /^$afusedir/ ) { s/[^s/]+[s]*//; s/%r/$afusedir/g; s/%m/$afuse_mountpoint/g; chomp; $cmd = $_; print "$cmdn"; system($_) == 0 or die "execution of FUSE filesystem failed!n" . "command:$cmdn" . "reason:$?n"; } }

With the afuse-fstab and Perl script in place you can tell afuse to use the Perl script for all mounting. Better still, shove the whole afuse invocation into a shell script so all you have to do is execute ~/ to have all FUSE automounting done.

$ vi ~/ #!/bin/bash afuse -o mount_template="~/etc/ %r %m" -o unmount_template="fusermount -u -z %m" ~/fuse $ chmod +x ~/ $ ~/ $ l ~/fuse/localhost drwxrwxr-x 1 ben ben 0 2008-01-24 17:42 bonnie/ drwxrwx--- 1 ben ben 0 2008-01-12 13:24 guten/ -rw-r----- 1 ben ben 44M 2008-01-24 16:35 linux-2.6.23.tar.bz2 $ l ~/fuse/vfedora864prx1/home/ben total 36K drwxrwxr-x 1 ben ben 4.0K 2008-01-30 22:01 ben-homedir-on-prx1/ drwxr-xr-x 1 ben ben 4.0K 2007-12-20 21:33 Desktop/ ...


  • System Administration
  • Tools & Utilities