diff --git a/fs/internal.h b/fs/internal.h index 204e1e253283..dffebf7b32f3 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -68,6 +68,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(struct vfsmount *); extern int __mnt_want_write_file(struct file *); diff --git a/fs/namespace.c b/fs/namespace.c index dc4424ae82de..9ee8bcf6e425 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1739,6 +1739,52 @@ static inline bool may_mandlock(void) } #endif +/** + * path_mounted - check whether path is mounted + * @path: path to check + * + * Determine whether @path refers to the root of a mount. + * + * Return: true if @path is the root of a mount, false if not. + */ +static inline bool path_mounted(const struct path *path) +{ + return path->mnt->mnt_root == path->dentry; +} + +static int can_umount(const struct path *path, int flags) +{ + struct mount *mnt = real_mount(path->mnt); + + if (!may_mount()) + return -EPERM; + if (!path_mounted(path)) + return -EINVAL; + if (!check_mnt(mnt)) + return -EINVAL; + if (mnt->mnt.mnt_flags & MNT_LOCKED) /* Check optimistically */ + return -EINVAL; + if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN)) + return -EPERM; + return 0; +} + +// caller is responsible for flags being sane +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); + + /* we mustn't call path_put() as that would clear mnt_expiry_mark */ + dput(path->dentry); + mntput_no_expire(mnt); + return ret; +} + /* * Now umount can handle mount points as well as block devices. * This is important for filesystems which use unnamed block devices.