1731 lines · 44.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only #include <linux/ceph/ceph_debug.h> #include <linux/backing-dev.h>#include <linux/ctype.h>#include <linux/fs.h>#include <linux/inet.h>#include <linux/in6.h>#include <linux/module.h>#include <linux/mount.h>#include <linux/fs_context.h>#include <linux/fs_parser.h>#include <linux/sched.h>#include <linux/seq_file.h>#include <linux/slab.h>#include <linux/statfs.h>#include <linux/string.h> #include "super.h"#include "mds_client.h"#include "cache.h"#include "crypto.h"#include "subvolume_metrics.h" #include <linux/ceph/ceph_features.h>#include <linux/ceph/decode.h>#include <linux/ceph/mon_client.h>#include <linux/ceph/auth.h>#include <linux/ceph/debugfs.h> #include <uapi/linux/magic.h> #define CREATE_TRACE_POINTS#include <trace/events/ceph.h> static DEFINE_SPINLOCK(ceph_fsc_lock);static LIST_HEAD(ceph_fsc_list); /* * Ceph superblock operations * * Handle the basics of mounting, unmounting. */ /* * super ops */static void ceph_put_super(struct super_block *s){	struct ceph_fs_client *fsc = ceph_sb_to_fs_client(s); 	doutc(fsc->client, "begin\n");	ceph_fscrypt_free_dummy_policy(fsc);	ceph_mdsc_close_sessions(fsc->mdsc);	doutc(fsc->client, "done\n");} static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf){	struct ceph_fs_client *fsc = ceph_inode_to_fs_client(d_inode(dentry));	struct ceph_mon_client *monc = &fsc->client->monc;	struct ceph_statfs st;	int i, err;	u64 data_pool; 	doutc(fsc->client, "begin\n");	if (fsc->mdsc->mdsmap->m_num_data_pg_pools == 1) {		data_pool = fsc->mdsc->mdsmap->m_data_pg_pools[0];	} else {		data_pool = CEPH_NOPOOL;	} 	err = ceph_monc_do_statfs(monc, data_pool, &st);	if (err < 0)		return err; 	/* fill in kstatfs */	buf->f_type = CEPH_SUPER_MAGIC;  /* ?? */ 	/*	 * Express utilization in terms of large blocks to avoid	 * overflow on 32-bit machines.	 */	buf->f_frsize = 1 << CEPH_BLOCK_SHIFT; 	/*	 * By default use root quota for stats; fallback to overall filesystem	 * usage if using 'noquotadf' mount option or if the root dir doesn't	 * have max_bytes quota set.	 */	if (ceph_test_mount_opt(fsc, NOQUOTADF) ||	    !ceph_quota_update_statfs(fsc, buf)) {		buf->f_blocks = le64_to_cpu(st.kb) >> (CEPH_BLOCK_SHIFT-10);		buf->f_bfree = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);		buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);	} 	/*	 * NOTE: for the time being, we make bsize == frsize to humor	 * not-yet-ancient versions of glibc that are broken.	 * Someday, we will probably want to report a real block	 * size...  whatever that may mean for a network file system!	 */	buf->f_bsize = buf->f_frsize; 	buf->f_files = le64_to_cpu(st.num_objects);	buf->f_ffree = -1;	buf->f_namelen = NAME_MAX; 	/* Must convert the fsid, for consistent values across arches */	buf->f_fsid.val[0] = 0;	mutex_lock(&monc->mutex);	for (i = 0 ; i < sizeof(monc->monmap->fsid) / sizeof(__le32) ; ++i)		buf->f_fsid.val[0] ^= le32_to_cpu(((__le32 *)&monc->monmap->fsid)[i]);	mutex_unlock(&monc->mutex); 	/* fold the fs_cluster_id into the upper bits */	buf->f_fsid.val[1] = monc->fs_cluster_id; 	doutc(fsc->client, "done\n");	return 0;} static int ceph_sync_fs(struct super_block *sb, int wait){	struct ceph_fs_client *fsc = ceph_sb_to_fs_client(sb);	struct ceph_client *cl = fsc->client; 	if (!wait) {		doutc(cl, "(non-blocking)\n");		ceph_flush_dirty_caps(fsc->mdsc);		ceph_flush_cap_releases(fsc->mdsc);		doutc(cl, "(non-blocking) done\n");		return 0;	} 	doutc(cl, "(blocking)\n");	ceph_osdc_sync(&fsc->client->osdc);	ceph_mdsc_sync(fsc->mdsc);	doutc(cl, "(blocking) done\n");	return 0;} /* * mount options */enum {	Opt_wsize,	Opt_rsize,	Opt_rasize,	Opt_caps_wanted_delay_min,	Opt_caps_wanted_delay_max,	Opt_caps_max,	Opt_readdir_max_entries,	Opt_readdir_max_bytes,	Opt_congestion_kb,	/* int args above */	Opt_snapdirname,	Opt_mds_namespace,	Opt_recover_session,	Opt_source,	Opt_mon_addr,	Opt_test_dummy_encryption,	/* string args above */	Opt_dirstat,	Opt_rbytes,	Opt_asyncreaddir,	Opt_dcache,	Opt_ino32,	Opt_fscache,	Opt_poolperm,	Opt_require_active_mds,	Opt_acl,	Opt_quotadf,	Opt_copyfrom,	Opt_wsync,	Opt_pagecache,	Opt_sparseread,	Opt_nearfull_sync,}; enum ceph_recover_session_mode {	ceph_recover_session_no,	ceph_recover_session_clean}; static const struct constant_table ceph_param_recover[] = {	{ "no",		ceph_recover_session_no },	{ "clean",	ceph_recover_session_clean },	{}}; static const struct fs_parameter_spec ceph_mount_parameters[] = {	fsparam_flag_no ("acl",				Opt_acl),	fsparam_flag_no ("asyncreaddir",		Opt_asyncreaddir),	fsparam_s32	("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_u32	("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_flag_no	("fsc",				Opt_fscache), // fsc|nofsc	fsparam_string	("fsc",				Opt_fscache), // fsc=...	fsparam_flag_no ("ino32",			Opt_ino32),	fsparam_string	("mds_namespace",		Opt_mds_namespace),	fsparam_string	("mon_addr",			Opt_mon_addr),	fsparam_flag_no	("nearfull_sync",		Opt_nearfull_sync),	fsparam_flag_no ("poolperm",			Opt_poolperm),	fsparam_flag_no ("quotadf",			Opt_quotadf),	fsparam_u32	("rasize",			Opt_rasize),	fsparam_flag_no ("rbytes",			Opt_rbytes),	fsparam_u32	("readdir_max_bytes",		Opt_readdir_max_bytes),	fsparam_u32	("readdir_max_entries",		Opt_readdir_max_entries),	fsparam_enum	("recover_session",		Opt_recover_session, ceph_param_recover),	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_flag	("test_dummy_encryption",	Opt_test_dummy_encryption),	fsparam_string	("test_dummy_encryption",	Opt_test_dummy_encryption),	fsparam_u32	("wsize",			Opt_wsize),	fsparam_flag_no	("wsync",			Opt_wsync),	fsparam_flag_no	("pagecache",			Opt_pagecache),	fsparam_flag_no	("sparseread",			Opt_sparseread),	{}}; struct ceph_parse_opts_ctx {	struct ceph_options		*copts;	struct ceph_mount_options	*opts;}; /* * Remove adjacent slashes and then the trailing slash, unless it is * the only remaining character. * * E.g. "//dir1////dir2///" --> "/dir1/dir2", "///" --> "/". */static void canonicalize_path(char *path){	int i, j = 0; 	for (i = 0; path[i] != '\0'; i++) {		if (path[i] != '/' || j < 1 || path[j - 1] != '/')			path[j++] = path[i];	} 	if (j > 1 && path[j - 1] == '/')		j--;	path[j] = '\0';} static int ceph_parse_old_source(const char *dev_name, const char *dev_name_end,				 struct fs_context *fc){	int r;	struct ceph_parse_opts_ctx *pctx = fc->fs_private;	struct ceph_mount_options *fsopt = pctx->opts; 	if (*dev_name_end != ':')		return invalfc(fc, "separator ':' missing in source"); 	r = ceph_parse_mon_ips(dev_name, dev_name_end - dev_name,			       pctx->copts, fc->log.log, ',');	if (r)		return r; 	fsopt->new_dev_syntax = false;	return 0;} static int ceph_parse_new_source(const char *dev_name, const char *dev_name_end,				 struct fs_context *fc){	size_t len;	struct ceph_fsid fsid;	struct ceph_parse_opts_ctx *pctx = fc->fs_private;	struct ceph_options *opts = pctx->copts;	struct ceph_mount_options *fsopt = pctx->opts;	const char *name_start = dev_name;	const char *fsid_start, *fs_name_start; 	if (*dev_name_end != '=') {		dout("separator '=' missing in source");		return -EINVAL;	} 	fsid_start = strchr(dev_name, '@');	if (!fsid_start)		return invalfc(fc, "missing cluster fsid");	len = fsid_start - name_start;	kfree(opts->name);	opts->name = kstrndup(name_start, len, GFP_KERNEL);	if (!opts->name)		return -ENOMEM;	dout("using %s entity name", opts->name); 	++fsid_start; /* start of cluster fsid */	fs_name_start = strchr(fsid_start, '.');	if (!fs_name_start)		return invalfc(fc, "missing file system name"); 	if (ceph_parse_fsid(fsid_start, &fsid))		return invalfc(fc, "Invalid FSID"); 	++fs_name_start; /* start of file system name */	len = dev_name_end - fs_name_start; 	if (!namespace_equals(fsopt, fs_name_start, len))		return invalfc(fc, "Mismatching mds_namespace");	kfree(fsopt->mds_namespace);	fsopt->mds_namespace = kstrndup(fs_name_start, len, GFP_KERNEL);	if (!fsopt->mds_namespace)		return -ENOMEM;	dout("file system (mds namespace) '%s'\n", fsopt->mds_namespace); 	fsopt->new_dev_syntax = true;	return 0;} /* * Parse the source parameter for new device format. Distinguish the device * spec from the path. Try parsing new device format and fallback to old * format if needed. * * New device syntax will looks like: *     <device_spec>=/<path> * where *     <device_spec> is name@fsid.fsname *     <path> is optional, but if present must begin with '/' * (monitor addresses are passed via mount option) * * Old device syntax is: *     <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_parse_opts_ctx *pctx = fc->fs_private;	struct ceph_mount_options *fsopt = pctx->opts;	char *dev_name = param->string, *dev_name_end;	int ret; 	dout("'%s'\n", dev_name);	if (!dev_name || !*dev_name)		return invalfc(fc, "Empty source"); 	dev_name_end = strchr(dev_name, '/');	if (dev_name_end) {		/*		 * The server_path will include the whole chars from userland		 * including the leading '/'.		 */		kfree(fsopt->server_path);		fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);		if (!fsopt->server_path)			return -ENOMEM; 		canonicalize_path(fsopt->server_path);	} else {		dev_name_end = dev_name + strlen(dev_name);	} 	dev_name_end--;		/* back up to separator */	if (dev_name_end < dev_name)		return invalfc(fc, "Path missing 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); 	dout("trying new device syntax");	ret = ceph_parse_new_source(dev_name, dev_name_end, fc);	if (ret) {		if (ret != -EINVAL)			return ret;		dout("trying old device syntax");		ret = ceph_parse_old_source(dev_name, dev_name_end, fc);		if (ret)			return ret;	} 	fc->source = param->string;	param->string = NULL;	return 0;} static int ceph_parse_mon_addr(struct fs_parameter *param,			       struct fs_context *fc){	struct ceph_parse_opts_ctx *pctx = fc->fs_private;	struct ceph_mount_options *fsopt = pctx->opts; 	kfree(fsopt->mon_addr);	fsopt->mon_addr = param->string;	param->string = NULL; 	return ceph_parse_mon_ips(fsopt->mon_addr, strlen(fsopt->mon_addr),				  pctx->copts, fc->log.log, '/');} 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->log.log);	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:		if (strlen(param->string) > NAME_MAX)			return invalfc(fc, "snapdirname too long");		kfree(fsopt->snapdir_name);		fsopt->snapdir_name = param->string;		param->string = NULL;		break;	case Opt_mds_namespace:		if (!namespace_equals(fsopt, param->string, strlen(param->string)))			return invalfc(fc, "Mismatching mds_namespace");		kfree(fsopt->mds_namespace);		fsopt->mds_namespace = param->string;		param->string = NULL;		break;	case Opt_recover_session:		mode = result.uint_32;		if (mode == ceph_recover_session_no)			fsopt->flags &= ~CEPH_MOUNT_OPT_CLEANRECOVER;		else if (mode == ceph_recover_session_clean)			fsopt->flags |= CEPH_MOUNT_OPT_CLEANRECOVER;		else			BUG();		break;	case Opt_source:		if (fc->source)			return invalfc(fc, "Multiple sources specified");		return ceph_parse_source(param, fc);	case Opt_mon_addr:		return ceph_parse_mon_addr(param, fc);	case Opt_wsize:		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 (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:		fsopt->rasize = ALIGN(result.uint_32, PAGE_SIZE);		break;	case Opt_caps_wanted_delay_min:		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 (result.uint_32 < 1)			goto out_of_range;		fsopt->caps_wanted_delay_max = result.uint_32;		break;	case Opt_caps_max:		if (result.int_32 < 0)			goto out_of_range;		fsopt->caps_max = result.int_32;		break;	case Opt_readdir_max_entries:		if (result.uint_32 < 1)			goto out_of_range;		fsopt->max_readdir = result.uint_32;		break;	case Opt_readdir_max_bytes:		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 (result.uint_32 < 1024) /* at least 1M */			goto out_of_range;		fsopt->congestion_kb = result.uint_32;		break;	case Opt_dirstat:		if (!result.negated)

Lines 1–500 of 1731.