Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #include <errno.h>
32 : : #include <stdio.h>
33 : : #include <fcntl.h>
34 : : #include <unistd.h>
35 : : #include <stdlib.h>
36 : : #include <sys/mman.h>
37 : : #include <sys/socket.h>
38 : : #include <sys/un.h>
39 : : #include <sys/types.h>
40 : : #include <sys/stat.h>
41 : : #include <netdb.h>
42 : : #include <arpa/inet.h>
43 : : #include <netinet/tcp.h>
44 : : #include <netinet/in.h>
45 : : #include "tapdisk.h"
46 : : #include "tapdisk-server.h"
47 : : #include "tapdisk-driver.h"
48 : : #include "tapdisk-interface.h"
49 : : #include "tapdisk-utils.h"
50 : : #include "tapdisk-fdreceiver.h"
51 : : #include "timeout-math.h"
52 : : #include "tapdisk-nbdserver.h"
53 : : #include "tapdisk-protocol-new.h"
54 : : #include "util.h"
55 : :
56 : : #ifdef HAVE_CONFIG_H
57 : : #include "config.h"
58 : : #endif
59 : :
60 : : #define INFO(_f, _a...) tlog_syslog(TLOG_INFO, "nbd: " _f, ##_a)
61 : : #define ERROR(_f, _a...) tlog_syslog(TLOG_WARN, "nbd: " _f, ##_a)
62 : :
63 : : #define N_PASSED_FDS 10
64 : : #define TAPDISK_NBDCLIENT_MAX_PATH_LEN 256
65 : :
66 : : #define MAX_NBD_REQS TAPDISK_DATA_REQUESTS
67 : : #define NBD_TIMEOUT 30
68 : : #define RECV_BUFFER_SIZE 256
69 : :
70 : : /*
71 : : * We'll only ever have one nbdclient fd receiver per tapdisk process, so let's
72 : : * just store it here globally. We'll also keep track of the passed fds here
73 : : * too.
74 : : */
75 : :
76 : : struct td_fdreceiver *fdreceiver = NULL;
77 : :
78 : : struct tdnbd_passed_fd {
79 : : char id[40];
80 : : int fd;
81 : : } passed_fds[N_PASSED_FDS];
82 : :
83 : : struct nbd_queued_io {
84 : : char *buffer;
85 : : int len;
86 : : int so_far;
87 : : };
88 : :
89 : : struct td_nbd_request {
90 : : td_request_t treq;
91 : : struct nbd_request nreq;
92 : : int timeout_event;
93 : : int fake;
94 : : struct nbd_queued_io header;
95 : : struct nbd_queued_io body; /* in or out, depending on whether
96 : : type is read or write. */
97 : : struct list_head queue;
98 : : };
99 : :
100 : : struct tdnbd_data
101 : : {
102 : : int writer_event_id;
103 : : struct list_head sent_reqs;
104 : : struct list_head pending_reqs;
105 : : struct list_head free_reqs;
106 : : struct td_nbd_request requests[MAX_NBD_REQS];
107 : : int nr_free_count;
108 : :
109 : : int reader_event_id;
110 : : struct nbd_reply current_reply;
111 : : struct nbd_queued_io cur_reply_qio;
112 : : struct td_nbd_request *curr_reply_req;
113 : :
114 : : int socket;
115 : : /*
116 : : * TODO tapdisk can talk to an Internet socket or a UNIX domain socket.
117 : : * Try to group struct members accordingly e.g. in a union.
118 : : */
119 : : struct sockaddr_in *remote;
120 : : struct sockaddr_un remote_un;
121 : : char *peer_ip;
122 : : int port;
123 : : char *name;
124 : :
125 : : int flags;
126 : : int closed;
127 : : };
128 : :
129 : : int global_id = 0;
130 : :
131 : : static void disable_write_queue(struct tdnbd_data *prv);
132 : :
133 : :
134 : : /* -- fdreceiver bits and pieces -- */
135 : :
136 : : static void
137 : 0 : tdnbd_stash_passed_fd(int fd, char *msg, void *data)
138 : : {
139 : 0 : int free_index = -1;
140 : : int i;
141 [ # # ]: 0 : for (i = 0; i < N_PASSED_FDS; i++)
142 : : /* Check for unused slot before attempting to compare
143 : : * names so that we never try to compare against the name
144 : : * of an unused slot */
145 [ # # ][ # # ]: 0 : if (passed_fds[i].fd == -1 || strncmp(msg, passed_fds[i].id,
146 : : sizeof(passed_fds[i].id) - 1) == 0) {
147 : : free_index = i;
148 : : break;
149 : : }
150 : :
151 [ # # ]: 0 : if (free_index == -1) {
152 : 0 : ERROR("Error - more than %d fds passed! cannot stash another",
153 : : N_PASSED_FDS);
154 : 0 : close(fd);
155 : 0 : return;
156 : : }
157 : :
158 : : /* There exists a possibility that the FD we are replacing is still
159 : : * open. Unconditionally close it here to avoid leaking FDs. Do not
160 : : * care about errors from close(). */
161 [ # # ]: 0 : if (passed_fds[free_index].fd > -1)
162 : 0 : close(passed_fds[free_index].fd);
163 : :
164 : 0 : passed_fds[free_index].fd = fd;
165 : 0 : safe_strncpy(passed_fds[free_index].id, msg,
166 : : sizeof(passed_fds[free_index].id));
167 : : }
168 : :
169 : : static int
170 : 0 : tdnbd_retrieve_passed_fd(const char *name)
171 : : {
172 : : int fd, i;
173 : :
174 [ # # ]: 0 : for (i = 0; i < N_PASSED_FDS; i++) {
175 [ # # ]: 0 : if (strncmp(name, passed_fds[i].id,
176 : : sizeof(passed_fds[i].id) - 1) == 0) {
177 : 0 : fd = passed_fds[i].fd;
178 : 0 : passed_fds[i].fd = -1;
179 : 0 : return fd;
180 : : }
181 : : }
182 : :
183 : 0 : ERROR("Couldn't find the fd named: %s", name);
184 : :
185 : 0 : return -1;
186 : : }
187 : :
188 : : void
189 : 0 : tdnbd_fdreceiver_start()
190 : : {
191 : : char fdreceiver_path[TAPDISK_NBDCLIENT_MAX_PATH_LEN];
192 : : int i;
193 : :
194 : : /* initialise the passed fds list */
195 [ # # ]: 0 : for (i = 0; i < N_PASSED_FDS; i++)
196 : 0 : passed_fds[i].fd = -1;
197 : :
198 : 0 : snprintf(fdreceiver_path, TAPDISK_NBDCLIENT_MAX_PATH_LEN,
199 : : "%s%d", TAPDISK_NBDCLIENT_LISTEN_SOCK_PATH, getpid());
200 : :
201 : 0 : fdreceiver = td_fdreceiver_start(fdreceiver_path,
202 : : tdnbd_stash_passed_fd, NULL);
203 : :
204 : 0 : }
205 : :
206 : : void
207 : 0 : tdnbd_fdreceiver_stop()
208 : : {
209 [ # # ]: 0 : if (fdreceiver)
210 : 0 : td_fdreceiver_stop(fdreceiver);
211 : 0 : }
212 : :
213 : : static void
214 : 0 : __cancel_req(int i, struct td_nbd_request *pos, int e)
215 : : {
216 : : char handle[9];
217 : 0 : memcpy(handle, pos->nreq.handle, 8);
218 : 0 : handle[8] = 0;
219 : 0 : INFO("Entry %d: handle='%s' type=%d, len=%d: %s",
220 : : i, handle, ntohl(pos->nreq.type), ntohl(pos->nreq.len), strerror(e));
221 : :
222 [ # # ]: 0 : if (pos->timeout_event >= 0) {
223 : 0 : tapdisk_server_unregister_event(pos->timeout_event);
224 : 0 : pos->timeout_event = -1;
225 : : }
226 : :
227 : 0 : td_complete_request(pos->treq, e);
228 : 0 : }
229 : :
230 : : static void
231 : 0 : tdnbd_disable(struct tdnbd_data *prv, int e)
232 : : {
233 : : struct td_nbd_request *pos, *q;
234 : 0 : int i = 0;
235 : :
236 : 0 : INFO("NBD client full-disable");
237 : :
238 : 0 : tapdisk_server_unregister_event(prv->writer_event_id);
239 : 0 : tapdisk_server_unregister_event(prv->reader_event_id);
240 : :
241 : 0 : INFO("NBD client cancelling sent reqs");
242 [ # # ]: 0 : list_for_each_entry_safe(pos, q, &prv->sent_reqs, queue)
243 : 0 : __cancel_req(i++, pos, e);
244 : :
245 : 0 : INFO("NBD client cancelling pending reqs");
246 [ # # ]: 0 : list_for_each_entry_safe(pos, q, &prv->pending_reqs, queue)
247 : 0 : __cancel_req(i++, pos, e);
248 : :
249 : 0 : INFO("Setting closed");
250 : 0 : prv->closed = 3;
251 : 0 : }
252 : :
253 : : /* NBD writer queue */
254 : :
255 : : /* Return code: how much is left to write, or a negative error code */
256 : : static int
257 : 0 : tdnbd_write_some(int fd, struct nbd_queued_io *data)
258 : : {
259 : 0 : int left = data->len - data->so_far;
260 : : int rc;
261 : : char *code;
262 : :
263 [ # # ]: 0 : while (left > 0) {
264 : 0 : rc = send(fd, data->buffer + data->so_far, left, 0);
265 : :
266 [ # # ]: 0 : if (rc == -1) {
267 [ # # ]: 0 : if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
268 : : return left;
269 : :
270 : 0 : code = strerror(errno);
271 [ # # ]: 0 : ERROR("Bad return code %d from send (%s)", rc,
272 : : (code == 0 ? "unknown" : code));
273 : 0 : return rc;
274 : : }
275 : :
276 [ # # ]: 0 : if (rc == 0) {
277 : 0 : ERROR("Server shutdown prematurely in write_some");
278 : 0 : return -1;
279 : : }
280 : :
281 : 0 : left -= rc;
282 : 0 : data->so_far += rc;
283 : : }
284 : :
285 : : return left;
286 : : }
287 : :
288 : : static int
289 : 0 : tdnbd_read_some(int fd, struct nbd_queued_io *data)
290 : : {
291 : 0 : int left = data->len - data->so_far;
292 : : int rc;
293 : : char *code;
294 : :
295 [ # # ]: 0 : while (left > 0) {
296 : 0 : rc = recv(fd, data->buffer + data->so_far, left, 0);
297 : :
298 [ # # ]: 0 : if (rc == -1) {
299 : :
300 [ # # ]: 0 : if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
301 : : return left;
302 : :
303 : 0 : code = strerror(errno);
304 [ # # ]: 0 : ERROR("Bad return code %d from send (%s)", rc,
305 : : (code == 0 ? "unknown" : code));
306 : 0 : return rc;
307 : : }
308 : :
309 [ # # ]: 0 : if (rc == 0) {
310 : 0 : ERROR("Server shutdown prematurely in read_some");
311 : 0 : return -1;
312 : : }
313 : :
314 : 0 : data->so_far += rc;
315 : 0 : left -= rc;
316 : : }
317 : :
318 : : return left;
319 : : }
320 : :
321 : : static void
322 : 0 : tdnbd_writer_cb(event_id_t eb, char mode, void *data)
323 : : {
324 : : struct td_nbd_request *pos, *q;
325 : 0 : struct tdnbd_data *prv = data;
326 : :
327 [ # # ]: 0 : list_for_each_entry_safe(pos, q, &prv->pending_reqs, queue) {
328 [ # # ]: 0 : if (tdnbd_write_some(prv->socket, &pos->header) > 0)
329 : : return;
330 : :
331 [ # # ]: 0 : if (ntohl(pos->nreq.type) == TAPDISK_NBD_CMD_WRITE) {
332 [ # # ]: 0 : if (tdnbd_write_some(prv->socket, &pos->body) > 0)
333 : : return;
334 : : }
335 : :
336 [ # # ]: 0 : if (ntohl(pos->nreq.type) == TAPDISK_NBD_CMD_DISC) {
337 : 0 : INFO("sent close request");
338 : : /*
339 : : * We don't expect a response from a DISC, so move the
340 : : * request back onto the free list
341 : : */
342 : 0 : list_move(&pos->queue, &prv->free_reqs);
343 : 0 : prv->nr_free_count++;
344 : 0 : prv->closed = 2;
345 : : } else {
346 : 0 : list_move(&pos->queue, &prv->sent_reqs);
347 : : }
348 : : }
349 : :
350 : : /* If we're here, we've written everything */
351 : :
352 : 0 : disable_write_queue(prv);
353 : :
354 [ # # ]: 0 : if (prv->closed == 2)
355 : 0 : tdnbd_disable(prv, EIO);
356 : :
357 : : return;
358 : : }
359 : :
360 : : static int
361 : 0 : enable_write_queue(struct tdnbd_data *prv)
362 : : {
363 [ # # ]: 0 : if (prv->writer_event_id >= 0)
364 : : return 0;
365 : :
366 : 0 : prv->writer_event_id =
367 : 0 : tapdisk_server_register_event(SCHEDULER_POLL_WRITE_FD,
368 : : prv->socket,
369 : 0 : TV_ZERO,
370 : : tdnbd_writer_cb,
371 : : prv);
372 : :
373 : 0 : return prv->writer_event_id;
374 : : }
375 : :
376 : : static void
377 : 0 : disable_write_queue(struct tdnbd_data *prv)
378 : : {
379 [ # # ]: 0 : if (prv->writer_event_id < 0)
380 : 0 : return;
381 : :
382 : 0 : tapdisk_server_unregister_event(prv->writer_event_id);
383 : :
384 : 0 : prv->writer_event_id = -1;
385 : : }
386 : :
387 : : static int
388 : 0 : tdnbd_queue_request(struct tdnbd_data *prv, int type, uint64_t offset,
389 : : char *buffer, uint32_t length, td_request_t treq, int fake)
390 : : {
391 [ # # ]: 0 : if (prv->nr_free_count == 0)
392 : : return -EBUSY;
393 : :
394 [ # # ]: 0 : if (prv->closed == 3) {
395 : 0 : td_complete_request(treq, -ETIMEDOUT);
396 : 0 : return -ETIMEDOUT;
397 : : }
398 : :
399 : 0 : struct td_nbd_request *req = list_entry(prv->free_reqs.next,
400 : : struct td_nbd_request, queue);
401 : :
402 : : /* fill in the request */
403 : :
404 : 0 : req->treq = treq;
405 : 0 : int id = global_id++;
406 : 0 : snprintf(req->nreq.handle, 8, "td%05x", id % 0xffff);
407 : :
408 : : /* Don't time the NBD requests out */
409 : 0 : req->timeout_event = -1;
410 : :
411 : 0 : req->nreq.magic = htonl(NBD_REQUEST_MAGIC);
412 : 0 : req->nreq.type = htonl(type);
413 : 0 : req->nreq.from = htonll(offset);
414 : 0 : req->nreq.len = htonl(length);
415 : 0 : req->header.buffer = (char *)&req->nreq;
416 : 0 : req->header.len = sizeof(req->nreq);
417 : 0 : req->header.so_far = 0;
418 : 0 : req->body.buffer = buffer;
419 : 0 : req->body.len = length;
420 : 0 : req->body.so_far = 0;
421 : 0 : req->fake = fake;
422 : :
423 : 0 : list_move_tail(&req->queue, &prv->pending_reqs);
424 : 0 : prv->nr_free_count--;
425 : :
426 [ # # ]: 0 : if (prv->writer_event_id < 0)
427 : 0 : enable_write_queue(prv);
428 : :
429 : : return 0;
430 : : }
431 : :
432 : : /* NBD Reader callback */
433 : :
434 : : static void
435 : 0 : tdnbd_reader_cb(event_id_t eb, char mode, void *data)
436 : : {
437 : : char handle[9];
438 : 0 : int do_disable = 0;
439 : :
440 : : /* Check to see if we're in the middle of reading a response already */
441 : 0 : struct tdnbd_data *prv = data;
442 : 0 : int rc = tdnbd_read_some(prv->socket, &prv->cur_reply_qio);
443 : :
444 [ # # ]: 0 : if (rc < 0) {
445 : 0 : ERROR("Error reading reply header: %d", rc);
446 : 0 : tdnbd_disable(prv, EIO);
447 : 0 : return;
448 : : }
449 : :
450 [ # # ]: 0 : if (rc > 0)
451 : : return; /* need more data */
452 : :
453 : : /* Got a header. */
454 [ # # ]: 0 : if (prv->current_reply.error != 0) {
455 : 0 : ERROR("Error in reply: %d", prv->current_reply.error);
456 : 0 : tdnbd_disable(prv, EIO);
457 : : return;
458 : : }
459 : :
460 : : /* Have we found the request yet? */
461 [ # # ]: 0 : if (prv->curr_reply_req == NULL) {
462 : : struct td_nbd_request *pos, *q;
463 [ # # ]: 0 : list_for_each_entry_safe(pos, q, &prv->sent_reqs, queue) {
464 [ # # ]: 0 : if (memcmp(pos->nreq.handle, prv->current_reply.handle,
465 : : 8) == 0) {
466 : 0 : prv->curr_reply_req = pos;
467 : 0 : break;
468 : : }
469 : : }
470 : :
471 [ # # ]: 0 : if (prv->curr_reply_req == NULL) {
472 : 0 : memcpy(handle, prv->current_reply.handle, 8);
473 : 0 : handle[8] = 0;
474 : :
475 : 0 : ERROR("Couldn't find request corresponding to reply "
476 : : "(reply handle='%s')", handle);
477 : 0 : tdnbd_disable(prv, EIO);
478 : : return;
479 : : }
480 : : }
481 : :
482 [ # # # ]: 0 : switch(ntohl(prv->curr_reply_req->nreq.type)) {
483 : : case TAPDISK_NBD_CMD_READ:
484 : 0 : rc = tdnbd_read_some(prv->socket,
485 : : &prv->curr_reply_req->body);
486 : :
487 [ # # ]: 0 : if (rc < 0) {
488 : 0 : ERROR("Error reading body of request: %d", rc);
489 : 0 : tdnbd_disable(prv, EIO);
490 : : return;
491 : : }
492 : :
493 [ # # ]: 0 : if (rc > 0)
494 : : return; /* need more data */
495 : :
496 : 0 : td_complete_request(prv->curr_reply_req->treq, 0);
497 : :
498 : : break;
499 : : case TAPDISK_NBD_CMD_WRITE:
500 : 0 : td_complete_request(prv->curr_reply_req->treq, 0);
501 : :
502 : : break;
503 : : default:
504 : 0 : ERROR("Unhandled request response: %d",
505 : : ntohl(prv->curr_reply_req->nreq.type));
506 : : do_disable = 1;
507 : : return;
508 : : }
509 : :
510 : : /* remove the state */
511 : 0 : list_move(&prv->curr_reply_req->queue, &prv->free_reqs);
512 : 0 : prv->nr_free_count++;
513 : :
514 : 0 : prv->cur_reply_qio.so_far = 0;
515 [ # # ]: 0 : if (prv->curr_reply_req->timeout_event >= 0) {
516 : 0 : tapdisk_server_unregister_event(
517 : : prv->curr_reply_req->timeout_event);
518 : : }
519 : :
520 : 0 : prv->curr_reply_req = NULL;
521 : :
522 : : /*
523 : : * NB: do this here otherwise we cancel the request that has just been
524 : : * moved
525 : : */
526 : : if (do_disable)
527 : : tdnbd_disable(prv, EIO);
528 : : }
529 : :
530 : : /* Wait a certain maximum amount of time for a socket to be readable and then
531 : : * recv() some bytes from it if it is. Returns -ETIMEDOUT if the select times out,
532 : : * otherwise -errno from whatever action failed.
533 : : *
534 : : * Otherwise, returns number of bytes read from the recv() (which could be 0)
535 : : */
536 : : static int
537 : 0 : tdnbd_wait_recv(int fd, void *buffer, size_t len, int flags)
538 : : {
539 : : struct timeval select_tv;
540 : : fd_set socks;
541 : : int rc;
542 : :
543 : 0 : FD_ZERO(&socks);
544 [ # # ][ # # ]: 0 : FD_SET(fd, &socks);
545 : 0 : select_tv.tv_sec = 10;
546 : 0 : select_tv.tv_usec = 0;
547 [ # # ][ # # ]: 0 : rc = TEMP_FAILURE_RETRY(select(fd + 1, &socks, NULL, NULL, &select_tv));
548 [ # # ]: 0 : if (rc < 0) return -errno;
549 [ # # ]: 0 : if (rc == 0) return -ETIMEDOUT;
550 : :
551 [ # # ][ # # ]: 0 : rc = TEMP_FAILURE_RETRY(recv(fd, buffer, len, flags));
552 [ # # ]: 0 : if (rc < 0) return -errno;
553 : : return rc;
554 : : }
555 : :
556 : : int
557 : 0 : negotiate_client_newstyle_options(int sock, td_driver_t *driver)
558 : : {
559 : : int rc;
560 : : struct nbd_new_option new_option;
561 : 0 : char exportname[] = NBD_FIXED_SINGLE_EXPORT;
562 : 0 : new_option.version = htobe64(NBD_OPT_MAGIC);
563 : 0 : new_option.optlen = htobe32(sizeof(exportname));
564 : 0 : new_option.option = htobe32( NBD_OPT_EXPORT_NAME);
565 : :
566 : : struct nbd_export_name_option_reply handshake_finish;
567 : : static const size_t NO_ZERO_HANDSHAKE_FINISH_SIZE = 10;
568 : :
569 : : /* Send EXPORTNAME_NAME option request */
570 : 0 : rc = send_fully_or_fail(sock, &new_option, sizeof(new_option));
571 [ # # ]: 0 : if (rc < 0)
572 : : {
573 : 0 : ERROR("Failed to send options to sock");
574 : : goto errout;
575 : : }
576 : : /* Send exportname name */
577 : 0 : rc = send_fully_or_fail(sock, exportname, sizeof(exportname));
578 [ # # ]: 0 : if (rc < 0)
579 : : {
580 : 0 : ERROR("Failed to send export name to sock");
581 : : goto errout;
582 : : }
583 : :
584 : : /* Collect the results in the handshake finished */
585 : 0 : rc = tdnbd_wait_recv(sock, &handshake_finish, NO_ZERO_HANDSHAKE_FINISH_SIZE, 0);
586 [ # # ]: 0 : if (rc < 0)
587 : : {
588 : 0 : ERROR("Failed to read handshake from sock: %s", strerror(-rc));
589 : : goto errout;
590 : : }
591 : :
592 : 0 : driver->info.size = be64toh(handshake_finish.exportsize) >> SECTOR_SHIFT;
593 : 0 : driver->info.sector_size = DEFAULT_SECTOR_SIZE;
594 : 0 : driver->info.info = 0;
595 : :
596 : 0 : rc = fcntl(sock, F_SETFL, O_NONBLOCK);
597 [ # # ]: 0 : if (rc != 0) {
598 : 0 : ERROR("Could not set O_NONBLOCK flag");
599 : : goto errout;
600 : : }
601 : :
602 : 0 : INFO("Successfully connected to New-style NBD server");
603 : :
604 : 0 : return 0;
605 : :
606 : : errout:
607 : 0 : close(sock);
608 : : return -1;
609 : : }
610 : :
611 : :
612 : : static int
613 : 0 : tdnbd_nbd_negotiate_old(struct tdnbd_data *prv, td_driver_t *driver)
614 : : {
615 : : int rc;
616 : : char buffer[RECV_BUFFER_SIZE];
617 : : uint64_t size;
618 : : uint32_t flags;
619 : 0 : int padbytes = 124;
620 : 0 : int sock = prv->socket;
621 : :
622 : : /*
623 : : * NBD OLD-style negotiation protocol:
624 : : *
625 : : * Server sends 'NBDMAGIC'
626 : : * then it sends 0x00420281861253L
627 : : * then it sends a 64 bit bigendian size <-- YOU ARE HERE
628 : : * then it sends a 32 bit bigendian flags
629 : : * then it sends 124 bytes of nothing
630 : : */
631 : :
632 : : /*
633 : : * We need to limit the time we spend in this function as we're still
634 : : * using blocking IO at this point
635 : : */
636 : :
637 : 0 : rc = tdnbd_wait_recv(sock, &size, sizeof(size), 0);
638 [ # # ]: 0 : if (rc < 0) {
639 : 0 : ERROR("Error in nbd_negotiate: %s", strerror(-rc));
640 : : goto errout;
641 : : }
642 [ # # ]: 0 : if (rc < sizeof(size)) {
643 : 0 : ERROR("Short read in OLD negotiation(3) (%d)\n", rc);
644 : : goto errout;
645 : : }
646 : :
647 : 0 : INFO("Got size: %"PRIu64"", ntohll(size));
648 : :
649 : 0 : driver->info.size = ntohll(size) >> SECTOR_SHIFT;
650 : 0 : driver->info.sector_size = DEFAULT_SECTOR_SIZE;
651 : 0 : driver->info.info = 0;
652 : :
653 : 0 : rc = tdnbd_wait_recv(sock, &flags, sizeof(flags), 0);
654 [ # # ]: 0 : if (rc < 0) {
655 : 0 : ERROR("Error in nbd_negotiate: %s", strerror(-rc));
656 : : goto errout;
657 : : }
658 [ # # ]: 0 : if (rc < sizeof(flags)) {
659 : 0 : ERROR("Short read in OLD negotiation(4) (%d)\n", rc);
660 : : goto errout;
661 : : }
662 : :
663 : 0 : INFO("Got flags: %"PRIu32"", ntohl(flags));
664 : :
665 [ # # ]: 0 : while (padbytes > 0) {
666 : 0 : rc = tdnbd_wait_recv(sock, buffer, padbytes, 0);
667 [ # # ]: 0 : if (rc < 0) {
668 : 0 : ERROR("Error in nbd_negotiate: %s", strerror(-rc));
669 : : goto errout;
670 : : }
671 : 0 : padbytes -= rc;
672 : : }
673 : :
674 : 0 : rc = fcntl(sock, F_SETFL, O_NONBLOCK);
675 : :
676 [ # # ]: 0 : if (rc != 0) {
677 : 0 : ERROR("Could not set O_NONBLOCK flag");
678 : : goto errout;
679 : : }
680 : 0 : INFO("Successfully connected to Old-style NBD server");
681 : :
682 : 0 : return 0;
683 : :
684 : : errout:
685 : 0 : close(sock);
686 : : return -1;
687 : : }
688 : :
689 : : static int
690 : 0 : tdnbd_nbd_negotiate_new(struct tdnbd_data *prv, td_driver_t *driver)
691 : : {
692 : : int rc;
693 : : uint16_t gflags;
694 : 0 : uint32_t cflags = htobe32(NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES);
695 : 0 : int sock = prv->socket;
696 : :
697 : : /*
698 : : * NBD NEW-style negotiation protocol:
699 : : *
700 : : * Server sends 'NBDMAGIC'
701 : : * then it sends 'IHAVEOPT'
702 : : * then it sends 16 bits of server handshake flags <-- YOU ARE HERE
703 : : * then it expects 32 bits of client handshake flags
704 : : * then we send additional options
705 : : */
706 : :
707 : : /* Receive NBD flags */
708 : 0 : rc = tdnbd_wait_recv(sock, &gflags, sizeof(gflags), 0);
709 [ # # ]: 0 : if (rc < 0) {
710 : 0 : ERROR("Error in nbd_negotiate: %s", strerror(-rc));
711 : : goto errout;
712 : : }
713 [ # # ]: 0 : if (rc < sizeof(gflags)) {
714 : 0 : ERROR("Short read in NEW negotiation(3) (%d)\n", rc);
715 : : goto errout;
716 : : }
717 : :
718 : : /* Send back flags*/
719 : 0 : rc = send(sock, &cflags, sizeof(cflags), 0);
720 [ # # ]: 0 : if (rc < sizeof(cflags)) {
721 : 0 : ERROR("Failed to send client flags");
722 : : goto errout;
723 : : }
724 : :
725 : 0 : return negotiate_client_newstyle_options(sock, driver);
726 : :
727 : : errout:
728 : 0 : close(sock);
729 : : return -1;
730 : : }
731 : :
732 : : static int
733 : 0 : tdnbd_nbd_negotiate(struct tdnbd_data *prv, td_driver_t *driver)
734 : : {
735 : : int rc;
736 : : uint64_t magic;
737 : 0 : int sock = prv->socket;
738 : :
739 : : /* Read the NBD opening magic number, which is the same for all protocol
740 : : * versions */
741 : 0 : rc = tdnbd_wait_recv(sock, &magic, sizeof(magic), 0);
742 [ # # ]: 0 : if (rc < 0) {
743 : 0 : ERROR("Error in nbd_negotiate: %s", strerror(-rc));
744 : : goto errout;
745 : : }
746 [ # # ]: 0 : if (rc < sizeof(magic)) {
747 : 0 : ERROR("Short read in negotiation(1) (wanted %ld got %d)\n", sizeof(magic), rc);
748 : : goto errout;
749 : : }
750 [ # # ]: 0 : if (htonll(NBD_MAGIC) != magic) {
751 : 0 : ERROR("Error in NBD negotiation: wanted '0x%" PRIx64 "' got '0x%" PRIx64 "'", htonll(NBD_MAGIC), magic);
752 : : goto errout;
753 : : }
754 : :
755 : : /* Read the second magic number, which tells us which NBD protocol the
756 : : * server is offering. */
757 : 0 : rc = tdnbd_wait_recv(sock, &magic, sizeof(magic), 0);
758 [ # # ]: 0 : if (rc < 0) {
759 : 0 : ERROR("Error in nbd_negotiate: %s", strerror(-rc));
760 : : goto errout;
761 : : }
762 [ # # ]: 0 : if (rc < sizeof(magic)) {
763 : 0 : ERROR("Short read in negotiation(2) (wanted %ld got %d)\n", sizeof(magic), rc);
764 : : goto errout;
765 : : }
766 : :
767 [ # # ]: 0 : if (htonll(NBD_OLD_VERSION) == magic)
768 : 0 : return tdnbd_nbd_negotiate_old(prv, driver);
769 [ # # ]: 0 : if (htonll(NBD_OPT_MAGIC) == magic)
770 : 0 : return tdnbd_nbd_negotiate_new(prv, driver);
771 : :
772 : 0 : ERROR("Unknown NBD MAGIC 2: Wanted '0x%" PRIx64 "' or '0x%" PRIx64 "', got '0x%" PRIx64 "'",
773 : : htonll(NBD_OLD_VERSION), htonll(NBD_OPT_MAGIC), magic);
774 : :
775 : : errout:
776 : 0 : close(sock);
777 : : return -1;
778 : : }
779 : :
780 : : static int
781 : 0 : tdnbd_connect_import_session(struct tdnbd_data *prv, td_driver_t* driver)
782 : : {
783 : : int sock;
784 : 0 : int opt = 1;
785 : : int rc;
786 : :
787 : 0 : sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
788 [ # # ]: 0 : if (sock < 0) {
789 : 0 : ERROR("Could not create socket: %s\n", strerror(errno));
790 : 0 : return -1;
791 : : }
792 : :
793 : 0 : rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt,
794 : : sizeof(opt));
795 [ # # ]: 0 : if (rc < 0) {
796 : 0 : ERROR("Could not set TCP_NODELAY: %s\n", strerror(errno));
797 : 0 : close(sock);
798 : : return -1;
799 : : }
800 : :
801 : 0 : prv->remote = (struct sockaddr_in *)malloc(
802 : : sizeof(struct sockaddr_in));
803 [ # # ]: 0 : if (!prv->remote) {
804 : 0 : ERROR("struct sockaddr_in malloc failure\n");
805 : 0 : close(sock);
806 : : return -1;
807 : : }
808 : 0 : prv->remote->sin_family = AF_INET;
809 : 0 : rc = inet_pton(AF_INET, prv->peer_ip, &(prv->remote->sin_addr.s_addr));
810 [ # # ]: 0 : if (rc < 0) {
811 : 0 : ERROR("Could not create inaddr: %s\n", strerror(errno));
812 : 0 : free(prv->remote);
813 : 0 : prv->remote = NULL;
814 : 0 : close(sock);
815 : : return -1;
816 : : }
817 [ # # ]: 0 : else if (rc == 0) {
818 : 0 : ERROR("inet_pton parse error\n");
819 : 0 : free(prv->remote);
820 : 0 : prv->remote = NULL;
821 : 0 : close(sock);
822 : : return -1;
823 : : }
824 [ # # ]: 0 : prv->remote->sin_port = htons(prv->port);
825 : :
826 [ # # ]: 0 : if (connect(sock, (struct sockaddr *)prv->remote,
827 : : sizeof(struct sockaddr)) < 0) {
828 : 0 : ERROR("Could not connect to peer: %s\n", strerror(errno));
829 : 0 : free(prv->remote);
830 : 0 : close(sock);
831 : : return -1;
832 : : }
833 : :
834 : 0 : prv->socket = sock;
835 : :
836 : 0 : return tdnbd_nbd_negotiate(prv, driver);
837 : : }
838 : :
839 : : /* -- interface -- */
840 : :
841 : : static int tdnbd_close(td_driver_t*);
842 : :
843 : : static int
844 : 0 : tdnbd_open(td_driver_t* driver, const char* name,
845 : : struct td_vbd_encryption *encryption, td_flag_t flags)
846 : : {
847 : : struct tdnbd_data *prv;
848 : : char peer_ip[256];
849 : : int port;
850 : : int rc;
851 : : int i;
852 : : struct stat buf;
853 : :
854 : 0 : driver->info.sector_size = 512;
855 : 0 : driver->info.info = 0;
856 : :
857 : 0 : prv = (struct tdnbd_data *)driver->data;
858 : : memset(prv, 0, sizeof(struct tdnbd_data));
859 : :
860 : 0 : INFO("Opening nbd export to %s (flags=%x)\n", name, flags);
861 : :
862 : 0 : prv->writer_event_id = -1;
863 : 0 : INIT_LIST_HEAD(&prv->sent_reqs);
864 : 0 : INIT_LIST_HEAD(&prv->pending_reqs);
865 : 0 : INIT_LIST_HEAD(&prv->free_reqs);
866 [ # # ]: 0 : for (i = 0; i < MAX_NBD_REQS; i++) {
867 : 0 : INIT_LIST_HEAD(&prv->requests[i].queue);
868 : 0 : prv->requests[i].timeout_event = -1;
869 : 0 : list_add(&prv->requests[i].queue, &prv->free_reqs);
870 : : }
871 : 0 : prv->nr_free_count = MAX_NBD_REQS;
872 : 0 : prv->cur_reply_qio.buffer = (char *)&prv->current_reply;
873 : 0 : prv->cur_reply_qio.len = sizeof(struct nbd_reply);
874 : :
875 : : bzero(&buf, sizeof(buf));
876 : 0 : rc = stat(name, &buf);
877 [ # # ][ # # ]: 0 : if (!rc && S_ISSOCK(buf.st_mode)) {
878 : 0 : int len = 0;
879 [ # # ]: 0 : if ((prv->socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
880 : 0 : ERROR("failed to create UNIX domain socket: %s\n",
881 : : strerror(errno));
882 : 0 : return -1;
883 : : }
884 : 0 : prv->remote_un.sun_family = AF_UNIX;
885 : 0 : safe_strncpy(prv->remote_un.sun_path, name, sizeof(prv->remote_un.sun_path));
886 : 0 : len = strlen(prv->remote_un.sun_path)
887 : : + sizeof(prv->remote_un.sun_family);
888 [ # # ]: 0 : if ((rc = connect(prv->socket, (struct sockaddr*)&prv->remote_un, len)
889 : 0 : == -1)) {
890 : 0 : ERROR("failed to connect to %s: %s\n", name, strerror(errno));
891 : : return -1;
892 : : }
893 : 0 : rc = tdnbd_nbd_negotiate(prv, driver);
894 [ # # ]: 0 : if (rc) {
895 : 0 : ERROR("failed to negotiate with the NBD server\n");
896 : : return -1;
897 : : }
898 : : } else {
899 : 0 : rc = sscanf(name, "%255[^:]:%d", peer_ip, &port);
900 [ # # ]: 0 : if (rc == 2) {
901 : 0 : prv->peer_ip = strdup(peer_ip);
902 [ # # ]: 0 : if (!prv->peer_ip) {
903 : 0 : ERROR("Failure to malloc for NBD destination");
904 : : return -1;
905 : : }
906 : 0 : prv->port = port;
907 : 0 : prv->name = NULL;
908 : 0 : INFO("Export peer=%s port=%d\n", prv->peer_ip, prv->port);
909 [ # # ]: 0 : if (tdnbd_connect_import_session(prv, driver) < 0)
910 : : return -1;
911 : :
912 : : } else {
913 : 0 : prv->socket = tdnbd_retrieve_passed_fd(name);
914 [ # # ]: 0 : if (prv->socket < 0) {
915 : 0 : ERROR("Couldn't find fd named: %s", name);
916 : : return -1;
917 : : }
918 : 0 : INFO("Found passed fd. Connecting...");
919 : 0 : prv->remote = NULL;
920 : 0 : prv->peer_ip = NULL;
921 : 0 : prv->name = strdup(name);
922 : 0 : prv->port = -1;
923 [ # # ]: 0 : if (tdnbd_nbd_negotiate(prv, driver) < 0) {
924 : 0 : ERROR("Failed to negotiate");
925 : : return -1;
926 : : }
927 : : }
928 : : }
929 : :
930 : 0 : prv->reader_event_id =
931 : 0 : tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
932 : 0 : prv->socket, TV_ZERO,
933 : : tdnbd_reader_cb,
934 : : (void *)prv);
935 : :
936 : 0 : prv->flags = flags;
937 : 0 : prv->closed = 0;
938 : :
939 [ # # ]: 0 : if (flags & TD_OPEN_SECONDARY)
940 : 0 : INFO("Opening in secondary mode: Read requests will be "
941 : : "forwarded");
942 : :
943 : : return 0;
944 : :
945 : : }
946 : :
947 : : static int
948 : 0 : tdnbd_close(td_driver_t* driver)
949 : : {
950 : 0 : struct tdnbd_data *prv = (struct tdnbd_data *)driver->data;
951 : : td_request_t treq;
952 : :
953 : : bzero(&treq, sizeof(treq));
954 : :
955 [ # # ]: 0 : if (prv->closed == 3) {
956 : 0 : INFO("NBD close: already decided that the connection is dead.");
957 [ # # ]: 0 : if (prv->socket >= 0)
958 : 0 : close(prv->socket);
959 : 0 : prv->socket = -1;
960 : 0 : return 0;
961 : : }
962 : :
963 : : /* Send a close packet */
964 : :
965 : 0 : INFO("Sending disconnect request");
966 : 0 : tdnbd_queue_request(prv, TAPDISK_NBD_CMD_DISC, 0, 0, 0, treq, 0);
967 : :
968 : 0 : INFO("Switching socket to blocking IO mode");
969 : 0 : fcntl(prv->socket, F_SETFL, fcntl(prv->socket, F_GETFL) & ~O_NONBLOCK);
970 : :
971 : 0 : INFO("Writing disconnection request");
972 : 0 : tdnbd_writer_cb(0, 0, prv);
973 : :
974 : 0 : INFO("Written");
975 : :
976 [ # # ]: 0 : if (prv->peer_ip) {
977 : 0 : free(prv->peer_ip);
978 : 0 : prv->peer_ip = NULL;
979 : : }
980 : :
981 [ # # ]: 0 : if (prv->name) {
982 : 0 : tdnbd_stash_passed_fd(prv->socket, prv->name, 0);
983 : 0 : free(prv->name);
984 : : } else {
985 [ # # ]: 0 : if (prv->socket >= 0)
986 : 0 : close(prv->socket);
987 : 0 : prv->socket = -1;
988 : : }
989 : :
990 : : return 0;
991 : : }
992 : :
993 : : static void
994 : 0 : tdnbd_queue_read(td_driver_t* driver, td_request_t treq)
995 : : {
996 : 0 : struct tdnbd_data *prv = (struct tdnbd_data *)driver->data;
997 : 0 : int size = treq.secs * driver->info.sector_size;
998 : 0 : uint64_t offset = treq.sec * (uint64_t)driver->info.sector_size;
999 : :
1000 [ # # ]: 0 : if (prv->flags & TD_OPEN_SECONDARY)
1001 : 0 : td_forward_request(treq);
1002 : : else
1003 : 0 : tdnbd_queue_request(prv, TAPDISK_NBD_CMD_READ, offset, treq.buf, size,
1004 : : treq, 0);
1005 : 0 : }
1006 : :
1007 : : static void
1008 : 0 : tdnbd_queue_write(td_driver_t* driver, td_request_t treq)
1009 : : {
1010 : 0 : struct tdnbd_data *prv = (struct tdnbd_data *)driver->data;
1011 : 0 : int size = treq.secs * driver->info.sector_size;
1012 : 0 : uint64_t offset = treq.sec * (uint64_t)driver->info.sector_size;
1013 : :
1014 : 0 : tdnbd_queue_request(prv, TAPDISK_NBD_CMD_WRITE,
1015 : 0 : offset, treq.buf, size, treq, 0);
1016 : 0 : }
1017 : :
1018 : : static int
1019 : 0 : tdnbd_get_parent_id(td_driver_t* driver, td_disk_id_t* id)
1020 : : {
1021 : 0 : return TD_NO_PARENT;
1022 : : }
1023 : :
1024 : : static int
1025 : 0 : tdnbd_validate_parent(td_driver_t *driver,
1026 : : td_driver_t *parent, td_flag_t flags)
1027 : : {
1028 : 0 : return -EINVAL;
1029 : : }
1030 : :
1031 : : struct tap_disk tapdisk_nbd = {
1032 : : .disk_type = "tapdisk_nbd",
1033 : : .private_data_size = sizeof(struct tdnbd_data),
1034 : : .flags = 0,
1035 : : .td_open = tdnbd_open,
1036 : : .td_close = tdnbd_close,
1037 : : .td_queue_read = tdnbd_queue_read,
1038 : : .td_queue_write = tdnbd_queue_write,
1039 : : .td_get_parent_id = tdnbd_get_parent_id,
1040 : : .td_validate_parent = tdnbd_validate_parent,
1041 : : };
|