9

How can I get the process ID of the driver of a FUSE filesystem?

For example, I currently have two SSHFS filesystems mounted on a Linux machine:

$ grep sshfs /proc/mounts
host:dir1 /home/gilles/net/dir1 fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0
host:dir2 /home/gilles/net/dir2 fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0
$ pidof sshfs
15031 15007

How can I know which of 15007 and 15031 is dir1 and which is dir2? Ideally I'd like to automate that, i.e. run somecommand /home/gilles/net/dir1 and have it display 15007 (or 15031, or “not a FUSE mount point”, as appropriate).

Note that I'm looking for a generic answer, not an answer that's specific to SSHFS, like tracking which host and port the sshfs processes are connected to, and what files the server-side process has open — which might not even be possible at all due to connection sharing.

I'm primarily interested in a Linux answer, but a generic answer that works on all systems that support FUSE would be ideal.

Why I want to know: to trace its operation, to kill it in case of problems, etc.

  • Maybe you can find an open file descriptor to the mount point in /proc/$pid/fd ? – jelle foks Apr 26 '15 at 04:04
  • "Generic answer that works on all systems with FUSE support" has just been proved impossible. :) Because you cannot assume that the driver name shown in /proc/mounts has to be identical to the one you specify to pidof. My NTFS partitions have just (unfortunately) shown the naked truth: the driver shows up as fuseblk in /proc/mounts, whilst you have to use pidof with /sbin/mount.ntfs (i.e. /bin/ntfs-3g) to get the pids you want. This is far from unified, methinks, and too far to even think of parsing /proc/mounts for driver names one might want to feed into pidof. – syntaxerror Apr 26 '15 at 09:07

2 Answers2

3

I don't think it's possible. Here's why. I took the naive approach, which was to add the pid of the process opening /dev/fuse to the meta data that fuse creates at mount time, struct fuse_conn. I then used that information to display a pid= field in the mount command. The patch is really simple:

diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 7354dc1..32b05ca 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -402,6 +402,9 @@ struct fuse_conn {
        /** The group id for this mount */
        kgid_t group_id;

+       /** The pid mounting process */
+       pid_t pid;
+
        /** The fuse mount flags for this mount */
        unsigned flags;

diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index e8799c1..23a27be 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -554,6 +554,7 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
        struct super_block *sb = root->d_sb;
        struct fuse_conn *fc = get_fuse_conn_super(sb);

+       seq_printf(m, ",pid=%u", fc->pid);
        seq_printf(m, ",user_id=%u", from_kuid_munged(&init_user_ns, fc->user_id));
        seq_printf(m, ",group_id=%u", from_kgid_munged(&init_user_ns, fc->group_id));
        if (fc->flags & FUSE_DEFAULT_PERMISSIONS)
@@ -1042,6 +1043,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)

        fc->release = fuse_free_conn;
        fc->flags = d.flags;
+       fc->pid = current->pid;
        fc->user_id = d.user_id;
        fc->group_id = d.group_id;
        fc->max_read = max_t(unsigned, 4096, d.max_read);
def@fractal [6:37] ~/p/linux -- master

I booted the kernel, mounted sshfs, ran mount:

root@192.168.1.1:/tmp on /root/tmp type fuse.sshfs (rw,nosuid,nodev,relatime,pid=1549,user_id=0,group_id=0)

Success? Unfortunately, not:

root      1552  0.0  0.0  45152   332 ?        Ssl  13:39   0:00 sshfs root@192.168.1.1:/tmp tmp                                                                

Then I realized: the remaining sshfs process is a child of the one that created the mount. It inherited from the fd. As fuse is implemented, we could have a multitude of processes inheriting from the fd. We could have the fd passed around in UNIX sockets, completely out of the original process tree.

We can obtain the 'who owns this TCP port' information, because sockets have this meta data, and simply parsing /proc tells us that information. Unfortunately, the fuse fd is a regular fd on /dev/fuse. Unless that fd somehow becomes special, I don't see how this can be implemented.

2

I think you can use a combination of ps and lsof to get the information you want.

The userspace application seems to always have the argument of the mount point as an argument. If you grep through your process list looking for commands with the mount point as an argument (e.g. ps -ef | egrep ' <mount>( |$)'), you should get all FUSE processes. Of course, you may also get other processes that happen to also have the mount point as an argument (e.g. ls <mount>). If you take the resulting list and run it through lsof and look for processes with an open connection to /dev/fuse, you'll find FUSE-related processes for your mount point.

So, for example, I have a gvfs FUSE process on my system:

gvfsd-fuse on /run/user/1000/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)

If I run a simple shell command that does all my ps and lsof work for me, I get something like this:

~$ for pid in \
     $(ps -ef | egrep ' /run/user/1000/gvfs( |$)' | awk '{print $2}')
   do 
     ps -f -p $(lsof -p "$pid" | fgrep /dev/fuse | awk '{print $2}')
   done
UID        PID  PPID  C STIME TTY          TIME CMD
username  2241  1997  0 Apr25 ?        00:00:00 /usr/lib/gvfs/gvfsd-fuse /run/user/1000/gvfs -f -o big_writes

That seems to do what you want. It would be nice if there was a FUSE-level kernel interface to tell you the mappings between PID or filehandle and mount point, but that doesn't seem to exist.

hrunting
  • 796
  • As you say, this assumes that the process took the mount point as an argument, which is often but not always the case. Furthermore there's no reliable way of distinguishing the filesystem-implementing process from other processes that may have that same directory name in their command line. – Gilles 'SO- stop being evil' Apr 29 '15 at 08:30
  • I think I explained both of those items fairly well. First, it doesn't matter whether the process that implements the filesystem takes the mount point as an argument; it only matters that when the process is called, the mount point is passed as an argument (the process can choose to ignore it). I think mount.fuse does this regardless of whether the implementing process expects an argument or not, so I don't think it's up the process whether the argument appears on the command line. – hrunting Apr 29 '15 at 11:59
  • Second, the reliable way of distinguishing a filesystem-implementing process from other processes is the lsof command looking for an open filehandle to /dev/fuse. I could craft a condition where that wouldn't be the case (a specific process called with the mount-point argument that holds open /dev/fuse), but outside of specific instances trying to misrepresent themselves as filesystem-implementing processes, the solution should reliably hold. – hrunting Apr 29 '15 at 11:59