@@ -31,6 +31,7 @@
#include <linux/backing-dev.h>
#include <linux/exportfs.h>
#include <linux/fiemap.h>
+#include <linux/fs_context.h>
#include <linux/module.h>
#include <linux/pagemap.h>
#include <linux/posix_acl.h>
@@ -1708,16 +1709,12 @@ static struct bch_fs *bch2_path_to_fs(const char *path)
return c ?: ERR_PTR(-ENOENT);
}
-static int bch2_remount(struct super_block *sb, int *flags, char *data)
+static int bch2_remount(struct super_block *sb, int *flags,
+ struct bch_opts opts)
{
struct bch_fs *c = sb->s_fs_info;
- struct bch_opts opts = bch2_opts_empty();
int ret;
- ret = bch2_parse_mount_opts(c, &opts, NULL, data);
- if (ret)
- goto err;
-
opt_set(opts, read_only, (*flags & SB_RDONLY) != 0);
if (opts.read_only != c->opts.read_only) {
@@ -1843,7 +1840,6 @@ static const struct super_operations bch_super_operations = {
.statfs = bch2_statfs,
.show_devname = bch2_show_devname,
.show_options = bch2_show_options,
- .remount_fs = bch2_remount,
.put_super = bch2_put_super,
.freeze_fs = bch2_freeze,
.unfreeze_fs = bch2_unfreeze,
@@ -1877,22 +1873,17 @@ static int bch2_test_super(struct super_block *s, void *data)
}
static struct dentry *bch2_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
+ int flags, const char *dev_name,
+ struct bch2_opts_parse opts_parse)
{
struct bch_fs *c;
struct super_block *sb;
struct inode *vinode;
- struct bch_opts opts = bch2_opts_empty();
+ struct bch_opts opts = opts_parse.opts;
int ret;
opt_set(opts, read_only, (flags & SB_RDONLY) != 0);
- ret = bch2_parse_mount_opts(NULL, &opts, NULL, data);
- if (ret) {
- ret = bch2_err_class(ret);
- return ERR_PTR(ret);
- }
-
if (!dev_name || strlen(dev_name) == 0)
return ERR_PTR(-EINVAL);
@@ -1921,7 +1912,7 @@ static struct dentry *bch2_mount(struct file_system_type *fs_type,
}
/* Some options can't be parsed until after the fs is started: */
- ret = bch2_parse_mount_opts(c, &opts, NULL, data);
+ ret = bch2_parse_mount_opts(c, &opts, NULL, opts_parse.parse_later.buf);
if (ret) {
bch2_fs_stop(c);
sb = ERR_PTR(ret);
@@ -2027,12 +2018,92 @@ static void bch2_kill_sb(struct super_block *sb)
bch2_fs_free(c);
}
+static void bch2_fs_context_free(struct fs_context *fc)
+{
+ struct bch2_opts_parse *opts = fc->fs_private;
+
+ if (opts) {
+ printbuf_exit(&opts->parse_later);
+ kfree(opts);
+ }
+}
+
+static int bch2_fs_parse_param(struct fs_context *fc,
+ struct fs_parameter *param)
+{
+ /*
+ * the "source" param, i.e., the name of the device(s) to mount,
+ * is handled by the VFS layer.
+ */
+ if (!strcmp(param->key, "source"))
+ return -ENOPARAM;
+
+ struct bch2_opts_parse *opts = fc->fs_private;
+ struct bch_fs *c = NULL;
+
+ /* for reconfigure, we already have a struct bch_fs */
+ if (fc->root)
+ c = fc->root->d_sb->s_fs_info;
+
+ int ret = bch2_parse_one_mount_opt(c, &opts->opts,
+ &opts->parse_later, param->key,
+ param->string);
+
+ return bch2_err_class(ret);
+}
+
+static int bch2_fs_get_tree(struct fs_context *fc)
+{
+ struct bch2_opts_parse *opts = fc->fs_private;
+ const char *dev_name = fc->source;
+ struct dentry *root;
+
+ root = bch2_mount(fc->fs_type, fc->sb_flags, dev_name, *opts);
+
+ if (IS_ERR(root))
+ return PTR_ERR(root);
+
+ fc->root = root;
+
+ return 0;
+}
+
+static int bch2_fs_reconfigure(struct fs_context *fc)
+{
+ struct super_block *sb = fc->root->d_sb;
+ struct bch2_opts_parse *opts = fc->fs_private;
+
+ return bch2_remount(sb, &fc->sb_flags, opts->opts);
+}
+
+static const struct fs_context_operations bch2_context_ops = {
+ .free = bch2_fs_context_free,
+ .parse_param = bch2_fs_parse_param,
+ .get_tree = bch2_fs_get_tree,
+ .reconfigure = bch2_fs_reconfigure,
+};
+
+static int bch2_init_fs_context(struct fs_context *fc)
+{
+ struct bch2_opts_parse *opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+
+ if (!opts)
+ return -ENOMEM;
+
+ opts->parse_later = PRINTBUF;
+
+ fc->ops = &bch2_context_ops;
+ fc->fs_private = opts;
+
+ return 0;
+}
+
static struct file_system_type bcache_fs_type = {
- .owner = THIS_MODULE,
- .name = "bcachefs",
- .mount = bch2_mount,
- .kill_sb = bch2_kill_sb,
- .fs_flags = FS_REQUIRES_DEV,
+ .owner = THIS_MODULE,
+ .name = "bcachefs",
+ .init_fs_context = bch2_init_fs_context,
+ .kill_sb = bch2_kill_sb,
+ .fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("bcachefs");
@@ -488,6 +488,13 @@ struct bch_opts {
#undef x
};
+struct bch2_opts_parse {
+ struct bch_opts opts;
+
+ /* to save opts that can't be parsed before the FS is opened: */
+ struct printbuf parse_later;
+};
+
static const __maybe_unused struct bch_opts bch2_opts_default = {
#define x(_name, _bits, _mode, _type, _sb_opt, _default, ...) \
._name##_defined = true, \
This updates bcachefs to use the new mount API: - Update the file_system_type to use the new init_fs_context() function. - Define the new fs_context_operations functions. - No longer register bch2_mount() and bch2_remount(); these are now called via the new fs_context functions. - Define a new helper type, bch2_opts_parse that includes a struct bch_opts and additionally a printbuf used to save options that can't be parsed until after the FS is opened. This enables us to parse as many options as possible prior to opening the filesystem while saving those options that need the open FS for later parsing. Signed-off-by: Thomas Bertschinger <tahbertschinger@gmail.com> --- fs/bcachefs/fs.c | 113 ++++++++++++++++++++++++++++++++++++--------- fs/bcachefs/opts.h | 7 +++ 2 files changed, 99 insertions(+), 21 deletions(-)