blob: 5fce6347de7a1f276d872e6b5129c0c5fa1178b2 [file] [log] [blame]
David Howells24dcb3d2018-11-01 23:33:31 +00001/* Filesystem access-by-fd.
2 *
3 * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11
12#include <linux/fs_context.h>
13#include <linux/slab.h>
14#include <linux/uaccess.h>
15#include <linux/syscalls.h>
16#include <linux/security.h>
17#include <linux/anon_inodes.h>
18#include <linux/namei.h>
19#include <linux/file.h>
20#include <uapi/linux/mount.h>
21#include "mount.h"
22
David Howells007ec262018-11-01 23:34:29 +000023/*
24 * Allow the user to read back any error, warning or informational messages.
25 */
26static ssize_t fscontext_read(struct file *file,
27 char __user *_buf, size_t len, loff_t *pos)
28{
29 struct fs_context *fc = file->private_data;
30 struct fc_log *log = fc->log;
31 unsigned int logsize = ARRAY_SIZE(log->buffer);
32 ssize_t ret;
33 char *p;
34 bool need_free;
35 int index, n;
36
37 ret = mutex_lock_interruptible(&fc->uapi_mutex);
38 if (ret < 0)
39 return ret;
40
41 if (log->head == log->tail) {
42 mutex_unlock(&fc->uapi_mutex);
43 return -ENODATA;
44 }
45
46 index = log->tail & (logsize - 1);
47 p = log->buffer[index];
48 need_free = log->need_free & (1 << index);
49 log->buffer[index] = NULL;
50 log->need_free &= ~(1 << index);
51 log->tail++;
52 mutex_unlock(&fc->uapi_mutex);
53
54 ret = -EMSGSIZE;
55 n = strlen(p);
56 if (n > len)
57 goto err_free;
58 ret = -EFAULT;
59 if (copy_to_user(_buf, p, n) != 0)
60 goto err_free;
61 ret = n;
62
63err_free:
64 if (need_free)
65 kfree(p);
66 return ret;
67}
68
David Howells24dcb3d2018-11-01 23:33:31 +000069static int fscontext_release(struct inode *inode, struct file *file)
70{
71 struct fs_context *fc = file->private_data;
72
73 if (fc) {
74 file->private_data = NULL;
75 put_fs_context(fc);
76 }
77 return 0;
78}
79
80const struct file_operations fscontext_fops = {
David Howells007ec262018-11-01 23:34:29 +000081 .read = fscontext_read,
David Howells24dcb3d2018-11-01 23:33:31 +000082 .release = fscontext_release,
83 .llseek = no_llseek,
84};
85
86/*
87 * Attach a filesystem context to a file and an fd.
88 */
89static int fscontext_create_fd(struct fs_context *fc, unsigned int o_flags)
90{
91 int fd;
92
93 fd = anon_inode_getfd("fscontext", &fscontext_fops, fc,
94 O_RDWR | o_flags);
95 if (fd < 0)
96 put_fs_context(fc);
97 return fd;
98}
99
David Howells007ec262018-11-01 23:34:29 +0000100static int fscontext_alloc_log(struct fs_context *fc)
101{
102 fc->log = kzalloc(sizeof(*fc->log), GFP_KERNEL);
103 if (!fc->log)
104 return -ENOMEM;
105 refcount_set(&fc->log->usage, 1);
106 fc->log->owner = fc->fs_type->owner;
107 return 0;
108}
109
David Howells24dcb3d2018-11-01 23:33:31 +0000110/*
111 * Open a filesystem by name so that it can be configured for mounting.
112 *
113 * We are allowed to specify a container in which the filesystem will be
114 * opened, thereby indicating which namespaces will be used (notably, which
115 * network namespace will be used for network filesystems).
116 */
117SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags)
118{
119 struct file_system_type *fs_type;
120 struct fs_context *fc;
121 const char *fs_name;
David Howells007ec262018-11-01 23:34:29 +0000122 int ret;
David Howells24dcb3d2018-11-01 23:33:31 +0000123
124 if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
125 return -EPERM;
126
127 if (flags & ~FSOPEN_CLOEXEC)
128 return -EINVAL;
129
130 fs_name = strndup_user(_fs_name, PAGE_SIZE);
131 if (IS_ERR(fs_name))
132 return PTR_ERR(fs_name);
133
134 fs_type = get_fs_type(fs_name);
135 kfree(fs_name);
136 if (!fs_type)
137 return -ENODEV;
138
139 fc = fs_context_for_mount(fs_type, 0);
140 put_filesystem(fs_type);
141 if (IS_ERR(fc))
142 return PTR_ERR(fc);
143
144 fc->phase = FS_CONTEXT_CREATE_PARAMS;
David Howells007ec262018-11-01 23:34:29 +0000145
146 ret = fscontext_alloc_log(fc);
147 if (ret < 0)
148 goto err_fc;
149
David Howells24dcb3d2018-11-01 23:33:31 +0000150 return fscontext_create_fd(fc, flags & FSOPEN_CLOEXEC ? O_CLOEXEC : 0);
David Howells007ec262018-11-01 23:34:29 +0000151
152err_fc:
153 put_fs_context(fc);
154 return ret;
David Howells24dcb3d2018-11-01 23:33:31 +0000155}