1102 lines · 28.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * stack_user.c * * Code which interfaces ocfs2 with fs/dlm and a userspace stack. * * Copyright (C) 2007 Oracle.  All rights reserved. */ #include <linux/module.h>#include <linux/fs.h>#include <linux/filelock.h>#include <linux/miscdevice.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/reboot.h>#include <linux/sched.h>#include <linux/uaccess.h> #include "stackglue.h" #include <linux/dlm_plock.h> /* * The control protocol starts with a handshake.  Until the handshake * is complete, the control device will fail all write(2)s. * * The handshake is simple.  First, the client reads until EOF.  Each line * of output is a supported protocol tag.  All protocol tags are a single * character followed by a two hex digit version number.  Currently the * only things supported is T01, for "Text-base version 0x01".  Next, the * client writes the version they would like to use, including the newline. * Thus, the protocol tag is 'T01\n'.  If the version tag written is * unknown, -EINVAL is returned.  Once the negotiation is complete, the * client can start sending messages. * * The T01 protocol has three messages.  First is the "SETN" message. * It has the following syntax: * *  SETN<space><8-char-hex-nodenum><newline> * * This is 14 characters. * * The "SETN" message must be the first message following the protocol. * It tells ocfs2_control the local node number. * * Next comes the "SETV" message.  It has the following syntax: * *  SETV<space><2-char-hex-major><space><2-char-hex-minor><newline> * * This is 11 characters. * * The "SETV" message sets the filesystem locking protocol version as * negotiated by the client.  The client negotiates based on the maximum * version advertised in /sys/fs/ocfs2/max_locking_protocol.  The major * number from the "SETV" message must match * ocfs2_user_plugin.sp_max_proto.pv_major, and the minor number * must be less than or equal to ...sp_max_version.pv_minor. * * Once this information has been set, mounts will be allowed.  From this * point on, the "DOWN" message can be sent for node down notification. * It has the following syntax: * *  DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline> * * eg: * *  DOWN 632A924FDD844190BDA93C0DF6B94899 00000001\n * * This is 47 characters. */ /* * Whether or not the client has done the handshake. * For now, we have just one protocol version. */#define OCFS2_CONTROL_PROTO			"T01\n"#define OCFS2_CONTROL_PROTO_LEN			4 /* Handshake states */#define OCFS2_CONTROL_HANDSHAKE_INVALID		(0)#define OCFS2_CONTROL_HANDSHAKE_READ		(1)#define OCFS2_CONTROL_HANDSHAKE_PROTOCOL	(2)#define OCFS2_CONTROL_HANDSHAKE_VALID		(3) /* Messages */#define OCFS2_CONTROL_MESSAGE_OP_LEN		4#define OCFS2_CONTROL_MESSAGE_SETNODE_OP	"SETN"#define OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN	14#define OCFS2_CONTROL_MESSAGE_SETVERSION_OP	"SETV"#define OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN	11#define OCFS2_CONTROL_MESSAGE_DOWN_OP		"DOWN"#define OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN	47#define OCFS2_TEXT_UUID_LEN			32#define OCFS2_CONTROL_MESSAGE_VERNUM_LEN	2#define OCFS2_CONTROL_MESSAGE_NODENUM_LEN	8#define VERSION_LOCK				"version_lock" enum ocfs2_connection_type {	WITH_CONTROLD,	NO_CONTROLD}; /* * ocfs2_live_connection is refcounted because the filesystem and * miscdevice sides can detach in different order.  Let's just be safe. */struct ocfs2_live_connection {	struct list_head		oc_list;	struct ocfs2_cluster_connection	*oc_conn;	enum ocfs2_connection_type	oc_type;	atomic_t                        oc_this_node;	int                             oc_our_slot;	struct dlm_lksb                 oc_version_lksb;	char                            oc_lvb[DLM_LVB_LEN];	struct completion               oc_sync_wait;	wait_queue_head_t		oc_wait;}; struct ocfs2_control_private {	struct list_head op_list;	int op_state;	int op_this_node;	struct ocfs2_protocol_version op_proto;}; /* SETN<space><8-char-hex-nodenum><newline> */struct ocfs2_control_message_setn {	char	tag[OCFS2_CONTROL_MESSAGE_OP_LEN];	char	space;	char	nodestr[OCFS2_CONTROL_MESSAGE_NODENUM_LEN];	char	newline;}; /* SETV<space><2-char-hex-major><space><2-char-hex-minor><newline> */struct ocfs2_control_message_setv {	char	tag[OCFS2_CONTROL_MESSAGE_OP_LEN];	char	space1;	char	major[OCFS2_CONTROL_MESSAGE_VERNUM_LEN];	char	space2;	char	minor[OCFS2_CONTROL_MESSAGE_VERNUM_LEN];	char	newline;}; /* DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline> */struct ocfs2_control_message_down {	char	tag[OCFS2_CONTROL_MESSAGE_OP_LEN];	char	space1;	char	uuid[OCFS2_TEXT_UUID_LEN];	char	space2;	char	nodestr[OCFS2_CONTROL_MESSAGE_NODENUM_LEN];	char	newline;}; union ocfs2_control_message {	char					tag[OCFS2_CONTROL_MESSAGE_OP_LEN];	struct ocfs2_control_message_setn	u_setn;	struct ocfs2_control_message_setv	u_setv;	struct ocfs2_control_message_down	u_down;}; static struct ocfs2_stack_plugin ocfs2_user_plugin; static atomic_t ocfs2_control_opened;static int ocfs2_control_this_node = -1;static struct ocfs2_protocol_version running_proto; static LIST_HEAD(ocfs2_live_connection_list);static LIST_HEAD(ocfs2_control_private_list);static DEFINE_MUTEX(ocfs2_control_lock); static inline void ocfs2_control_set_handshake_state(struct file *file,						     int state){	struct ocfs2_control_private *p = file->private_data;	p->op_state = state;} static inline int ocfs2_control_get_handshake_state(struct file *file){	struct ocfs2_control_private *p = file->private_data;	return p->op_state;} static struct ocfs2_live_connection *ocfs2_connection_find(const char *name){	size_t len = strlen(name);	struct ocfs2_live_connection *c; 	BUG_ON(!mutex_is_locked(&ocfs2_control_lock)); 	list_for_each_entry(c, &ocfs2_live_connection_list, oc_list) {		if ((c->oc_conn->cc_namelen == len) &&		    !strncmp(c->oc_conn->cc_name, name, len))			return c;	} 	return NULL;} /* * ocfs2_live_connection structures are created underneath the ocfs2 * mount path.  Since the VFS prevents multiple calls to * fill_super(), we can't get dupes here. */static int ocfs2_live_connection_attach(struct ocfs2_cluster_connection *conn,				     struct ocfs2_live_connection *c){	int rc = 0; 	mutex_lock(&ocfs2_control_lock);	c->oc_conn = conn; 	if ((c->oc_type == NO_CONTROLD) || atomic_read(&ocfs2_control_opened))		list_add(&c->oc_list, &ocfs2_live_connection_list);	else {		printk(KERN_ERR		       "ocfs2: Userspace control daemon is not present\n");		rc = -ESRCH;	} 	mutex_unlock(&ocfs2_control_lock);	return rc;} /* * This function disconnects the cluster connection from ocfs2_control. * Afterwards, userspace can't affect the cluster connection. */static void ocfs2_live_connection_drop(struct ocfs2_live_connection *c){	mutex_lock(&ocfs2_control_lock);	list_del_init(&c->oc_list);	c->oc_conn = NULL;	mutex_unlock(&ocfs2_control_lock); 	kfree(c);} static int ocfs2_control_cfu(void *target, size_t target_len,			     const char __user *buf, size_t count){	/* The T01 expects write(2) calls to have exactly one command */	if ((count != target_len) ||	    (count > sizeof(union ocfs2_control_message)))		return -EINVAL; 	if (copy_from_user(target, buf, target_len))		return -EFAULT; 	return 0;} static ssize_t ocfs2_control_validate_protocol(struct file *file,					       const char __user *buf,					       size_t count){	ssize_t ret;	char kbuf[OCFS2_CONTROL_PROTO_LEN]; 	ret = ocfs2_control_cfu(kbuf, OCFS2_CONTROL_PROTO_LEN,				buf, count);	if (ret)		return ret; 	if (strncmp(kbuf, OCFS2_CONTROL_PROTO, OCFS2_CONTROL_PROTO_LEN))		return -EINVAL; 	ocfs2_control_set_handshake_state(file,					  OCFS2_CONTROL_HANDSHAKE_PROTOCOL); 	return count;} static void ocfs2_control_send_down(const char *uuid,				    int nodenum){	struct ocfs2_live_connection *c; 	mutex_lock(&ocfs2_control_lock); 	c = ocfs2_connection_find(uuid);	if (c) {		BUG_ON(c->oc_conn == NULL);		c->oc_conn->cc_recovery_handler(nodenum,						c->oc_conn->cc_recovery_data);	} 	mutex_unlock(&ocfs2_control_lock);} /* * Called whenever configuration elements are sent to /dev/ocfs2_control. * If all configuration elements are present, try to set the global * values.  If there is a problem, return an error.  Skip any missing * elements, and only bump ocfs2_control_opened when we have all elements * and are successful. */static int ocfs2_control_install_private(struct file *file){	int rc = 0;	int set_p = 1;	struct ocfs2_control_private *p = file->private_data; 	BUG_ON(p->op_state != OCFS2_CONTROL_HANDSHAKE_PROTOCOL); 	mutex_lock(&ocfs2_control_lock); 	if (p->op_this_node < 0) {		set_p = 0;	} else if ((ocfs2_control_this_node >= 0) &&		   (ocfs2_control_this_node != p->op_this_node)) {		rc = -EINVAL;		goto out_unlock;	} 	if (!p->op_proto.pv_major) {		set_p = 0;	} else if (!list_empty(&ocfs2_live_connection_list) &&		   ((running_proto.pv_major != p->op_proto.pv_major) ||		    (running_proto.pv_minor != p->op_proto.pv_minor))) {		rc = -EINVAL;		goto out_unlock;	} 	if (set_p) {		ocfs2_control_this_node = p->op_this_node;		running_proto.pv_major = p->op_proto.pv_major;		running_proto.pv_minor = p->op_proto.pv_minor;		atomic_inc(&ocfs2_control_opened);		ocfs2_control_set_handshake_state(file,					OCFS2_CONTROL_HANDSHAKE_VALID);	} out_unlock:	mutex_unlock(&ocfs2_control_lock); 	return rc;} static int ocfs2_control_get_this_node(void){	int rc; 	mutex_lock(&ocfs2_control_lock);	if (ocfs2_control_this_node < 0)		rc = -EINVAL;	else		rc = ocfs2_control_this_node;	mutex_unlock(&ocfs2_control_lock); 	return rc;} static int ocfs2_control_do_setnode_msg(struct file *file,					struct ocfs2_control_message_setn *msg){	long nodenum;	struct ocfs2_control_private *p = file->private_data; 	if (ocfs2_control_get_handshake_state(file) !=	    OCFS2_CONTROL_HANDSHAKE_PROTOCOL)		return -EINVAL; 	if (strncmp(msg->tag, OCFS2_CONTROL_MESSAGE_SETNODE_OP,		    OCFS2_CONTROL_MESSAGE_OP_LEN))		return -EINVAL; 	if ((msg->space != ' ') || (msg->newline != '\n'))		return -EINVAL;	msg->space = msg->newline = '\0'; 	if (kstrtol(msg->nodestr, 16, &nodenum))		return -EINVAL; 	if ((nodenum == LONG_MIN) || (nodenum == LONG_MAX) ||	    (nodenum > INT_MAX) || (nodenum < 0))		return -ERANGE;	p->op_this_node = nodenum; 	return ocfs2_control_install_private(file);} static int ocfs2_control_do_setversion_msg(struct file *file,					   struct ocfs2_control_message_setv *msg){	long major, minor;	struct ocfs2_control_private *p = file->private_data;	struct ocfs2_protocol_version *max =		&ocfs2_user_plugin.sp_max_proto; 	if (ocfs2_control_get_handshake_state(file) !=	    OCFS2_CONTROL_HANDSHAKE_PROTOCOL)		return -EINVAL; 	if (strncmp(msg->tag, OCFS2_CONTROL_MESSAGE_SETVERSION_OP,		    OCFS2_CONTROL_MESSAGE_OP_LEN))		return -EINVAL; 	if ((msg->space1 != ' ') || (msg->space2 != ' ') ||	    (msg->newline != '\n'))		return -EINVAL;	msg->space1 = msg->space2 = msg->newline = '\0'; 	if (kstrtol(msg->major, 16, &major))		return -EINVAL;	if (kstrtol(msg->minor, 16, &minor))		return -EINVAL; 	/*	 * The major must be between 1 and 255, inclusive.  The minor	 * must be between 0 and 255, inclusive.  The version passed in	 * must be within the maximum version supported by the filesystem.	 */	if ((major == LONG_MIN) || (major == LONG_MAX) ||	    (major > (u8)-1) || (major < 1))		return -ERANGE;	if ((minor == LONG_MIN) || (minor == LONG_MAX) ||	    (minor > (u8)-1) || (minor < 0))		return -ERANGE;	if ((major != max->pv_major) ||	    (minor > max->pv_minor))		return -EINVAL; 	p->op_proto.pv_major = major;	p->op_proto.pv_minor = minor; 	return ocfs2_control_install_private(file);} static int ocfs2_control_do_down_msg(struct file *file,				     struct ocfs2_control_message_down *msg){	long nodenum; 	if (ocfs2_control_get_handshake_state(file) !=	    OCFS2_CONTROL_HANDSHAKE_VALID)		return -EINVAL; 	if (strncmp(msg->tag, OCFS2_CONTROL_MESSAGE_DOWN_OP,		    OCFS2_CONTROL_MESSAGE_OP_LEN))		return -EINVAL; 	if ((msg->space1 != ' ') || (msg->space2 != ' ') ||	    (msg->newline != '\n'))		return -EINVAL;	msg->space1 = msg->space2 = msg->newline = '\0'; 	if (kstrtol(msg->nodestr, 16, &nodenum))		return -EINVAL; 	if ((nodenum == LONG_MIN) || (nodenum == LONG_MAX) ||	    (nodenum > INT_MAX) || (nodenum < 0))		return -ERANGE; 	ocfs2_control_send_down(msg->uuid, nodenum); 	return 0;} static ssize_t ocfs2_control_message(struct file *file,				     const char __user *buf,				     size_t count){	ssize_t ret;	union ocfs2_control_message msg; 	/* Try to catch padding issues */	WARN_ON(offsetof(struct ocfs2_control_message_down, uuid) !=		(sizeof(msg.u_down.tag) + sizeof(msg.u_down.space1))); 	memset(&msg, 0, sizeof(union ocfs2_control_message));	ret = ocfs2_control_cfu(&msg, count, buf, count);	if (ret)		goto out; 	if ((count == OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN) &&	    !strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_SETNODE_OP,		     OCFS2_CONTROL_MESSAGE_OP_LEN))		ret = ocfs2_control_do_setnode_msg(file, &msg.u_setn);	else if ((count == OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN) &&		 !strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_SETVERSION_OP,			  OCFS2_CONTROL_MESSAGE_OP_LEN))		ret = ocfs2_control_do_setversion_msg(file, &msg.u_setv);	else if ((count == OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN) &&		 !strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_DOWN_OP,			  OCFS2_CONTROL_MESSAGE_OP_LEN))		ret = ocfs2_control_do_down_msg(file, &msg.u_down);	else		ret = -EINVAL; out:	return ret ? ret : count;} static ssize_t ocfs2_control_write(struct file *file,				   const char __user *buf,				   size_t count,				   loff_t *ppos){

Lines 1–500 of 1102.