925 lines · 22.9 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/key.h>#include <keys/ceph-type.h>#include <linux/module.h>#include <linux/mount.h>#include <linux/nsproxy.h>#include <linux/fs_parser.h>#include <linux/sched.h>#include <linux/sched/mm.h>#include <linux/seq_file.h>#include <linux/slab.h>#include <linux/statfs.h>#include <linux/string.h>#include <linux/vmalloc.h>  #include <linux/ceph/ceph_features.h>#include <linux/ceph/libceph.h>#include <linux/ceph/debugfs.h>#include <linux/ceph/decode.h>#include <linux/ceph/mon_client.h>#include <linux/ceph/auth.h>#include "crypto.h"  /* * Module compatibility interface.  For now it doesn't do anything, * but its existence signals a certain level of functionality. * * The data buffer is used to pass information both to and from * libceph.  The return value indicates whether libceph determines * it is compatible with the caller (from another kernel module), * given the provided data. * * The data pointer can be null. */bool libceph_compatible(void *data){	return true;}EXPORT_SYMBOL(libceph_compatible); static int param_get_supported_features(char *buffer,					const struct kernel_param *kp){	return sprintf(buffer, "0x%llx", CEPH_FEATURES_SUPPORTED_DEFAULT);}static const struct kernel_param_ops param_ops_supported_features = {	.get = param_get_supported_features,};module_param_cb(supported_features, &param_ops_supported_features, NULL,		0444); const char *ceph_msg_type_name(int type){	switch (type) {	case CEPH_MSG_SHUTDOWN: return "shutdown";	case CEPH_MSG_PING: return "ping";	case CEPH_MSG_AUTH: return "auth";	case CEPH_MSG_AUTH_REPLY: return "auth_reply";	case CEPH_MSG_MON_MAP: return "mon_map";	case CEPH_MSG_MON_GET_MAP: return "mon_get_map";	case CEPH_MSG_MON_SUBSCRIBE: return "mon_subscribe";	case CEPH_MSG_MON_SUBSCRIBE_ACK: return "mon_subscribe_ack";	case CEPH_MSG_STATFS: return "statfs";	case CEPH_MSG_STATFS_REPLY: return "statfs_reply";	case CEPH_MSG_MON_GET_VERSION: return "mon_get_version";	case CEPH_MSG_MON_GET_VERSION_REPLY: return "mon_get_version_reply";	case CEPH_MSG_MDS_MAP: return "mds_map";	case CEPH_MSG_FS_MAP_USER: return "fs_map_user";	case CEPH_MSG_CLIENT_SESSION: return "client_session";	case CEPH_MSG_CLIENT_RECONNECT: return "client_reconnect";	case CEPH_MSG_CLIENT_REQUEST: return "client_request";	case CEPH_MSG_CLIENT_REQUEST_FORWARD: return "client_request_forward";	case CEPH_MSG_CLIENT_REPLY: return "client_reply";	case CEPH_MSG_CLIENT_CAPS: return "client_caps";	case CEPH_MSG_CLIENT_CAPRELEASE: return "client_cap_release";	case CEPH_MSG_CLIENT_QUOTA: return "client_quota";	case CEPH_MSG_CLIENT_SNAP: return "client_snap";	case CEPH_MSG_CLIENT_LEASE: return "client_lease";	case CEPH_MSG_POOLOP_REPLY: return "poolop_reply";	case CEPH_MSG_POOLOP: return "poolop";	case CEPH_MSG_MON_COMMAND: return "mon_command";	case CEPH_MSG_MON_COMMAND_ACK: return "mon_command_ack";	case CEPH_MSG_OSD_MAP: return "osd_map";	case CEPH_MSG_OSD_OP: return "osd_op";	case CEPH_MSG_OSD_OPREPLY: return "osd_opreply";	case CEPH_MSG_WATCH_NOTIFY: return "watch_notify";	case CEPH_MSG_OSD_BACKOFF: return "osd_backoff";	default: return "unknown";	}}EXPORT_SYMBOL(ceph_msg_type_name); /* * Initially learn our fsid, or verify an fsid matches. */int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid){	if (client->have_fsid) {		if (ceph_fsid_compare(&client->fsid, fsid)) {			pr_err("bad fsid, had %pU got %pU",			       &client->fsid, fsid);			return -1;		}	} else {		memcpy(&client->fsid, fsid, sizeof(*fsid));	}	return 0;}EXPORT_SYMBOL(ceph_check_fsid); static int strcmp_null(const char *s1, const char *s2){	if (!s1 && !s2)		return 0;	if (s1 && !s2)		return -1;	if (!s1 && s2)		return 1;	return strcmp(s1, s2);} int ceph_compare_options(struct ceph_options *new_opt,			 struct ceph_client *client){	struct ceph_options *opt1 = new_opt;	struct ceph_options *opt2 = client->options;	int ofs = offsetof(struct ceph_options, mon_addr);	int i;	int ret; 	/*	 * Don't bother comparing options if network namespaces don't	 * match.	 */	if (!net_eq(current->nsproxy->net_ns, read_pnet(&client->msgr.net)))		return -1; 	ret = memcmp(opt1, opt2, ofs);	if (ret)		return ret; 	ret = strcmp_null(opt1->name, opt2->name);	if (ret)		return ret; 	if (opt1->key && !opt2->key)		return -1;	if (!opt1->key && opt2->key)		return 1;	if (opt1->key && opt2->key) {		if (opt1->key->type != opt2->key->type)			return -1;		if (opt1->key->created.tv_sec != opt2->key->created.tv_sec)			return -1;		if (opt1->key->created.tv_nsec != opt2->key->created.tv_nsec)			return -1;		if (opt1->key->len != opt2->key->len)			return -1;		if (opt1->key->key && !opt2->key->key)			return -1;		if (!opt1->key->key && opt2->key->key)			return 1;		if (opt1->key->key && opt2->key->key) {			ret = memcmp(opt1->key->key, opt2->key->key, opt1->key->len);			if (ret)				return ret;		}	} 	ret = ceph_compare_crush_locs(&opt1->crush_locs, &opt2->crush_locs);	if (ret)		return ret; 	/* any matching mon ip implies a match */	for (i = 0; i < opt1->num_mon; i++) {		if (ceph_monmap_contains(client->monc.monmap,				 &opt1->mon_addr[i]))			return 0;	}	return -1;}EXPORT_SYMBOL(ceph_compare_options); int ceph_parse_fsid(const char *str, struct ceph_fsid *fsid){	int i = 0;	char tmp[3];	int err = -EINVAL;	int d; 	dout("%s '%s'\n", __func__, str);	tmp[2] = 0;	while (*str && i < 16) {		if (ispunct(*str)) {			str++;			continue;		}		if (!isxdigit(str[0]) || !isxdigit(str[1]))			break;		tmp[0] = str[0];		tmp[1] = str[1];		if (sscanf(tmp, "%x", &d) < 1)			break;		fsid->fsid[i] = d & 0xff;		i++;		str += 2;	} 	if (i == 16)		err = 0;	dout("%s ret %d got fsid %pU\n", __func__, err, fsid);	return err;}EXPORT_SYMBOL(ceph_parse_fsid); /* * ceph options */enum {	Opt_osdkeepalivetimeout,	Opt_mount_timeout,	Opt_osd_idle_ttl,	Opt_osd_request_timeout,	/* int args above */	Opt_fsid,	Opt_name,	Opt_secret,	Opt_key,	Opt_ip,	Opt_crush_location,	Opt_read_from_replica,	Opt_ms_mode,	/* string args above */	Opt_share,	Opt_crc,	Opt_cephx_require_signatures,	Opt_cephx_sign_messages,	Opt_tcp_nodelay,	Opt_abort_on_full,	Opt_rxbounce,}; enum {	Opt_read_from_replica_no,	Opt_read_from_replica_balance,	Opt_read_from_replica_localize,}; static const struct constant_table ceph_param_read_from_replica[] = {	{"no",		Opt_read_from_replica_no},	{"balance",	Opt_read_from_replica_balance},	{"localize",	Opt_read_from_replica_localize},	{}}; enum ceph_ms_mode {	Opt_ms_mode_legacy,	Opt_ms_mode_crc,	Opt_ms_mode_secure,	Opt_ms_mode_prefer_crc,	Opt_ms_mode_prefer_secure}; static const struct constant_table ceph_param_ms_mode[] = {	{"legacy",		Opt_ms_mode_legacy},	{"crc",			Opt_ms_mode_crc},	{"secure",		Opt_ms_mode_secure},	{"prefer-crc",		Opt_ms_mode_prefer_crc},	{"prefer-secure",	Opt_ms_mode_prefer_secure},	{}}; static const struct fs_parameter_spec ceph_parameters[] = {	fsparam_flag	("abort_on_full",		Opt_abort_on_full),	__fsparam	(NULL, "cephx_require_signatures", Opt_cephx_require_signatures,			 fs_param_neg_with_no|fs_param_deprecated, NULL),	fsparam_flag_no ("cephx_sign_messages",		Opt_cephx_sign_messages),	fsparam_flag_no ("crc",				Opt_crc),	fsparam_string	("crush_location",		Opt_crush_location),	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_enum	("read_from_replica",		Opt_read_from_replica,			 ceph_param_read_from_replica),	fsparam_flag	("rxbounce",			Opt_rxbounce),	fsparam_enum	("ms_mode",			Opt_ms_mode,			 ceph_param_ms_mode),	fsparam_string	("secret",			Opt_secret),	fsparam_flag_no ("share",			Opt_share),	fsparam_flag_no ("tcp_nodelay",			Opt_tcp_nodelay),	{}}; struct ceph_options *ceph_alloc_options(void){	struct ceph_options *opt; 	opt = kzalloc_obj(*opt);	if (!opt)		return NULL; 	opt->crush_locs = RB_ROOT;	opt->mon_addr = kzalloc_objs(*opt->mon_addr, CEPH_MAX_MON);	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;	opt->read_from_replica = CEPH_READ_FROM_REPLICA_DEFAULT;	opt->con_modes[0] = CEPH_CON_MODE_UNKNOWN;	opt->con_modes[1] = CEPH_CON_MODE_UNKNOWN;	return opt;}EXPORT_SYMBOL(ceph_alloc_options); void ceph_destroy_options(struct ceph_options *opt){	dout("destroy_options %p\n", opt);	if (!opt)		return; 	ceph_clear_crush_locs(&opt->crush_locs);	kfree(opt->name);	if (opt->key) {		ceph_crypto_key_destroy(opt->key);		kfree(opt->key);	}	kfree(opt->mon_addr);	kfree(opt);}EXPORT_SYMBOL(ceph_destroy_options); /* get secret from key store */static int get_secret(struct ceph_crypto_key *dst, const char *name,		      struct p_log *log){	struct key *ukey;	int key_err;	int err = 0;	struct ceph_crypto_key *ckey; 	ukey = request_key(&key_type_ceph, name, NULL);	if (IS_ERR(ukey)) {		/* request_key errors don't map nicely to mount(2)		   errors; don't even try, but still printk */		key_err = PTR_ERR(ukey);		switch (key_err) {		case -ENOKEY:			error_plog(log, "Failed due to key not found: %s",			       name);			break;		case -EKEYEXPIRED:			error_plog(log, "Failed due to expired key: %s",			       name);			break;		case -EKEYREVOKED:			error_plog(log, "Failed due to revoked key: %s",			       name);			break;		default:			error_plog(log, "Failed due to key error %d: %s",			       key_err, name);		}		err = -EPERM;		goto out;	} 	ckey = ukey->payload.data[0];	err = ceph_crypto_key_clone(dst, ckey);	if (err)		goto out_key;	/* pass through, err is 0 */ out_key:	key_put(ukey);out:	return err;} int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt,		       struct fc_log *l, char delim){	struct p_log log = {.prefix = "libceph", .log = l};	int ret; 	/* ip1[:port1][<delim>ip2[:port2]...] */	ret = ceph_parse_ips(buf, buf + len, opt->mon_addr, CEPH_MAX_MON,			     &opt->num_mon, delim);	if (ret) {		error_plog(&log, "Failed to parse monitor IPs: %d", ret);		return ret;	} 	return 0;}EXPORT_SYMBOL(ceph_parse_mon_ips); int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt,		     struct fc_log *l){	struct fs_parse_result result;	int token, err;	struct p_log log = {.prefix = "libceph", .log = l}; 	token = __fs_parse(&log, 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) {			error_plog(&log, "Failed to parse ip: %d", err);			return err;		}		opt->flags |= CEPH_OPT_MYIP;		break; 	case Opt_fsid:		err = ceph_parse_fsid(param->string, &opt->fsid);		if (err) {			error_plog(&log, "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); 		opt->key = kzalloc_obj(*opt->key);		if (!opt->key)			return -ENOMEM;		err = ceph_crypto_key_unarmor(opt->key, param->string);		if (err) {			error_plog(&log, "Failed to parse secret: %d", err);			return err;		}		break;	case Opt_key:		ceph_crypto_key_destroy(opt->key);		kfree(opt->key); 		opt->key = kzalloc_obj(*opt->key);		if (!opt->key)			return -ENOMEM;		return get_secret(opt->key, param->string, &log);	case Opt_crush_location:		ceph_clear_crush_locs(&opt->crush_locs);		err = ceph_parse_crush_location(param->string,						&opt->crush_locs);		if (err) {			error_plog(&log, "Failed to parse CRUSH location: %d",				   err);			return err;		}		break;	case Opt_read_from_replica:		switch (result.uint_32) {		case Opt_read_from_replica_no:			opt->read_from_replica = 0;			break;		case Opt_read_from_replica_balance:			opt->read_from_replica = CEPH_OSD_FLAG_BALANCE_READS;			break;		case Opt_read_from_replica_localize:			opt->read_from_replica = CEPH_OSD_FLAG_LOCALIZE_READS;			break;		default:			BUG();		}		break;	case Opt_ms_mode:

Lines 1–500 of 925.