On Thu, 2019-11-28 at 11:21 +0100, Ilya Dryomov wrote: > From: David Howells <dhowells@xxxxxxxxxx> > > Convert the ceph filesystem to the new internal mount API as the old > one will be obsoleted and removed. This allows greater flexibility in > communication of mount parameters between userspace, the VFS and the > filesystem. > > See Documentation/filesystems/mount_api.txt for more information. > > [ Numerous string handling, leak and regression fixes; rbd conversion > was particularly broken and had to be redone almost from scratch. ] > > Signed-off-by: David Howells <dhowells@xxxxxxxxxx> > Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> > Signed-off-by: Ilya Dryomov <idryomov@xxxxxxxxx> > --- > drivers/block/rbd.c | 262 ++++++++------ > fs/ceph/cache.c | 9 +- > fs/ceph/cache.h | 5 +- > fs/ceph/super.c | 646 ++++++++++++++++++----------------- > fs/ceph/super.h | 1 - > include/linux/ceph/libceph.h | 10 +- > net/ceph/ceph_common.c | 419 +++++++++++------------ > net/ceph/messenger.c | 2 - > 8 files changed, 681 insertions(+), 673 deletions(-) > > Based on last posting by Jeff (v4?): > - replace rbd_parse_monolithic() as it was still going off into > invalid memory on some configuration strings > - back out unrelated changes to next_token(), dup_token() and > rbd_add_parse_args() > - back out dubious changes to source (aka dev_name) parsing logic, > create_fs_client() and ceph_real_mount() > - fix memory leaks in ceph_get_tree(), fc->s_fs_info doesn't own fsc > anymore > - make some parsing failures fatal again (monitor ip(s), etc) > - bring back osdtimeout case to avoid crashes when it's specified > - update log messages for fc-style logging > - propagate fc to more functions for logging (get_secret(), etc) > - remove ceph_config_context > - make parsing behaviour and naming consistent across all three modules > > diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c > index 8798c89a4dc5..77a6b0f87975 100644 > --- a/drivers/block/rbd.c > +++ b/drivers/block/rbd.c > @@ -34,7 +34,7 @@ > #include <linux/ceph/cls_lock_client.h> > #include <linux/ceph/striper.h> > #include <linux/ceph/decode.h> > -#include <linux/parser.h> > +#include <linux/fs_parser.h> > #include <linux/bsearch.h> > > #include <linux/kernel.h> > @@ -838,34 +838,34 @@ enum { > Opt_queue_depth, > Opt_alloc_size, > Opt_lock_timeout, > - Opt_last_int, > /* int args above */ > Opt_pool_ns, > - Opt_last_string, > /* string args above */ > Opt_read_only, > Opt_read_write, > Opt_lock_on_read, > Opt_exclusive, > Opt_notrim, > - Opt_err > }; > > -static match_table_t rbd_opts_tokens = { > - {Opt_queue_depth, "queue_depth=%d"}, > - {Opt_alloc_size, "alloc_size=%d"}, > - {Opt_lock_timeout, "lock_timeout=%d"}, > - /* int args above */ > - {Opt_pool_ns, "_pool_ns=%s"}, > - /* string args above */ > - {Opt_read_only, "read_only"}, > - {Opt_read_only, "ro"}, /* Alternate spelling */ > - {Opt_read_write, "read_write"}, > - {Opt_read_write, "rw"}, /* Alternate spelling */ > - {Opt_lock_on_read, "lock_on_read"}, > - {Opt_exclusive, "exclusive"}, > - {Opt_notrim, "notrim"}, > - {Opt_err, NULL} > +static const struct fs_parameter_spec rbd_param_specs[] = { > + fsparam_u32 ("alloc_size", Opt_alloc_size), > + fsparam_flag ("exclusive", Opt_exclusive), > + fsparam_flag ("lock_on_read", Opt_lock_on_read), > + fsparam_u32 ("lock_timeout", Opt_lock_timeout), > + fsparam_flag ("notrim", Opt_notrim), > + fsparam_string ("_pool_ns", Opt_pool_ns), > + fsparam_u32 ("queue_depth", Opt_queue_depth), > + fsparam_flag ("read_only", Opt_read_only), > + fsparam_flag ("read_write", Opt_read_write), > + fsparam_flag ("ro", Opt_read_only), > + fsparam_flag ("rw", Opt_read_write), > + {} > +}; > + > +static const struct fs_parameter_description rbd_parameters = { > + .name = "rbd", > + .specs = rbd_param_specs, > }; > > struct rbd_options { > @@ -886,87 +886,12 @@ struct rbd_options { > #define RBD_EXCLUSIVE_DEFAULT false > #define RBD_TRIM_DEFAULT true > > -struct parse_rbd_opts_ctx { > +struct rbd_parse_opts_ctx { > struct rbd_spec *spec; > + struct ceph_options *copts; > struct rbd_options *opts; > }; > > -static int parse_rbd_opts_token(char *c, void *private) > -{ > - struct parse_rbd_opts_ctx *pctx = private; > - substring_t argstr[MAX_OPT_ARGS]; > - int token, intval, ret; > - > - token = match_token(c, rbd_opts_tokens, argstr); > - if (token < Opt_last_int) { > - ret = match_int(&argstr[0], &intval); > - if (ret < 0) { > - pr_err("bad option arg (not int) at '%s'\n", c); > - return ret; > - } > - dout("got int token %d val %d\n", token, intval); > - } else if (token > Opt_last_int && token < Opt_last_string) { > - dout("got string token %d val %s\n", token, argstr[0].from); > - } else { > - dout("got token %d\n", token); > - } > - > - switch (token) { > - case Opt_queue_depth: > - if (intval < 1) { > - pr_err("queue_depth out of range\n"); > - return -EINVAL; > - } > - pctx->opts->queue_depth = intval; > - break; > - case Opt_alloc_size: > - if (intval < SECTOR_SIZE) { > - pr_err("alloc_size out of range\n"); > - return -EINVAL; > - } > - if (!is_power_of_2(intval)) { > - pr_err("alloc_size must be a power of 2\n"); > - return -EINVAL; > - } > - pctx->opts->alloc_size = intval; > - break; > - case Opt_lock_timeout: > - /* 0 is "wait forever" (i.e. infinite timeout) */ > - if (intval < 0 || intval > INT_MAX / 1000) { > - pr_err("lock_timeout out of range\n"); > - return -EINVAL; > - } > - pctx->opts->lock_timeout = msecs_to_jiffies(intval * 1000); > - break; > - case Opt_pool_ns: > - kfree(pctx->spec->pool_ns); > - pctx->spec->pool_ns = match_strdup(argstr); > - if (!pctx->spec->pool_ns) > - return -ENOMEM; > - break; > - case Opt_read_only: > - pctx->opts->read_only = true; > - break; > - case Opt_read_write: > - pctx->opts->read_only = false; > - break; > - case Opt_lock_on_read: > - pctx->opts->lock_on_read = true; > - break; > - case Opt_exclusive: > - pctx->opts->exclusive = true; > - break; > - case Opt_notrim: > - pctx->opts->trim = false; > - break; > - default: > - /* libceph prints "bad option" msg */ > - return -EINVAL; > - } > - > - return 0; > -} > - > static char* obj_op_name(enum obj_operation_type op_type) > { > switch (op_type) { > @@ -6423,6 +6348,122 @@ static inline char *dup_token(const char **buf, size_t *lenp) > return dup; > } > > +static int rbd_parse_param(struct fs_parameter *param, > + struct rbd_parse_opts_ctx *pctx) > +{ > + struct rbd_options *opt = pctx->opts; > + struct fs_parse_result result; > + int token, ret; > + > + ret = ceph_parse_param(param, pctx->copts, NULL); > + if (ret != -ENOPARAM) > + return ret; > + > + token = fs_parse(NULL, &rbd_parameters, param, &result); > + dout("%s fs_parse '%s' token %d\n", __func__, param->key, token); > + if (token < 0) { > + if (token == -ENOPARAM) { > + return invalf(NULL, "rbd: Unknown parameter '%s'", > + param->key); > + } > + return token; > + } > + > + switch (token) { > + case Opt_queue_depth: > + if (result.uint_32 < 1) > + goto out_of_range; > + opt->queue_depth = result.uint_32; > + break; > + case Opt_alloc_size: > + if (result.uint_32 < SECTOR_SIZE) > + goto out_of_range; > + if (!is_power_of_2(result.uint_32)) { > + return invalf(NULL, "rbd: alloc_size must be a power of 2"); > + } > + opt->alloc_size = result.uint_32; > + break; > + case Opt_lock_timeout: > + /* 0 is "wait forever" (i.e. infinite timeout) */ > + if (result.uint_32 > INT_MAX / 1000) > + goto out_of_range; > + opt->lock_timeout = msecs_to_jiffies(result.uint_32 * 1000); > + break; > + case Opt_pool_ns: > + kfree(pctx->spec->pool_ns); > + pctx->spec->pool_ns = param->string; > + param->string = NULL; > + break; > + case Opt_read_only: > + opt->read_only = true; > + break; > + case Opt_read_write: > + opt->read_only = false; > + break; > + case Opt_lock_on_read: > + opt->lock_on_read = true; > + break; > + case Opt_exclusive: > + opt->exclusive = true; > + break; > + case Opt_notrim: > + opt->trim = false; > + break; > + default: > + BUG(); > + } > + > + return 0; > + > +out_of_range: > + return invalf(NULL, "rbd: %s out of range", param->key); > +} > + > +/* > + * This duplicates most of generic_parse_monolithic(), untying it from > + * fs_context and skipping standard superblock and security options. > + */ > +static int rbd_parse_options(char *options, struct rbd_parse_opts_ctx *pctx) > +{ > + char *key; > + int ret = 0; > + > + dout("%s '%s'\n", __func__, options); > + while ((key = strsep(&options, ",")) != NULL) { > + if (*key) { > + struct fs_parameter param = { > + .key = key, > + .type = fs_value_is_string, > + }; > + char *value = strchr(key, '='); > + size_t v_len = 0; > + > + if (value) { > + if (value == key) > + continue; > + *value++ = 0; > + v_len = strlen(value); > + } > + > + > + if (v_len > 0) { > + param.string = kmemdup_nul(value, v_len, > + GFP_KERNEL); > + if (!param.string) > + return -ENOMEM; > + } > + param.size = v_len; > + > + ret = rbd_parse_param(¶m, pctx); > + kfree(param.string); > + if (ret) > + break; > + } > + } > + > + return ret; > +} > + > /* > * Parse the options provided for an "rbd add" (i.e., rbd image > * mapping) request. These arrive via a write to /sys/bus/rbd/add, > @@ -6474,8 +6515,7 @@ static int rbd_add_parse_args(const char *buf, > const char *mon_addrs; > char *snap_name; > size_t mon_addrs_size; > - struct parse_rbd_opts_ctx pctx = { 0 }; > - struct ceph_options *copts; > + struct rbd_parse_opts_ctx pctx = { 0 }; > int ret; > > /* The first four tokens are required */ > @@ -6486,7 +6526,7 @@ static int rbd_add_parse_args(const char *buf, > return -EINVAL; > } > mon_addrs = buf; > - mon_addrs_size = len + 1; > + mon_addrs_size = len; > buf += len; > > ret = -EINVAL; > @@ -6536,6 +6576,10 @@ static int rbd_add_parse_args(const char *buf, > *(snap_name + len) = '\0'; > pctx.spec->snap_name = snap_name; > > + pctx.copts = ceph_alloc_options(); > + if (!pctx.copts) > + goto out_mem; > + > /* Initialize all rbd options to the defaults */ > > pctx.opts = kzalloc(sizeof(*pctx.opts), GFP_KERNEL); > @@ -6550,27 +6594,27 @@ static int rbd_add_parse_args(const char *buf, > pctx.opts->exclusive = RBD_EXCLUSIVE_DEFAULT; > pctx.opts->trim = RBD_TRIM_DEFAULT; > > - copts = ceph_parse_options(options, mon_addrs, > - mon_addrs + mon_addrs_size - 1, > - parse_rbd_opts_token, &pctx); > - if (IS_ERR(copts)) { > - ret = PTR_ERR(copts); > + ret = ceph_parse_mon_ips(mon_addrs, mon_addrs_size, pctx.copts, NULL); > + if (ret) > goto out_err; > - } > - kfree(options); > > - *ceph_opts = copts; > + ret = rbd_parse_options(options, &pctx); > + if (ret) > + goto out_err; > + > + *ceph_opts = pctx.copts; > *opts = pctx.opts; > *rbd_spec = pctx.spec; > - > + kfree(options); > return 0; > + > out_mem: > ret = -ENOMEM; > out_err: > kfree(pctx.opts); > + ceph_destroy_options(pctx.copts); > rbd_spec_put(pctx.spec); > kfree(options); > - > return ret; > } > > diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c > index b2ec29eeb4c4..73f24f307a4a 100644 > --- a/fs/ceph/cache.c > +++ b/fs/ceph/cache.c > @@ -8,6 +8,7 @@ > > #include <linux/ceph/ceph_debug.h> > > +#include <linux/fs_context.h> > #include "super.h" > #include "cache.h" > > @@ -49,7 +50,7 @@ void ceph_fscache_unregister(void) > fscache_unregister_netfs(&ceph_cache_netfs); > } > > -int ceph_fscache_register_fs(struct ceph_fs_client* fsc) > +int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc) > { > const struct ceph_fsid *fsid = &fsc->client->fsid; > const char *fscache_uniq = fsc->mount_options->fscache_uniq; > @@ -66,8 +67,8 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc) > if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len)) > continue; > > - pr_err("fscache cookie already registered for fsid %pU\n", fsid); > - pr_err(" use fsc=%%s mount option to specify a uniquifier\n"); > + errorf(fc, "ceph: fscache cookie already registered for fsid %pU, use fsc=<uniquifier> option", > + fsid); > err = -EBUSY; > goto out_unlock; > } > @@ -95,7 +96,7 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc) > list_add_tail(&ent->list, &ceph_fscache_list); > } else { > kfree(ent); > - pr_err("unable to register fscache cookie for fsid %pU\n", > + errorf(fc, "ceph: unable to register fscache cookie for fsid %pU", > fsid); > /* all other fs ignore this error */ > } > diff --git a/fs/ceph/cache.h b/fs/ceph/cache.h > index e486fac3434d..89dbdd1eb14a 100644 > --- a/fs/ceph/cache.h > +++ b/fs/ceph/cache.h > @@ -16,7 +16,7 @@ extern struct fscache_netfs ceph_cache_netfs; > int ceph_fscache_register(void); > void ceph_fscache_unregister(void); > > -int ceph_fscache_register_fs(struct ceph_fs_client* fsc); > +int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc); > void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc); > > void ceph_fscache_register_inode_cookie(struct inode *inode); > @@ -88,7 +88,8 @@ static inline void ceph_fscache_unregister(void) > { > } > > -static inline int ceph_fscache_register_fs(struct ceph_fs_client* fsc) > +static inline int ceph_fscache_register_fs(struct ceph_fs_client* fsc, > + struct fs_context *fc) > { > return 0; > } > diff --git a/fs/ceph/super.c b/fs/ceph/super.c > index b47f43fc2d68..9c9a7c68eea3 100644 > --- a/fs/ceph/super.c > +++ b/fs/ceph/super.c > @@ -9,7 +9,8 @@ > #include <linux/in6.h> > #include <linux/module.h> > #include <linux/mount.h> > -#include <linux/parser.h> > +#include <linux/fs_context.h> > +#include <linux/fs_parser.h> > #include <linux/sched.h> > #include <linux/seq_file.h> > #include <linux/slab.h> > @@ -138,280 +139,308 @@ enum { > Opt_readdir_max_entries, > Opt_readdir_max_bytes, > Opt_congestion_kb, > - Opt_last_int, > /* int args above */ > Opt_snapdirname, > Opt_mds_namespace, > - Opt_fscache_uniq, > Opt_recover_session, > - Opt_last_string, > + Opt_source, > /* string args above */ > Opt_dirstat, > - Opt_nodirstat, > Opt_rbytes, > - Opt_norbytes, > Opt_asyncreaddir, > - Opt_noasyncreaddir, > Opt_dcache, > - Opt_nodcache, > Opt_ino32, > - Opt_noino32, > Opt_fscache, > - Opt_nofscache, > Opt_poolperm, > - Opt_nopoolperm, > Opt_require_active_mds, > - Opt_norequire_active_mds, > -#ifdef CONFIG_CEPH_FS_POSIX_ACL > Opt_acl, > -#endif > - Opt_noacl, > Opt_quotadf, > - Opt_noquotadf, > Opt_copyfrom, > - Opt_nocopyfrom, > }; > > -static match_table_t fsopt_tokens = { > - {Opt_wsize, "wsize=%d"}, > - {Opt_rsize, "rsize=%d"}, > - {Opt_rasize, "rasize=%d"}, > - {Opt_caps_wanted_delay_min, "caps_wanted_delay_min=%d"}, > - {Opt_caps_wanted_delay_max, "caps_wanted_delay_max=%d"}, > - {Opt_caps_max, "caps_max=%d"}, > - {Opt_readdir_max_entries, "readdir_max_entries=%d"}, > - {Opt_readdir_max_bytes, "readdir_max_bytes=%d"}, > - {Opt_congestion_kb, "write_congestion_kb=%d"}, > - /* int args above */ > - {Opt_snapdirname, "snapdirname=%s"}, > - {Opt_mds_namespace, "mds_namespace=%s"}, > - {Opt_recover_session, "recover_session=%s"}, > - {Opt_fscache_uniq, "fsc=%s"}, > - /* string args above */ > - {Opt_dirstat, "dirstat"}, > - {Opt_nodirstat, "nodirstat"}, > - {Opt_rbytes, "rbytes"}, > - {Opt_norbytes, "norbytes"}, > - {Opt_asyncreaddir, "asyncreaddir"}, > - {Opt_noasyncreaddir, "noasyncreaddir"}, > - {Opt_dcache, "dcache"}, > - {Opt_nodcache, "nodcache"}, > - {Opt_ino32, "ino32"}, > - {Opt_noino32, "noino32"}, > - {Opt_fscache, "fsc"}, > - {Opt_nofscache, "nofsc"}, > - {Opt_poolperm, "poolperm"}, > - {Opt_nopoolperm, "nopoolperm"}, > - {Opt_require_active_mds, "require_active_mds"}, > - {Opt_norequire_active_mds, "norequire_active_mds"}, > -#ifdef CONFIG_CEPH_FS_POSIX_ACL > - {Opt_acl, "acl"}, > -#endif > - {Opt_noacl, "noacl"}, > - {Opt_quotadf, "quotadf"}, > - {Opt_noquotadf, "noquotadf"}, > - {Opt_copyfrom, "copyfrom"}, > - {Opt_nocopyfrom, "nocopyfrom"}, > - {-1, NULL} > +enum ceph_recover_session_mode { > + ceph_recover_session_no, > + ceph_recover_session_clean > +}; > + > +static const struct fs_parameter_enum ceph_mount_param_enums[] = { > + { Opt_recover_session, "no", ceph_recover_session_no }, > + { Opt_recover_session, "clean", ceph_recover_session_clean }, > + {} > +}; > + > +static const struct fs_parameter_spec ceph_mount_param_specs[] = { > + fsparam_flag_no ("acl", Opt_acl), > + fsparam_flag_no ("asyncreaddir", Opt_asyncreaddir), > + fsparam_u32 ("caps_max", Opt_caps_max), > + fsparam_u32 ("caps_wanted_delay_max", Opt_caps_wanted_delay_max), > + fsparam_u32 ("caps_wanted_delay_min", Opt_caps_wanted_delay_min), > + fsparam_s32 ("write_congestion_kb", Opt_congestion_kb), > + fsparam_flag_no ("copyfrom", Opt_copyfrom), > + fsparam_flag_no ("dcache", Opt_dcache), > + fsparam_flag_no ("dirstat", Opt_dirstat), > + __fsparam (fs_param_is_string, "fsc", Opt_fscache, > + fs_param_neg_with_no | fs_param_v_optional), > + fsparam_flag_no ("ino32", Opt_ino32), > + fsparam_string ("mds_namespace", Opt_mds_namespace), > + fsparam_flag_no ("poolperm", Opt_poolperm), > + fsparam_flag_no ("quotadf", Opt_quotadf), > + fsparam_u32 ("rasize", Opt_rasize), > + fsparam_flag_no ("rbytes", Opt_rbytes), > + fsparam_s32 ("readdir_max_bytes", Opt_readdir_max_bytes), > + fsparam_s32 ("readdir_max_entries", Opt_readdir_max_entries), > + fsparam_enum ("recover_session", Opt_recover_session), > + fsparam_flag_no ("require_active_mds", Opt_require_active_mds), > + fsparam_u32 ("rsize", Opt_rsize), > + fsparam_string ("snapdirname", Opt_snapdirname), > + fsparam_string ("source", Opt_source), > + fsparam_u32 ("wsize", Opt_wsize), > + {} > +}; > + > +static const struct fs_parameter_description ceph_mount_parameters = { > + .name = "ceph", > + .specs = ceph_mount_param_specs, > + .enums = ceph_mount_param_enums, > }; > > -static int parse_fsopt_token(char *c, void *private) > +struct ceph_parse_opts_ctx { > + struct ceph_options *copts; > + struct ceph_mount_options *opts; > +}; > + > +/* > + * Parse the source parameter. Distinguish the server list from the path. > + * Internally we do not include the leading '/' in the path. > + * > + * The source will look like: > + * <server_spec>[,<server_spec>...]:[<path>] > + * where > + * <server_spec> is <ip>[:<port>] > + * <path> is optional, but if present must begin with '/' > + */ > +static int ceph_parse_source(struct fs_parameter *param, struct fs_context *fc) > { > - struct ceph_mount_options *fsopt = private; > - substring_t argstr[MAX_OPT_ARGS]; > - int token, intval, ret; > + struct ceph_parse_opts_ctx *pctx = fc->fs_private; > + struct ceph_mount_options *fsopt = pctx->opts; > + char *dev_name = param->string, *dev_name_end; > + int ret; > > - token = match_token((char *)c, fsopt_tokens, argstr); > - if (token < 0) > - return -EINVAL; > + dout("%s '%s'\n", __func__, dev_name); > + if (!dev_name || !*dev_name) > + return invalf(fc, "ceph: Empty source"); > > - if (token < Opt_last_int) { > - ret = match_int(&argstr[0], &intval); > - if (ret < 0) { > - pr_err("bad option arg (not int) at '%s'\n", c); > - return ret; > + dev_name_end = strchr(dev_name, '/'); > + if (dev_name_end) { > + if (strlen(dev_name_end) > 1) { > + kfree(fsopt->server_path); > + fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL); > + if (!fsopt->server_path) > + return -ENOMEM; > } > - dout("got int token %d val %d\n", token, intval); > - } else if (token > Opt_last_int && token < Opt_last_string) { > - dout("got string token %d val %s\n", token, > - argstr[0].from); > } else { > - dout("got token %d\n", token); > + dev_name_end = dev_name + strlen(dev_name); > } > > + dev_name_end--; /* back up to ':' separator */ > + if (dev_name_end < dev_name || *dev_name_end != ':') > + return invalf(fc, "ceph: No path or : separator in source"); > + > + dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name); > + if (fsopt->server_path) > + dout("server path '%s'\n", fsopt->server_path); > + > + ret = ceph_parse_mon_ips(param->string, dev_name_end - dev_name, > + pctx->copts, fc); > + if (ret) > + return ret; > + > + fc->source = param->string; > + param->string = NULL; > + return 0; > +} > + > +static int ceph_parse_mount_param(struct fs_context *fc, > + struct fs_parameter *param) > +{ > + struct ceph_parse_opts_ctx *pctx = fc->fs_private; > + struct ceph_mount_options *fsopt = pctx->opts; > + struct fs_parse_result result; > + unsigned int mode; > + int token, ret; > + > + ret = ceph_parse_param(param, pctx->copts, fc); > + if (ret != -ENOPARAM) > + return ret; > + > + token = fs_parse(fc, &ceph_mount_parameters, param, &result); > + dout("%s fs_parse '%s' token %d\n", __func__, param->key, token); > + if (token < 0) > + return token; > + > switch (token) { > case Opt_snapdirname: > kfree(fsopt->snapdir_name); > - fsopt->snapdir_name = kstrndup(argstr[0].from, > - argstr[0].to-argstr[0].from, > - GFP_KERNEL); > - if (!fsopt->snapdir_name) > - return -ENOMEM; > + fsopt->snapdir_name = param->string; > + param->string = NULL; > break; > case Opt_mds_namespace: > kfree(fsopt->mds_namespace); > - fsopt->mds_namespace = kstrndup(argstr[0].from, > - argstr[0].to-argstr[0].from, > - GFP_KERNEL); > - if (!fsopt->mds_namespace) > - return -ENOMEM; > + fsopt->mds_namespace = param->string; > + param->string = NULL; > break; > case Opt_recover_session: > - if (!strncmp(argstr[0].from, "no", > - argstr[0].to - argstr[0].from)) { > + mode = result.uint_32; > + if (mode == ceph_recover_session_no) > fsopt->flags &= ~CEPH_MOUNT_OPT_CLEANRECOVER; > - } else if (!strncmp(argstr[0].from, "clean", > - argstr[0].to - argstr[0].from)) { > + else if (mode == ceph_recover_session_clean) > fsopt->flags |= CEPH_MOUNT_OPT_CLEANRECOVER; > - } else { > - return -EINVAL; > - } > - break; > - case Opt_fscache_uniq: > -#ifdef CONFIG_CEPH_FSCACHE > - kfree(fsopt->fscache_uniq); > - fsopt->fscache_uniq = kstrndup(argstr[0].from, > - argstr[0].to-argstr[0].from, > - GFP_KERNEL); > - if (!fsopt->fscache_uniq) > - return -ENOMEM; > - fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE; > + else > + BUG(); > break; > -#else > - pr_err("fscache support is disabled\n"); > - return -EINVAL; > -#endif > + case Opt_source: > + if (fc->source) > + return invalf(fc, "ceph: Multiple sources specified"); > + return ceph_parse_source(param, fc); > case Opt_wsize: > - if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_WRITE_SIZE) > - return -EINVAL; > - fsopt->wsize = ALIGN(intval, PAGE_SIZE); > + if (result.uint_32 < PAGE_SIZE || > + result.uint_32 > CEPH_MAX_WRITE_SIZE) > + goto out_of_range; > + fsopt->wsize = ALIGN(result.uint_32, PAGE_SIZE); > break; > case Opt_rsize: > - if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_READ_SIZE) > - return -EINVAL; > - fsopt->rsize = ALIGN(intval, PAGE_SIZE); > + if (result.uint_32 < PAGE_SIZE || > + result.uint_32 > CEPH_MAX_READ_SIZE) > + goto out_of_range; > + fsopt->rsize = ALIGN(result.uint_32, PAGE_SIZE); > break; > case Opt_rasize: > - if (intval < 0) > - return -EINVAL; > - fsopt->rasize = ALIGN(intval, PAGE_SIZE); > + fsopt->rasize = ALIGN(result.uint_32, PAGE_SIZE); > break; > case Opt_caps_wanted_delay_min: > - if (intval < 1) > - return -EINVAL; > - fsopt->caps_wanted_delay_min = intval; > + if (result.uint_32 < 1) > + goto out_of_range; > + fsopt->caps_wanted_delay_min = result.uint_32; > break; > case Opt_caps_wanted_delay_max: > - if (intval < 1) > - return -EINVAL; > - fsopt->caps_wanted_delay_max = intval; > + if (result.uint_32 < 1) > + goto out_of_range; > + fsopt->caps_wanted_delay_max = result.uint_32; > break; > case Opt_caps_max: > - if (intval < 0) > - return -EINVAL; > - fsopt->caps_max = intval; > + fsopt->caps_max = result.uint_32; > break; > case Opt_readdir_max_entries: > - if (intval < 1) > - return -EINVAL; > - fsopt->max_readdir = intval; > + if (result.uint_32 < 1) > + goto out_of_range; > + fsopt->max_readdir = result.uint_32; > break; > case Opt_readdir_max_bytes: > - if (intval < (int)PAGE_SIZE && intval != 0) > - return -EINVAL; > - fsopt->max_readdir_bytes = intval; > + if (result.uint_32 < PAGE_SIZE && result.uint_32 != 0) > + goto out_of_range; > + fsopt->max_readdir_bytes = result.uint_32; > break; > case Opt_congestion_kb: > - if (intval < 1024) /* at least 1M */ > - return -EINVAL; > - fsopt->congestion_kb = intval; > + if (result.uint_32 < 1024) /* at least 1M */ > + goto out_of_range; > + fsopt->congestion_kb = result.uint_32; > break; > case Opt_dirstat: > - fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT; > - break; > - case Opt_nodirstat: > - fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT; > + if (!result.negated) > + fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT; > + else > + fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT; > break; > case Opt_rbytes: > - fsopt->flags |= CEPH_MOUNT_OPT_RBYTES; > - break; > - case Opt_norbytes: > - fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES; > + if (!result.negated) > + fsopt->flags |= CEPH_MOUNT_OPT_RBYTES; > + else > + fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES; > break; > case Opt_asyncreaddir: > - fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR; > - break; > - case Opt_noasyncreaddir: > - fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR; > + if (!result.negated) > + fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR; > + else > + fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR; > break; > case Opt_dcache: > - fsopt->flags |= CEPH_MOUNT_OPT_DCACHE; > - break; > - case Opt_nodcache: > - fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE; > + if (!result.negated) > + fsopt->flags |= CEPH_MOUNT_OPT_DCACHE; > + else > + fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE; > break; > case Opt_ino32: > - fsopt->flags |= CEPH_MOUNT_OPT_INO32; > - break; > - case Opt_noino32: > - fsopt->flags &= ~CEPH_MOUNT_OPT_INO32; > + if (!result.negated) > + fsopt->flags |= CEPH_MOUNT_OPT_INO32; > + else > + fsopt->flags &= ~CEPH_MOUNT_OPT_INO32; > break; > + > case Opt_fscache: > #ifdef CONFIG_CEPH_FSCACHE > - fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE; > kfree(fsopt->fscache_uniq); > fsopt->fscache_uniq = NULL; > + if (result.negated) { > + fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE; > + } else { > + fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE; > + fsopt->fscache_uniq = param->string; > + param->string = NULL; > + } > break; > #else > - pr_err("fscache support is disabled\n"); > - return -EINVAL; > + return invalf(fc, "ceph: fscache support is disabled"); > #endif > - case Opt_nofscache: > - fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE; > - kfree(fsopt->fscache_uniq); > - fsopt->fscache_uniq = NULL; > - break; > case Opt_poolperm: > - fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM; > - break; > - case Opt_nopoolperm: > - fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM; > + if (!result.negated) > + fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM; > + else > + fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM; > break; > case Opt_require_active_mds: > - fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT; > - break; > - case Opt_norequire_active_mds: > - fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT; > + if (!result.negated) > + fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT; > + else > + fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT; > break; > case Opt_quotadf: > - fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF; > - break; > - case Opt_noquotadf: > - fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF; > + if (!result.negated) > + fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF; > + else > + fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF; > break; > case Opt_copyfrom: > - fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM; > - break; > - case Opt_nocopyfrom: > - fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM; > + if (!result.negated) > + fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM; > + else > + fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM; > break; > -#ifdef CONFIG_CEPH_FS_POSIX_ACL > case Opt_acl: > - fsopt->sb_flags |= SB_POSIXACL; > - break; > + if (!result.negated) { > +#ifdef CONFIG_CEPH_FS_POSIX_ACL > + fc->sb_flags |= SB_POSIXACL; > +#else > + return invalf(fc, "ceph: POSIX ACL support is disabled"); > #endif > - case Opt_noacl: > - fsopt->sb_flags &= ~SB_POSIXACL; > + } else { > + fc->sb_flags &= ~SB_POSIXACL; > + } > break; > default: > - BUG_ON(token); > + BUG(); > } > return 0; > + > +out_of_range: > + return invalf(fc, "ceph: %s out of range", param->key); > } > > static void destroy_mount_options(struct ceph_mount_options *args) > { > dout("destroy_mount_options %p\n", args); > + if (!args) > + return; > + > kfree(args->snapdir_name); > kfree(args->mds_namespace); > kfree(args->server_path); > @@ -459,91 +488,6 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt, > return ceph_compare_options(new_opt, fsc->client); > } > > -static int parse_mount_options(struct ceph_mount_options **pfsopt, > - struct ceph_options **popt, > - int flags, char *options, > - const char *dev_name) > -{ > - struct ceph_mount_options *fsopt; > - const char *dev_name_end; > - int err; > - > - if (!dev_name || !*dev_name) > - return -EINVAL; > - > - fsopt = kzalloc(sizeof(*fsopt), GFP_KERNEL); > - if (!fsopt) > - return -ENOMEM; > - > - dout("parse_mount_options %p, dev_name '%s'\n", fsopt, dev_name); > - > - fsopt->sb_flags = flags; > - fsopt->flags = CEPH_MOUNT_OPT_DEFAULT; > - > - fsopt->wsize = CEPH_MAX_WRITE_SIZE; > - fsopt->rsize = CEPH_MAX_READ_SIZE; > - fsopt->rasize = CEPH_RASIZE_DEFAULT; > - fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL); > - if (!fsopt->snapdir_name) { > - err = -ENOMEM; > - goto out; > - } > - > - fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT; > - fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT; > - fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT; > - fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT; > - fsopt->congestion_kb = default_congestion_kb(); > - > - /* > - * Distinguish the server list from the path in "dev_name". > - * Internally we do not include the leading '/' in the path. > - * > - * "dev_name" will look like: > - * <server_spec>[,<server_spec>...]:[<path>] > - * where > - * <server_spec> is <ip>[:<port>] > - * <path> is optional, but if present must begin with '/' > - */ > - dev_name_end = strchr(dev_name, '/'); > - if (dev_name_end) { > - if (strlen(dev_name_end) > 1) { > - fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL); > - if (!fsopt->server_path) { > - err = -ENOMEM; > - goto out; > - } > - } > - } else { > - dev_name_end = dev_name + strlen(dev_name); > - } > - err = -EINVAL; > - dev_name_end--; /* back up to ':' separator */ > - if (dev_name_end < dev_name || *dev_name_end != ':') { > - pr_err("device name is missing path (no : separator in %s)\n", > - dev_name); > - goto out; > - } > - dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name); > - if (fsopt->server_path) > - dout("server path '%s'\n", fsopt->server_path); > - > - *popt = ceph_parse_options(options, dev_name, dev_name_end, > - parse_fsopt_token, (void *)fsopt); > - if (IS_ERR(*popt)) { > - err = PTR_ERR(*popt); > - goto out; > - } > - > - /* success */ > - *pfsopt = fsopt; > - return 0; > - > -out: > - destroy_mount_options(fsopt); > - return err; > -} > - > /** > * ceph_show_options - Show mount options in /proc/mounts > * @m: seq_file to write to > @@ -587,7 +531,7 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root) > seq_puts(m, ",noquotadf"); > > #ifdef CONFIG_CEPH_FS_POSIX_ACL > - if (fsopt->sb_flags & SB_POSIXACL) > + if (root->d_sb->s_flags & SB_POSIXACL) > seq_puts(m, ",acl"); > else > seq_puts(m, ",noacl"); > @@ -860,12 +804,6 @@ static void ceph_umount_begin(struct super_block *sb) > fsc->filp_gen++; // invalidate open files > } > > -static int ceph_remount(struct super_block *sb, int *flags, char *data) > -{ > - sync_filesystem(sb); > - return 0; > -} > - > static const struct super_operations ceph_super_ops = { > .alloc_inode = ceph_alloc_inode, > .free_inode = ceph_free_inode, > @@ -874,7 +812,6 @@ static const struct super_operations ceph_super_ops = { > .evict_inode = ceph_evict_inode, > .sync_fs = ceph_sync_fs, > .put_super = ceph_put_super, > - .remount_fs = ceph_remount, > .show_options = ceph_show_options, > .statfs = ceph_statfs, > .umount_begin = ceph_umount_begin, > @@ -935,7 +872,8 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc, > /* > * mount: join the ceph cluster, and open root directory. > */ > -static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc) > +static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc, > + struct fs_context *fc) > { > int err; > unsigned long started = jiffies; /* note the start time */ > @@ -952,7 +890,7 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc) > > /* setup fscache */ > if (fsc->mount_options->flags & CEPH_MOUNT_OPT_FSCACHE) { > - err = ceph_fscache_register_fs(fsc); > + err = ceph_fscache_register_fs(fsc, fc); > if (err < 0) > goto out; > } > @@ -987,18 +925,16 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc) > return ERR_PTR(err); > } > > -static int ceph_set_super(struct super_block *s, void *data) > +static int ceph_set_super(struct super_block *s, struct fs_context *fc) > { > - struct ceph_fs_client *fsc = data; > + struct ceph_fs_client *fsc = s->s_fs_info; > int ret; > > - dout("set_super %p data %p\n", s, data); > + dout("set_super %p\n", s); > > - s->s_flags = fsc->mount_options->sb_flags; > s->s_maxbytes = MAX_LFS_FILESIZE; > > s->s_xattr = ceph_xattr_handlers; > - s->s_fs_info = fsc; > fsc->sb = s; > fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */ > > @@ -1010,24 +946,18 @@ static int ceph_set_super(struct super_block *s, void *data) > s->s_time_min = 0; > s->s_time_max = U32_MAX; > > - ret = set_anon_super(s, NULL); /* what is that second arg for? */ > + ret = set_anon_super_fc(s, fc); > if (ret != 0) > - goto fail; > - > - return ret; > - > -fail: > - s->s_fs_info = NULL; > - fsc->sb = NULL; > + fsc->sb = NULL; > return ret; > } > > /* > * share superblock if same fs AND options > */ > -static int ceph_compare_super(struct super_block *sb, void *data) > +static int ceph_compare_super(struct super_block *sb, struct fs_context *fc) > { > - struct ceph_fs_client *new = data; > + struct ceph_fs_client *new = fc->s_fs_info; > struct ceph_mount_options *fsopt = new->mount_options; > struct ceph_options *opt = new->client->options; > struct ceph_fs_client *other = ceph_sb_to_client(sb); > @@ -1043,7 +973,7 @@ static int ceph_compare_super(struct super_block *sb, void *data) > dout("fsid doesn't match\n"); > return 0; > } > - if (fsopt->sb_flags != other->mount_options->sb_flags) { > + if (fc->sb_flags != (sb->s_flags & ~SB_BORN)) { > dout("flags differ\n"); > return 0; > } > @@ -1073,46 +1003,46 @@ static int ceph_setup_bdi(struct super_block *sb, struct ceph_fs_client *fsc) > return 0; > } > > -static struct dentry *ceph_mount(struct file_system_type *fs_type, > - int flags, const char *dev_name, void *data) > +static int ceph_get_tree(struct fs_context *fc) > { > + struct ceph_parse_opts_ctx *pctx = fc->fs_private; > struct super_block *sb; > struct ceph_fs_client *fsc; > struct dentry *res; > + int (*compare_super)(struct super_block *, struct fs_context *) = > + ceph_compare_super; > int err; > - int (*compare_super)(struct super_block *, void *) = ceph_compare_super; > - struct ceph_mount_options *fsopt = NULL; > - struct ceph_options *opt = NULL; > > - dout("ceph_mount\n"); > + dout("ceph_get_tree\n"); > + > + if (!fc->source) > + return invalf(fc, "ceph: No source"); > > #ifdef CONFIG_CEPH_FS_POSIX_ACL > - flags |= SB_POSIXACL; > + fc->sb_flags |= SB_POSIXACL; > #endif > - err = parse_mount_options(&fsopt, &opt, flags, data, dev_name); > - if (err < 0) { > - res = ERR_PTR(err); > - goto out_final; > - } > > /* create client (which we may/may not use) */ > - fsc = create_fs_client(fsopt, opt); > + fsc = create_fs_client(pctx->opts, pctx->copts); > + pctx->opts = NULL; > + pctx->copts = NULL; > if (IS_ERR(fsc)) { > - res = ERR_CAST(fsc); > + err = PTR_ERR(fsc); > goto out_final; > } > > err = ceph_mdsc_init(fsc); > - if (err < 0) { > - res = ERR_PTR(err); > + if (err < 0) > goto out; > - } > > if (ceph_test_opt(fsc->client, NOSHARE)) > compare_super = NULL; > - sb = sget(fs_type, compare_super, ceph_set_super, flags, fsc); > + > + fc->s_fs_info = fsc; > + sb = sget_fc(fc, compare_super, ceph_set_super); > + fc->s_fs_info = NULL; > if (IS_ERR(sb)) { > - res = ERR_CAST(sb); > + err = PTR_ERR(sb); > goto out; > } > > @@ -1123,18 +1053,19 @@ static struct dentry *ceph_mount(struct file_system_type *fs_type, > } else { > dout("get_sb using new client %p\n", fsc); > err = ceph_setup_bdi(sb, fsc); > - if (err < 0) { > - res = ERR_PTR(err); > + if (err < 0) > goto out_splat; > - } > } > > - res = ceph_real_mount(fsc); > - if (IS_ERR(res)) > + res = ceph_real_mount(fsc, fc); > + if (IS_ERR(res)) { > + err = PTR_ERR(res); > goto out_splat; > + } > dout("root %p inode %p ino %llx.%llx\n", res, > d_inode(res), ceph_vinop(d_inode(res))); > - return res; > + fc->root = fsc->sb->s_root; > + return 0; > > out_splat: > ceph_mdsc_close_sessions(fsc->mdsc); > @@ -1144,8 +1075,79 @@ static struct dentry *ceph_mount(struct file_system_type *fs_type, > out: > destroy_fs_client(fsc); > out_final: > - dout("ceph_mount fail %ld\n", PTR_ERR(res)); > - return res; > + dout("ceph_get_tree fail %d\n", err); > + return err; > +} > + > +static void ceph_free_fc(struct fs_context *fc) > +{ > + struct ceph_parse_opts_ctx *pctx = fc->fs_private; > + > + if (pctx) { > + destroy_mount_options(pctx->opts); > + ceph_destroy_options(pctx->copts); > + kfree(pctx); > + } > +} > + > +static int ceph_reconfigure_fc(struct fs_context *fc) > +{ > + sync_filesystem(fc->root->d_sb); > + return 0; > +} > + > +static const struct fs_context_operations ceph_context_ops = { > + .free = ceph_free_fc, > + .parse_param = ceph_parse_mount_param, > + .get_tree = ceph_get_tree, > + .reconfigure = ceph_reconfigure_fc, > +}; > + > +/* > + * Set up the filesystem mount context. > + */ > +static int ceph_init_fs_context(struct fs_context *fc) > +{ > + struct ceph_parse_opts_ctx *pctx; > + struct ceph_mount_options *fsopt; > + > + pctx = kzalloc(sizeof(*pctx), GFP_KERNEL); > + if (!pctx) > + return -ENOMEM; > + > + pctx->copts = ceph_alloc_options(); > + if (!pctx->copts) > + goto nomem; > + > + pctx->opts = kzalloc(sizeof(*pctx->opts), GFP_KERNEL); > + if (!pctx->opts) > + goto nomem; > + > + fsopt = pctx->opts; > + fsopt->flags = CEPH_MOUNT_OPT_DEFAULT; > + > + fsopt->wsize = CEPH_MAX_WRITE_SIZE; > + fsopt->rsize = CEPH_MAX_READ_SIZE; > + fsopt->rasize = CEPH_RASIZE_DEFAULT; > + fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL); > + if (!fsopt->snapdir_name) > + goto nomem; > + > + fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT; > + fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT; > + fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT; > + fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT; > + fsopt->congestion_kb = default_congestion_kb(); > + > + fc->fs_private = pctx; > + fc->ops = &ceph_context_ops; > + return 0; > + > +nomem: > + destroy_mount_options(pctx->opts); > + ceph_destroy_options(pctx->copts); > + kfree(pctx); > + return -ENOMEM; > } > > static void ceph_kill_sb(struct super_block *s) > @@ -1172,7 +1174,7 @@ static void ceph_kill_sb(struct super_block *s) > static struct file_system_type ceph_fs_type = { > .owner = THIS_MODULE, > .name = "ceph", > - .mount = ceph_mount, > + .init_fs_context = ceph_init_fs_context, > .kill_sb = ceph_kill_sb, > .fs_flags = FS_RENAME_DOES_D_MOVE, > }; > diff --git a/fs/ceph/super.h b/fs/ceph/super.h > index e31c0177fcc6..f0f9cb7447ac 100644 > --- a/fs/ceph/super.h > +++ b/fs/ceph/super.h > @@ -74,7 +74,6 @@ > > struct ceph_mount_options { > int flags; > - int sb_flags; > > int wsize; /* max write size */ > int rsize; /* max read size */ > diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h > index b9dbda1c26aa..8fe9b80e80a5 100644 > --- a/include/linux/ceph/libceph.h > +++ b/include/linux/ceph/libceph.h > @@ -280,10 +280,12 @@ extern const char *ceph_msg_type_name(int type); > extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid); > extern void *ceph_kvmalloc(size_t size, gfp_t flags); > > -extern struct ceph_options *ceph_parse_options(char *options, > - const char *dev_name, const char *dev_name_end, > - int (*parse_extra_token)(char *c, void *private), > - void *private); > +struct fs_parameter; > +struct ceph_options *ceph_alloc_options(void); > +int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt, > + struct fs_context *fc); > +int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, > + struct fs_context *fc); > int ceph_print_client_options(struct seq_file *m, struct ceph_client *client, > bool show_all); > extern void ceph_destroy_options(struct ceph_options *opt); > diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c > index 2d568246803f..a9d6c97b5b0d 100644 > --- a/net/ceph/ceph_common.c > +++ b/net/ceph/ceph_common.c > @@ -11,7 +11,7 @@ > #include <linux/module.h> > #include <linux/mount.h> > #include <linux/nsproxy.h> > -#include <linux/parser.h> > +#include <linux/fs_parser.h> > #include <linux/sched.h> > #include <linux/sched/mm.h> > #include <linux/seq_file.h> > @@ -254,58 +254,77 @@ enum { > Opt_mount_timeout, > Opt_osd_idle_ttl, > Opt_osd_request_timeout, > - Opt_last_int, > /* int args above */ > Opt_fsid, > Opt_name, > Opt_secret, > Opt_key, > Opt_ip, > - Opt_last_string, > /* string args above */ > Opt_share, > - Opt_noshare, > Opt_crc, > - Opt_nocrc, > Opt_cephx_require_signatures, > - Opt_nocephx_require_signatures, > Opt_cephx_sign_messages, > - Opt_nocephx_sign_messages, > Opt_tcp_nodelay, > - Opt_notcp_nodelay, > Opt_abort_on_full, > }; > > -static match_table_t opt_tokens = { > - {Opt_osdtimeout, "osdtimeout=%d"}, > - {Opt_osdkeepalivetimeout, "osdkeepalive=%d"}, > - {Opt_mount_timeout, "mount_timeout=%d"}, > - {Opt_osd_idle_ttl, "osd_idle_ttl=%d"}, > - {Opt_osd_request_timeout, "osd_request_timeout=%d"}, > - /* int args above */ > - {Opt_fsid, "fsid=%s"}, > - {Opt_name, "name=%s"}, > - {Opt_secret, "secret=%s"}, > - {Opt_key, "key=%s"}, > - {Opt_ip, "ip=%s"}, > - /* string args above */ > - {Opt_share, "share"}, > - {Opt_noshare, "noshare"}, > - {Opt_crc, "crc"}, > - {Opt_nocrc, "nocrc"}, > - {Opt_cephx_require_signatures, "cephx_require_signatures"}, > - {Opt_nocephx_require_signatures, "nocephx_require_signatures"}, > - {Opt_cephx_sign_messages, "cephx_sign_messages"}, > - {Opt_nocephx_sign_messages, "nocephx_sign_messages"}, > - {Opt_tcp_nodelay, "tcp_nodelay"}, > - {Opt_notcp_nodelay, "notcp_nodelay"}, > - {Opt_abort_on_full, "abort_on_full"}, > - {-1, NULL} > +static const struct fs_parameter_spec ceph_param_specs[] = { > + fsparam_flag ("abort_on_full", Opt_abort_on_full), > + fsparam_flag_no ("cephx_require_signatures", Opt_cephx_require_signatures), > + fsparam_flag_no ("cephx_sign_messages", Opt_cephx_sign_messages), > + fsparam_flag_no ("crc", Opt_crc), > + fsparam_string ("fsid", Opt_fsid), > + fsparam_string ("ip", Opt_ip), > + fsparam_string ("key", Opt_key), > + fsparam_u32 ("mount_timeout", Opt_mount_timeout), > + fsparam_string ("name", Opt_name), > + fsparam_u32 ("osd_idle_ttl", Opt_osd_idle_ttl), > + fsparam_u32 ("osd_request_timeout", Opt_osd_request_timeout), > + fsparam_u32 ("osdkeepalive", Opt_osdkeepalivetimeout), > + __fsparam (fs_param_is_s32, "osdtimeout", Opt_osdtimeout, > + fs_param_deprecated), > + fsparam_string ("secret", Opt_secret), > + fsparam_flag_no ("share", Opt_share), > + fsparam_flag_no ("tcp_nodelay", Opt_tcp_nodelay), > + {} > +}; > + > +static const struct fs_parameter_description ceph_parameters = { > + .name = "libceph", > + .specs = ceph_param_specs, > }; > > +struct ceph_options *ceph_alloc_options(void) > +{ > + struct ceph_options *opt; > + > + opt = kzalloc(sizeof(*opt), GFP_KERNEL); > + if (!opt) > + return NULL; > + > + opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr), > + GFP_KERNEL); > + if (!opt->mon_addr) { > + kfree(opt); > + return NULL; > + } > + > + opt->flags = CEPH_OPT_DEFAULT; > + opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT; > + opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT; > + opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT; > + opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT; > + return opt; > +} > +EXPORT_SYMBOL(ceph_alloc_options); > + > void ceph_destroy_options(struct ceph_options *opt) > { > dout("destroy_options %p\n", opt); > + if (!opt) > + return; > + > kfree(opt->name); > if (opt->key) { > ceph_crypto_key_destroy(opt->key); > @@ -317,7 +336,9 @@ void ceph_destroy_options(struct ceph_options *opt) > EXPORT_SYMBOL(ceph_destroy_options); > > /* get secret from key store */ > -static int get_secret(struct ceph_crypto_key *dst, const char *name) { > +static int get_secret(struct ceph_crypto_key *dst, const char *name, > + struct fs_context *fc) > +{ > struct key *ukey; > int key_err; > int err = 0; > @@ -330,20 +351,20 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name) { > key_err = PTR_ERR(ukey); > switch (key_err) { > case -ENOKEY: > - pr_warn("ceph: Mount failed due to key not found: %s\n", > - name); > + errorf(fc, "libceph: Failed due to key not found: %s", > + name); > break; > case -EKEYEXPIRED: > - pr_warn("ceph: Mount failed due to expired key: %s\n", > - name); > + errorf(fc, "libceph: Failed due to expired key: %s", > + name); > break; > case -EKEYREVOKED: > - pr_warn("ceph: Mount failed due to revoked key: %s\n", > - name); > + errorf(fc, "libceph: Failed due to revoked key: %s", > + name); > break; > default: > - pr_warn("ceph: Mount failed due to unknown key error %d: %s\n", > - key_err, name); > + errorf(fc, "libceph: Failed due to key error %d: %s", > + key_err, name); > } > err = -EPERM; > goto out; > @@ -361,217 +382,157 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name) { > return err; > } > > -struct ceph_options * > -ceph_parse_options(char *options, const char *dev_name, > - const char *dev_name_end, > - int (*parse_extra_token)(char *c, void *private), > - void *private) > +int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt, > + struct fs_context *fc) > { > - struct ceph_options *opt; > - const char *c; > - int err = -ENOMEM; > - substring_t argstr[MAX_OPT_ARGS]; > - > - opt = kzalloc(sizeof(*opt), GFP_KERNEL); > - if (!opt) > - return ERR_PTR(-ENOMEM); > - opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr), > - GFP_KERNEL); > - if (!opt->mon_addr) > - goto out; > - > - dout("parse_options %p options '%s' dev_name '%s'\n", opt, options, > - dev_name); > - > - /* start with defaults */ > - opt->flags = CEPH_OPT_DEFAULT; > - opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT; > - opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT; > - opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT; > - opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT; > + int ret; > > - /* get mon ip(s) */ > /* ip1[:port1][,ip2[:port2]...] */ > - err = ceph_parse_ips(dev_name, dev_name_end, opt->mon_addr, > - CEPH_MAX_MON, &opt->num_mon); > - if (err < 0) > - goto out; > + ret = ceph_parse_ips(buf, buf + len, opt->mon_addr, CEPH_MAX_MON, > + &opt->num_mon); > + if (ret) { > + errorf(fc, "libceph: Failed to parse monitor IPs: %d", ret); > + return ret; > + } > > - /* parse mount options */ > - while ((c = strsep(&options, ",")) != NULL) { > - int token, intval; > - if (!*c) > - continue; > - err = -EINVAL; > - token = match_token((char *)c, opt_tokens, argstr); > - if (token < 0 && parse_extra_token) { > - /* extra? */ > - err = parse_extra_token((char *)c, private); > - if (err < 0) { > - pr_err("bad option at '%s'\n", c); > - goto out; > - } > - continue; > - } > - if (token < Opt_last_int) { > - err = match_int(&argstr[0], &intval); > - if (err < 0) { > - pr_err("bad option arg (not int) at '%s'\n", c); > - goto out; > - } > - dout("got int token %d val %d\n", token, intval); > - } else if (token > Opt_last_int && token < Opt_last_string) { > - dout("got string token %d val %s\n", token, > - argstr[0].from); > - } else { > - dout("got token %d\n", token); > + return 0; > +} > +EXPORT_SYMBOL(ceph_parse_mon_ips); > + > +int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, > + struct fs_context *fc) > +{ > + struct fs_parse_result result; > + int token, err; > + > + token = fs_parse(fc, &ceph_parameters, param, &result); > + dout("%s fs_parse '%s' token %d\n", __func__, param->key, token); > + if (token < 0) > + return token; > + > + switch (token) { > + case Opt_ip: > + err = ceph_parse_ips(param->string, > + param->string + param->size, > + &opt->my_addr, > + 1, NULL); > + if (err) { > + errorf(fc, "libceph: Failed to parse ip: %d", err); > + return err; > } > - switch (token) { > - case Opt_ip: > - err = ceph_parse_ips(argstr[0].from, > - argstr[0].to, > - &opt->my_addr, > - 1, NULL); > - if (err < 0) > - goto out; > - opt->flags |= CEPH_OPT_MYIP; > - break; > + opt->flags |= CEPH_OPT_MYIP; > + break; > > - case Opt_fsid: > - err = parse_fsid(argstr[0].from, &opt->fsid); > - if (err == 0) > - opt->flags |= CEPH_OPT_FSID; > - break; > - case Opt_name: > - kfree(opt->name); > - opt->name = kstrndup(argstr[0].from, > - argstr[0].to-argstr[0].from, > - GFP_KERNEL); > - if (!opt->name) { > - err = -ENOMEM; > - goto out; > - } > - break; > - case Opt_secret: > - ceph_crypto_key_destroy(opt->key); > - kfree(opt->key); > - > - opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL); > - if (!opt->key) { > - err = -ENOMEM; > - goto out; > - } > - err = ceph_crypto_key_unarmor(opt->key, argstr[0].from); > - if (err < 0) > - goto out; > - break; > - case Opt_key: > - ceph_crypto_key_destroy(opt->key); > - kfree(opt->key); > - > - opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL); > - if (!opt->key) { > - err = -ENOMEM; > - goto out; > - } > - err = get_secret(opt->key, argstr[0].from); > - if (err < 0) > - goto out; > - break; > + case Opt_fsid: > + err = parse_fsid(param->string, &opt->fsid); > + if (err) { > + errorf(fc, "libceph: Failed to parse fsid: %d", err); > + return err; > + } > + opt->flags |= CEPH_OPT_FSID; > + break; > + case Opt_name: > + kfree(opt->name); > + opt->name = param->string; > + param->string = NULL; > + break; > + case Opt_secret: > + ceph_crypto_key_destroy(opt->key); > + kfree(opt->key); > > - /* misc */ > - case Opt_osdtimeout: > - pr_warn("ignoring deprecated osdtimeout option\n"); > - break; > - case Opt_osdkeepalivetimeout: > - /* 0 isn't well defined right now, reject it */ > - if (intval < 1 || intval > INT_MAX / 1000) { > - pr_err("osdkeepalive out of range\n"); > - err = -EINVAL; > - goto out; > - } > - opt->osd_keepalive_timeout = > - msecs_to_jiffies(intval * 1000); > - break; > - case Opt_osd_idle_ttl: > - /* 0 isn't well defined right now, reject it */ > - if (intval < 1 || intval > INT_MAX / 1000) { > - pr_err("osd_idle_ttl out of range\n"); > - err = -EINVAL; > - goto out; > - } > - opt->osd_idle_ttl = msecs_to_jiffies(intval * 1000); > - break; > - case Opt_mount_timeout: > - /* 0 is "wait forever" (i.e. infinite timeout) */ > - if (intval < 0 || intval > INT_MAX / 1000) { > - pr_err("mount_timeout out of range\n"); > - err = -EINVAL; > - goto out; > - } > - opt->mount_timeout = msecs_to_jiffies(intval * 1000); > - break; > - case Opt_osd_request_timeout: > - /* 0 is "wait forever" (i.e. infinite timeout) */ > - if (intval < 0 || intval > INT_MAX / 1000) { > - pr_err("osd_request_timeout out of range\n"); > - err = -EINVAL; > - goto out; > - } > - opt->osd_request_timeout = msecs_to_jiffies(intval * 1000); > - break; > + opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL); > + if (!opt->key) > + return -ENOMEM; > + err = ceph_crypto_key_unarmor(opt->key, param->string); > + if (err) { > + errorf(fc, "libceph: Failed to parse secret: %d", err); > + return err; > + } > + break; > + case Opt_key: > + ceph_crypto_key_destroy(opt->key); > + kfree(opt->key); > > - case Opt_share: > + opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL); > + if (!opt->key) > + return -ENOMEM; > + return get_secret(opt->key, param->string, fc); > + > + case Opt_osdtimeout: > + warnf(fc, "libceph: Ignoring osdtimeout"); > + break; > + case Opt_osdkeepalivetimeout: > + /* 0 isn't well defined right now, reject it */ > + if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000) > + goto out_of_range; > + opt->osd_keepalive_timeout = > + msecs_to_jiffies(result.uint_32 * 1000); > + break; > + case Opt_osd_idle_ttl: > + /* 0 isn't well defined right now, reject it */ > + if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000) > + goto out_of_range; > + opt->osd_idle_ttl = msecs_to_jiffies(result.uint_32 * 1000); > + break; > + case Opt_mount_timeout: > + /* 0 is "wait forever" (i.e. infinite timeout) */ > + if (result.uint_32 > INT_MAX / 1000) > + goto out_of_range; > + opt->mount_timeout = msecs_to_jiffies(result.uint_32 * 1000); > + break; > + case Opt_osd_request_timeout: > + /* 0 is "wait forever" (i.e. infinite timeout) */ > + if (result.uint_32 > INT_MAX / 1000) > + goto out_of_range; > + opt->osd_request_timeout = > + msecs_to_jiffies(result.uint_32 * 1000); > + break; > + > + case Opt_share: > + if (!result.negated) > opt->flags &= ~CEPH_OPT_NOSHARE; > - break; > - case Opt_noshare: > + else > opt->flags |= CEPH_OPT_NOSHARE; > - break; > - > - case Opt_crc: > + break; > + case Opt_crc: > + if (!result.negated) > opt->flags &= ~CEPH_OPT_NOCRC; > - break; > - case Opt_nocrc: > + else > opt->flags |= CEPH_OPT_NOCRC; > - break; > - > - case Opt_cephx_require_signatures: > + break; > + case Opt_cephx_require_signatures: > + if (!result.negated) > opt->flags &= ~CEPH_OPT_NOMSGAUTH; > - break; > - case Opt_nocephx_require_signatures: > + else > opt->flags |= CEPH_OPT_NOMSGAUTH; > - break; > - case Opt_cephx_sign_messages: > + break; > + case Opt_cephx_sign_messages: > + if (!result.negated) > opt->flags &= ~CEPH_OPT_NOMSGSIGN; > - break; > - case Opt_nocephx_sign_messages: > + else > opt->flags |= CEPH_OPT_NOMSGSIGN; > - break; > - > - case Opt_tcp_nodelay: > + break; > + case Opt_tcp_nodelay: > + if (!result.negated) > opt->flags |= CEPH_OPT_TCP_NODELAY; > - break; > - case Opt_notcp_nodelay: > + else > opt->flags &= ~CEPH_OPT_TCP_NODELAY; > - break; > + break; > > - case Opt_abort_on_full: > - opt->flags |= CEPH_OPT_ABORT_ON_FULL; > - break; > + case Opt_abort_on_full: > + opt->flags |= CEPH_OPT_ABORT_ON_FULL; > + break; > > - default: > - BUG_ON(token); > - } > + default: > + BUG(); > } > > - /* success */ > - return opt; > + return 0; > > -out: > - ceph_destroy_options(opt); > - return ERR_PTR(err); > +out_of_range: > + return invalf(fc, "libceph: %s out of range", param->key); > } > -EXPORT_SYMBOL(ceph_parse_options); > +EXPORT_SYMBOL(ceph_parse_param); > > int ceph_print_client_options(struct seq_file *m, struct ceph_client *client, > bool show_all) > diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c > index e4cb3db2ee77..5b4bd8261002 100644 > --- a/net/ceph/messenger.c > +++ b/net/ceph/messenger.c > @@ -2004,10 +2004,8 @@ int ceph_parse_ips(const char *c, const char *end, > return 0; > > bad: > - pr_err("parse_ips bad ip '%.*s'\n", (int)(end - c), c); > return ret; > } > -EXPORT_SYMBOL(ceph_parse_ips); > > static int process_banner(struct ceph_connection *con) > { Nice work on all of it. LGTM. Acked-by: Jeff Layton <jlayton@xxxxxxxxxx>