aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2018-11-01 19:07:25 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2019-02-28 03:29:26 -0500
commit3e1aeb00e6d132efc151dacc062b38269bc9eccc (patch)
treed6cb75c4e0540cdd94e381c2afcacd6d7142fb30
parent846e56621897a63966b7f03a70be29060394c363 (diff)
vfs: Implement a filesystem superblock creation/configuration context
[AV - unfuck kern_mount_data(); we want non-NULL ->mnt_ns on long-living mounts] [AV - reordering fs/namespace.c is badly overdue, but let's keep it separate from that series] [AV - drop simple_pin_fs() change] [AV - clean vfs_kern_mount() failure exits up] Implement a filesystem context concept to be used during superblock creation for mount and superblock reconfiguration for remount. The mounting procedure then becomes: (1) Allocate new fs_context context. (2) Configure the context. (3) Create superblock. (4) Query the superblock. (5) Create a mount for the superblock. (6) Destroy the context. Rather than calling fs_type->mount(), an fs_context struct is created and fs_type->init_fs_context() is called to set it up. Pointers exist for the filesystem and LSM to hang their private data off. A set of operations has to be set by ->init_fs_context() to provide freeing, duplication, option parsing, binary data parsing, validation, mounting and superblock filling. Legacy filesystems are supported by the provision of a set of legacy fs_context operations that build up a list of mount options and then invoke fs_type->mount() from within the fs_context ->get_tree() operation. This allows all filesystems to be accessed using fs_context. It should be noted that, whilst this patch adds a lot of lines of code, there is quite a bit of duplication with existing code that can be eliminated should all filesystems be converted over. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/filesystems.c4
-rw-r--r--fs/fs_context.c300
-rw-r--r--fs/namespace.c25
-rw-r--r--include/linux/fs.h2
-rw-r--r--include/linux/fs_context.h5
5 files changed, 319 insertions, 17 deletions
diff --git a/fs/filesystems.c b/fs/filesystems.c
index b03f57b1105b..9135646e41ac 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -16,6 +16,7 @@
16#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/slab.h> 17#include <linux/slab.h>
18#include <linux/uaccess.h> 18#include <linux/uaccess.h>
19#include <linux/fs_parser.h>
19 20
20/* 21/*
21 * Handling of filesystem drivers list. 22 * Handling of filesystem drivers list.
@@ -73,6 +74,9 @@ int register_filesystem(struct file_system_type * fs)
73 int res = 0; 74 int res = 0;
74 struct file_system_type ** p; 75 struct file_system_type ** p;
75 76
77 if (fs->parameters && !fs_validate_description(fs->parameters))
78 return -EINVAL;
79
76 BUG_ON(strchr(fs->name, '.')); 80 BUG_ON(strchr(fs->name, '.'));
77 if (fs->next) 81 if (fs->next)
78 return -EBUSY; 82 return -EBUSY;
diff --git a/fs/fs_context.c b/fs/fs_context.c
index 825d1b2c8807..aa7e0ffb591a 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -12,6 +12,7 @@
12 12
13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14#include <linux/fs_context.h> 14#include <linux/fs_context.h>
15#include <linux/fs_parser.h>
15#include <linux/fs.h> 16#include <linux/fs.h>
16#include <linux/mount.h> 17#include <linux/mount.h>
17#include <linux/nsproxy.h> 18#include <linux/nsproxy.h>
@@ -25,13 +26,217 @@
25#include "mount.h" 26#include "mount.h"
26#include "internal.h" 27#include "internal.h"
27 28
29enum legacy_fs_param {
30 LEGACY_FS_UNSET_PARAMS,
31 LEGACY_FS_MONOLITHIC_PARAMS,
32 LEGACY_FS_INDIVIDUAL_PARAMS,
33};
34
28struct legacy_fs_context { 35struct legacy_fs_context {
29 char *legacy_data; /* Data page for legacy filesystems */ 36 char *legacy_data; /* Data page for legacy filesystems */
30 size_t data_size; 37 size_t data_size;
38 enum legacy_fs_param param_type;
31}; 39};
32 40
33static int legacy_init_fs_context(struct fs_context *fc); 41static int legacy_init_fs_context(struct fs_context *fc);
34 42
43static const struct constant_table common_set_sb_flag[] = {
44 { "dirsync", SB_DIRSYNC },
45 { "lazytime", SB_LAZYTIME },
46 { "mand", SB_MANDLOCK },
47 { "posixacl", SB_POSIXACL },
48 { "ro", SB_RDONLY },
49 { "sync", SB_SYNCHRONOUS },
50};
51
52static const struct constant_table common_clear_sb_flag[] = {
53 { "async", SB_SYNCHRONOUS },
54 { "nolazytime", SB_LAZYTIME },
55 { "nomand", SB_MANDLOCK },
56 { "rw", SB_RDONLY },
57 { "silent", SB_SILENT },
58};
59
60static const char *const forbidden_sb_flag[] = {
61 "bind",
62 "dev",
63 "exec",
64 "move",
65 "noatime",
66 "nodev",
67 "nodiratime",
68 "noexec",
69 "norelatime",
70 "nostrictatime",
71 "nosuid",
72 "private",
73 "rec",
74 "relatime",
75 "remount",
76 "shared",
77 "slave",
78 "strictatime",
79 "suid",
80 "unbindable",
81};
82
83/*
84 * Check for a common mount option that manipulates s_flags.
85 */
86static int vfs_parse_sb_flag(struct fs_context *fc, const char *key)
87{
88 unsigned int token;
89 unsigned int i;
90
91 for (i = 0; i < ARRAY_SIZE(forbidden_sb_flag); i++)
92 if (strcmp(key, forbidden_sb_flag[i]) == 0)
93 return -EINVAL;
94
95 token = lookup_constant(common_set_sb_flag, key, 0);
96 if (token) {
97 fc->sb_flags |= token;
98 fc->sb_flags_mask |= token;
99 return 0;
100 }
101
102 token = lookup_constant(common_clear_sb_flag, key, 0);
103 if (token) {
104 fc->sb_flags &= ~token;
105 fc->sb_flags_mask |= token;
106 return 0;
107 }
108
109 return -ENOPARAM;
110}
111
112/**
113 * vfs_parse_fs_param - Add a single parameter to a superblock config
114 * @fc: The filesystem context to modify
115 * @param: The parameter
116 *
117 * A single mount option in string form is applied to the filesystem context
118 * being set up. Certain standard options (for example "ro") are translated
119 * into flag bits without going to the filesystem. The active security module
120 * is allowed to observe and poach options. Any other options are passed over
121 * to the filesystem to parse.
122 *
123 * This may be called multiple times for a context.
124 *
125 * Returns 0 on success and a negative error code on failure. In the event of
126 * failure, supplementary error information may have been set.
127 */
128int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param)
129{
130 int ret;
131
132 if (!param->key)
133 return invalf(fc, "Unnamed parameter\n");
134
135 ret = vfs_parse_sb_flag(fc, param->key);
136 if (ret != -ENOPARAM)
137 return ret;
138
139 ret = security_fs_context_parse_param(fc, param);
140 if (ret != -ENOPARAM)
141 /* Param belongs to the LSM or is disallowed by the LSM; so
142 * don't pass to the FS.
143 */
144 return ret;
145
146 if (fc->ops->parse_param) {
147 ret = fc->ops->parse_param(fc, param);
148 if (ret != -ENOPARAM)
149 return ret;
150 }
151
152 /* If the filesystem doesn't take any arguments, give it the
153 * default handling of source.
154 */
155 if (strcmp(param->key, "source") == 0) {
156 if (param->type != fs_value_is_string)
157 return invalf(fc, "VFS: Non-string source");
158 if (fc->source)
159 return invalf(fc, "VFS: Multiple sources");
160 fc->source = param->string;
161 param->string = NULL;
162 return 0;
163 }
164
165 return invalf(fc, "%s: Unknown parameter '%s'",
166 fc->fs_type->name, param->key);
167}
168EXPORT_SYMBOL(vfs_parse_fs_param);
169
170/**
171 * vfs_parse_fs_string - Convenience function to just parse a string.
172 */
173int vfs_parse_fs_string(struct fs_context *fc, const char *key,
174 const char *value, size_t v_size)
175{
176 int ret;
177
178 struct fs_parameter param = {
179 .key = key,
180 .type = fs_value_is_string,
181 .size = v_size,
182 };
183
184 if (v_size > 0) {
185 param.string = kmemdup_nul(value, v_size, GFP_KERNEL);
186 if (!param.string)
187 return -ENOMEM;
188 }
189
190 ret = vfs_parse_fs_param(fc, &param);
191 kfree(param.string);
192 return ret;
193}
194EXPORT_SYMBOL(vfs_parse_fs_string);
195
196/**
197 * generic_pa