To cleanup its mountpoint a fuse application registres signal hook that calls
'fusermount' tool. But in case of abnormal exit (SIGSEGV, SIGKILL) application
has no chance to call fusermount and the mountpoint is left in inconsistent
state (it returns ENOTCONN error).
There is an option that was added recently "auto_unmount" but it utilizes
user-space daemon and not very reliable (it also can be killed with SIGKILL).
Instead we implement unmount on '/dev/fuse' file close. With it there is no
need to use 'auto_unmount' or call 'fusermount' on shutdown but we keep it for
compatibility with old kernels.
Current implementation unmounts original mountpoint and all bind mounts. So
it differs from original implementation that called 'fusermount' only on
original mount.
Note that both fusermount and kernel style mount cleanup unmounts filesystem
only in current process namespace. If daemon changed filesystem namespace
then those mountpoints are left untouched.
Tested: run a fuse filesystem and tried to kill it different ways:
SIGTERM, SIGKILL, "umount dir". Check that it also works in case of bind mounts.
Google-Bug-Id: 7718269
Change-Id: I0838b40e1e3c9328c76674d5043b7a700b9053b7
Signed-off-by: Anatol Pomozov <***@gmail.com>
---
fs/fuse/dev.c | 34 ++++++++++++++++++++++++++++++++++
fs/namespace.c | 10 ++++++++--
include/linux/mount.h | 1 +
3 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index e9bdec0..4f592b7 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -19,6 +19,10 @@
#include <linux/pipe_fs_i.h>
#include <linux/swap.h>
#include <linux/splice.h>
+#include <linux/nsproxy.h>
+#include <linux/mount.h>
+
+#include "../mount.h"
MODULE_ALIAS_MISCDEV(FUSE_MINOR);
MODULE_ALIAS("devname:fuse");
@@ -2082,10 +2086,34 @@ void fuse_abort_conn(struct fuse_conn *fc)
}
EXPORT_SYMBOL_GPL(fuse_abort_conn);
+static void fuse_umount(struct super_block *sb)
+{
+ struct nsproxy *nsp = task_nsproxy(current);
+ struct mnt_namespace *ns = nsp->mnt_ns;
+ struct mount *mnt, *tmp;
+
+ list_for_each_entry_safe(mnt, tmp, &ns->list, mnt_list) {
+ struct vfsmount *vfsmnt = &mnt->mnt;
+ if (vfsmnt->mnt_sb == sb) {
+ /* in case of mount binds there can be more than one
+ * mountpoint that corresponds to sb
+ */
+ mntget(vfsmnt);
+ do_umount(vfsmnt, 0);
+ mntput(vfsmnt);
+
+ /* TODO: better debug message? */
+ pr_debug("fuse: mountpoint (%d,%d) automatically unmounted\n",
+ MAJOR(sb->s_dev), MINOR(sb->s_dev));
+ }
+ }
+}
+
int fuse_dev_release(struct inode *inode, struct file *file)
{
struct fuse_conn *fc = fuse_get_conn(file);
if (fc) {
+ struct super_block *sb = fc->sb;
spin_lock(&fc->lock);
fc->connected = 0;
fc->blocked = 0;
@@ -2094,6 +2122,12 @@ int fuse_dev_release(struct inode *inode, struct file *file)
wake_up_all(&fc->blocked_waitq);
spin_unlock(&fc->lock);
fuse_conn_put(fc);
+
+ /* super block might already be NULL if we killed this fs by
+ * "umount"
+ */
+ if (sb)
+ fuse_umount(sb);
}
return 0;
diff --git a/fs/namespace.c b/fs/namespace.c
index 55605c5..d7496d8 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1147,7 +1147,7 @@ void umount_tree(struct mount *mnt, int propagate, struct list_head *kill)
static void shrink_submounts(struct mount *mnt, struct list_head *umounts);
-static int do_umount(struct mount *mnt, int flags)
+static int __do_umount(struct mount *mnt, int flags)
{
struct super_block *sb = mnt->mnt.mnt_sb;
int retval;
@@ -1237,6 +1237,12 @@ static int do_umount(struct mount *mnt, int flags)
return retval;
}
+int do_umount(struct vfsmount *mnt, int flags)
+{
+ return __do_umount(real_mount(mnt), flags);
+}
+EXPORT_SYMBOL(do_umount);
+
/*
* Now umount can handle mount points as well as block devices.
* This is important for filesystems which use unnamed block devices.
@@ -1272,7 +1278,7 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
if (!ns_capable(mnt->mnt_ns->user_ns, CAP_SYS_ADMIN))
goto dput_and_out;
- retval = do_umount(mnt, flags);
+ retval = __do_umount(mnt, flags);
dput_and_out:
/* we mustn't call path_put() as that would clear mnt_expiry_mark */
dput(path.dentry);
diff --git a/include/linux/mount.h b/include/linux/mount.h
index d7029f4..333c1e8 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -65,6 +65,7 @@ extern struct vfsmount *mntget(struct vfsmount *mnt);
extern void mnt_pin(struct vfsmount *mnt);
extern void mnt_unpin(struct vfsmount *mnt);
extern int __mnt_is_readonly(struct vfsmount *mnt);
+extern int do_umount(struct vfsmount *mnt, int flags);
struct file_system_type;
extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
--
1.8.1.3