diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ocfs2/stack_user.c | 94 |
1 files changed, 89 insertions, 5 deletions
diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index ff8d30757924..a5e58e2b9d59 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -35,9 +35,21 @@ * 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. If the version tag - * written is unknown, -EINVAL is returned. Once the negotiation is - * complete, the client can start sending messages. + * 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 only has one message, "DOWN". 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. */ /* @@ -49,6 +61,11 @@ #define OCFS2_CONTROL_HANDSHAKE_INVALID (0) #define OCFS2_CONTROL_HANDSHAKE_READ (1) #define OCFS2_CONTROL_HANDSHAKE_VALID (2) +#define OCFS2_CONTROL_MESSAGE_DOWN "DOWN" +#define OCFS2_CONTROL_MESSAGE_DOWN_LEN 4 +#define OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN 47 +#define OCFS2_TEXT_UUID_LEN 32 +#define OCFS2_CONTROL_MESSAGE_NODENUM_LEN 8 /* * ocfs2_live_connection is refcounted because the filesystem and @@ -149,7 +166,7 @@ static void ocfs2_live_connection_drop(struct ocfs2_live_connection *c) kfree(c); } -static ssize_t ocfs2_control_cfu(char *target, size_t target_len, +static ssize_t 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 */ @@ -185,6 +202,73 @@ static ssize_t ocfs2_control_validate_handshake(struct file *file, 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); +} + +/* DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline> */ +struct ocfs2_control_message_down { + char tag[OCFS2_CONTROL_MESSAGE_DOWN_LEN]; + char space1; + char uuid[OCFS2_TEXT_UUID_LEN]; + char space2; + char nodestr[OCFS2_CONTROL_MESSAGE_NODENUM_LEN]; + char newline; +}; + +static ssize_t ocfs2_control_message(struct file *file, + const char __user *buf, + size_t count) +{ + ssize_t ret; + char *p = NULL; + long nodenum; + struct ocfs2_control_message_down msg; + + /* Try to catch padding issues */ + WARN_ON(offsetof(struct ocfs2_control_message_down, uuid) != + (sizeof(msg.tag) + sizeof(msg.space1))); + + memset(&msg, 0, sizeof(struct ocfs2_control_message_down)); + ret = ocfs2_control_cfu(&msg, OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN, + buf, count); + if (ret != count) + return ret; + + if (strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_DOWN, + strlen(OCFS2_CONTROL_MESSAGE_DOWN))) + return -EINVAL; + + if ((msg.space1 != ' ') || (msg.space2 != ' ') || + (msg.newline != '\n')) + return -EINVAL; + msg.space1 = msg.space2 = msg.newline = '\0'; + + nodenum = simple_strtol(msg.nodestr, &p, 16); + if (!p || *p) + return -EINVAL; + + if ((nodenum == LONG_MIN) || (nodenum == LONG_MAX) || + (nodenum > INT_MAX) || (nodenum < 0)) + return -ERANGE; + + ocfs2_control_send_down(msg.uuid, nodenum); + + return count; +} static ssize_t ocfs2_control_write(struct file *file, const char __user *buf, @@ -204,7 +288,7 @@ static ssize_t ocfs2_control_write(struct file *file, break; case OCFS2_CONTROL_HANDSHAKE_VALID: - ret = count; /* XXX */ + ret = ocfs2_control_message(file, buf, count); break; default: |