diff --git a/fs/internal.h b/fs/internal.h index 377f984e9226..9badc3b8cc11 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -85,6 +85,7 @@ extern int finish_automount(struct vfsmount *, struct path *); extern int sb_prepare_remount_readonly(struct super_block *); extern void __init mnt_init(void); +int path_umount(struct path *path, int flags); extern int __mnt_want_write_file(struct file *); extern void __mnt_drop_write_file(struct file *); diff --git a/fs/namespace.c b/fs/namespace.c index 7955b65334e8..52804361aaff 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1750,6 +1750,36 @@ SYSCALL_DEFINE1(oldumount, char __user *, name) #endif +static int can_umount(const struct path *path, int flags) + { + struct mount *mnt = real_mount(path->mnt); + if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) + return -EINVAL; + if (!may_mount()) + return -EPERM; + if (path->dentry != path->mnt->mnt_root) + return -EINVAL; + if (!check_mnt(mnt)) + return -EINVAL; + if (mnt->mnt.mnt_flags & MNT_LOCKED) + return -EINVAL; + if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN)) + return -EPERM; + return 0; + } + +int path_umount(struct path *path, int flags) + { + struct mount *mnt = real_mount(path->mnt); + int ret; + ret = can_umount(path, flags); + if (!ret) + ret = do_umount(mnt, flags); + dput(path->dentry); + mntput_no_expire(mnt); + return ret; + } + static bool is_mnt_ns_file(struct dentry *dentry) { /* Is this a proxy for a mount namespace? */