diff options
Diffstat (limited to 'fs/ocfs2')
-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 @@ | |||
35 | * of output is a supported protocol tag. All protocol tags are a single | 35 | * of output is a supported protocol tag. All protocol tags are a single |
36 | * character followed by a two hex digit version number. Currently the | 36 | * character followed by a two hex digit version number. Currently the |
37 | * only things supported is T01, for "Text-base version 0x01". Next, the | 37 | * only things supported is T01, for "Text-base version 0x01". Next, the |
38 | * client writes the version they would like to use. If the version tag | 38 | * client writes the version they would like to use, including the newline. |
39 | * written is unknown, -EINVAL is returned. Once the negotiation is | 39 | * Thus, the protocol tag is 'T01\n'. If the version tag written is |
40 | * complete, the client can start sending messages. | 40 | * unknown, -EINVAL is returned. Once the negotiation is complete, the |
41 | * client can start sending messages. | ||
42 | * | ||
43 | * The T01 protocol only has one message, "DOWN". It has the following | ||
44 | * syntax: | ||
45 | * | ||
46 | * DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline> | ||
47 | * | ||
48 | * eg: | ||
49 | * | ||
50 | * DOWN 632A924FDD844190BDA93C0DF6B94899 00000001\n | ||
51 | * | ||
52 | * This is 47 characters. | ||
41 | */ | 53 | */ |
42 | 54 | ||
43 | /* | 55 | /* |
@@ -49,6 +61,11 @@ | |||
49 | #define OCFS2_CONTROL_HANDSHAKE_INVALID (0) | 61 | #define OCFS2_CONTROL_HANDSHAKE_INVALID (0) |
50 | #define OCFS2_CONTROL_HANDSHAKE_READ (1) | 62 | #define OCFS2_CONTROL_HANDSHAKE_READ (1) |
51 | #define OCFS2_CONTROL_HANDSHAKE_VALID (2) | 63 | #define OCFS2_CONTROL_HANDSHAKE_VALID (2) |
64 | #define OCFS2_CONTROL_MESSAGE_DOWN "DOWN" | ||
65 | #define OCFS2_CONTROL_MESSAGE_DOWN_LEN 4 | ||
66 | #define OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN 47 | ||
67 | #define OCFS2_TEXT_UUID_LEN 32 | ||
68 | #define OCFS2_CONTROL_MESSAGE_NODENUM_LEN 8 | ||
52 | 69 | ||
53 | /* | 70 | /* |
54 | * ocfs2_live_connection is refcounted because the filesystem and | 71 | * ocfs2_live_connection is refcounted because the filesystem and |
@@ -149,7 +166,7 @@ static void ocfs2_live_connection_drop(struct ocfs2_live_connection *c) | |||
149 | kfree(c); | 166 | kfree(c); |
150 | } | 167 | } |
151 | 168 | ||
152 | static ssize_t ocfs2_control_cfu(char *target, size_t target_len, | 169 | static ssize_t ocfs2_control_cfu(void *target, size_t target_len, |
153 | const char __user *buf, size_t count) | 170 | const char __user *buf, size_t count) |
154 | { | 171 | { |
155 | /* The T01 expects write(2) calls to have exactly one command */ | 172 | /* 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, | |||
185 | return count; | 202 | return count; |
186 | } | 203 | } |
187 | 204 | ||
205 | static void ocfs2_control_send_down(const char *uuid, | ||
206 | int nodenum) | ||
207 | { | ||
208 | struct ocfs2_live_connection *c; | ||
209 | |||
210 | mutex_lock(&ocfs2_control_lock); | ||
211 | |||
212 | c = ocfs2_connection_find(uuid); | ||
213 | if (c) { | ||
214 | BUG_ON(c->oc_conn == NULL); | ||
215 | c->oc_conn->cc_recovery_handler(nodenum, | ||
216 | c->oc_conn->cc_recovery_data); | ||
217 | } | ||
218 | |||
219 | mutex_unlock(&ocfs2_control_lock); | ||
220 | } | ||
221 | |||
222 | /* DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline> */ | ||
223 | struct ocfs2_control_message_down { | ||
224 | char tag[OCFS2_CONTROL_MESSAGE_DOWN_LEN]; | ||
225 | char space1; | ||
226 | char uuid[OCFS2_TEXT_UUID_LEN]; | ||
227 | char space2; | ||
228 | char nodestr[OCFS2_CONTROL_MESSAGE_NODENUM_LEN]; | ||
229 | char newline; | ||
230 | }; | ||
231 | |||
232 | static ssize_t ocfs2_control_message(struct file *file, | ||
233 | const char __user *buf, | ||
234 | size_t count) | ||
235 | { | ||
236 | ssize_t ret; | ||
237 | char *p = NULL; | ||
238 | long nodenum; | ||
239 | struct ocfs2_control_message_down msg; | ||
240 | |||
241 | /* Try to catch padding issues */ | ||
242 | WARN_ON(offsetof(struct ocfs2_control_message_down, uuid) != | ||
243 | (sizeof(msg.tag) + sizeof(msg.space1))); | ||
244 | |||
245 | memset(&msg, 0, sizeof(struct ocfs2_control_message_down)); | ||
246 | ret = ocfs2_control_cfu(&msg, OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN, | ||
247 | buf, count); | ||
248 | if (ret != count) | ||
249 | return ret; | ||
250 | |||
251 | if (strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_DOWN, | ||
252 | strlen(OCFS2_CONTROL_MESSAGE_DOWN))) | ||
253 | return -EINVAL; | ||
254 | |||
255 | if ((msg.space1 != ' ') || (msg.space2 != ' ') || | ||
256 | (msg.newline != '\n')) | ||
257 | return -EINVAL; | ||
258 | msg.space1 = msg.space2 = msg.newline = '\0'; | ||
259 | |||
260 | nodenum = simple_strtol(msg.nodestr, &p, 16); | ||
261 | if (!p || *p) | ||
262 | return -EINVAL; | ||
263 | |||
264 | if ((nodenum == LONG_MIN) || (nodenum == LONG_MAX) || | ||
265 | (nodenum > INT_MAX) || (nodenum < 0)) | ||
266 | return -ERANGE; | ||
267 | |||
268 | ocfs2_control_send_down(msg.uuid, nodenum); | ||
269 | |||
270 | return count; | ||
271 | } | ||
188 | 272 | ||
189 | static ssize_t ocfs2_control_write(struct file *file, | 273 | static ssize_t ocfs2_control_write(struct file *file, |
190 | const char __user *buf, | 274 | const char __user *buf, |
@@ -204,7 +288,7 @@ static ssize_t ocfs2_control_write(struct file *file, | |||
204 | break; | 288 | break; |
205 | 289 | ||
206 | case OCFS2_CONTROL_HANDSHAKE_VALID: | 290 | case OCFS2_CONTROL_HANDSHAKE_VALID: |
207 | ret = count; /* XXX */ | 291 | ret = ocfs2_control_message(file, buf, count); |
208 | break; | 292 | break; |
209 | 293 | ||
210 | default: | 294 | default: |