diff options
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 59 |
1 files changed, 34 insertions, 25 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 0570729c87fd..4fb1691b4355 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1584,6 +1584,14 @@ static inline bool may_mount(void) return ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN); } +static inline bool may_mandlock(void) +{ +#ifndef CONFIG_MANDATORY_FILE_LOCKING + return false; +#endif + return capable(CAP_SYS_ADMIN); +} + /* * Now umount can handle mount points as well as block devices. * This is important for filesystems which use unnamed block devices. @@ -1953,9 +1961,9 @@ static struct mountpoint *lock_mount(struct path *path) struct vfsmount *mnt; struct dentry *dentry = path->dentry; retry: - mutex_lock(&dentry->d_inode->i_mutex); + inode_lock(dentry->d_inode); if (unlikely(cant_mount(dentry))) { - mutex_unlock(&dentry->d_inode->i_mutex); + inode_unlock(dentry->d_inode); return ERR_PTR(-ENOENT); } namespace_lock(); @@ -1966,13 +1974,13 @@ retry: mp = new_mountpoint(dentry); if (IS_ERR(mp)) { namespace_unlock(); - mutex_unlock(&dentry->d_inode->i_mutex); + inode_unlock(dentry->d_inode); return mp; } return mp; } namespace_unlock(); - mutex_unlock(&path->dentry->d_inode->i_mutex); + inode_unlock(path->dentry->d_inode); path_put(path); path->mnt = mnt; dentry = path->dentry = dget(mnt->mnt_root); @@ -1984,7 +1992,7 @@ static void unlock_mount(struct mountpoint *where) struct dentry *dentry = where->m_dentry; put_mountpoint(where); namespace_unlock(); - mutex_unlock(&dentry->d_inode->i_mutex); + inode_unlock(dentry->d_inode); } static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp) @@ -2601,18 +2609,18 @@ static long exact_copy_from_user(void *to, const void __user * from, return n; } -int copy_mount_options(const void __user * data, unsigned long *where) +void *copy_mount_options(const void __user * data) { int i; - unsigned long page; unsigned long size; + char *copy; - *where = 0; if (!data) - return 0; + return NULL; - if (!(page = __get_free_page(GFP_KERNEL))) - return -ENOMEM; + copy = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!copy) + return ERR_PTR(-ENOMEM); /* We only care that *some* data at the address the user * gave us is valid. Just in case, we'll zero @@ -2623,15 +2631,14 @@ int copy_mount_options(const void __user * data, unsigned long *where) if (size > PAGE_SIZE) size = PAGE_SIZE; - i = size - exact_copy_from_user((void *)page, data, size); + i = size - exact_copy_from_user(copy, data, size); if (!i) { - free_page(page); - return -EFAULT; + kfree(copy); + return ERR_PTR(-EFAULT); } if (i != PAGE_SIZE) - memset((char *)page + i, 0, PAGE_SIZE - i); - *where = page; - return 0; + memset(copy + i, 0, PAGE_SIZE - i); + return copy; } char *copy_mount_string(const void __user *data) @@ -2677,6 +2684,8 @@ long do_mount(const char *dev_name, const char __user *dir_name, type_page, flags, data_page); if (!retval && !may_mount()) retval = -EPERM; + if (!retval && (flags & MS_MANDLOCK) && !may_mandlock()) + retval = -EPERM; if (retval) goto dput_out; @@ -2896,7 +2905,7 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, int ret; char *kernel_type; char *kernel_dev; - unsigned long data_page; + void *options; kernel_type = copy_mount_string(type); ret = PTR_ERR(kernel_type); @@ -2908,14 +2917,14 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, if (IS_ERR(kernel_dev)) goto out_dev; - ret = copy_mount_options(data, &data_page); - if (ret < 0) + options = copy_mount_options(data); + ret = PTR_ERR(options); + if (IS_ERR(options)) goto out_data; - ret = do_mount(kernel_dev, dir_name, kernel_type, flags, - (void *) data_page); + ret = do_mount(kernel_dev, dir_name, kernel_type, flags, options); - free_page(data_page); + kfree(options); out_data: kfree(kernel_dev); out_dev: @@ -2939,9 +2948,9 @@ bool is_path_reachable(struct mount *mnt, struct dentry *dentry, return &mnt->mnt == root->mnt && is_subdir(dentry, root->dentry); } -int path_is_under(struct path *path1, struct path *path2) +bool path_is_under(struct path *path1, struct path *path2) { - int res; + bool res; read_seqlock_excl(&mount_lock); res = is_path_reachable(real_mount(path1->mnt), path1->dentry, path2); read_sequnlock_excl(&mount_lock); |