diff options
Diffstat (limited to 'toys/other/nbd_server.c')
-rw-r--r-- | toys/other/nbd_server.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/toys/other/nbd_server.c b/toys/other/nbd_server.c new file mode 100644 index 00000000..98c1937c --- /dev/null +++ b/toys/other/nbd_server.c @@ -0,0 +1,88 @@ +/* nbd-server.c - network block device server + * + * Copyright 2022 Rob Landley <rob@landley.net> + * + * Not in SUSv4. + * + * See https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md + +// Work around dash in name trying to put - in function name. +USE_NBD_SERVER(NEWTOY(nbd_server, "<1>1r", 0)) +USE_NBD_SERVER(OLDTOY(nbd-server, nbd_server, TOYFLAG_USR|TOYFLAG_BIN)) + +config NBD_SERVER + bool "nbd-server" + default y + help + usage: nbd-server [-r] FILE + + Serve a Network Block Device from FILE on stdin/out (ala inetd). + + -r Read only export +*/ + +// TODO: -r, block size, exit signal? + +#define FOR_nbd_server +#include "toys.h" + +static int copy_loop(int from, int to, unsigned len) +{ + int try, rc = 0; + + errno = 0; + while (len) { + xreadall(from, toybuf, try = len>4096 ? 4096 : len); + if (!rc && try != writeall(to, toybuf, try)) rc = errno; + len -= try; + } + + return rc; +} + +void nbd_server_main(void) +{ + unsigned long long *ll = (void *)toybuf, offset, handle; + unsigned short *ss = (void *)toybuf; + unsigned *uu = (void *)toybuf, type, length; + int fd = xopen(*toys.optargs, O_RDWR*!FLAG(r)); + + type = 1; + setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &type, sizeof(int)); + + // Send original recipe negotiation, with device length and flags + memcpy(toybuf, "NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53", 16); + ll[2] = SWAP_BE64(fdlength(fd)); + uu[6] = SWAP_BE32(5+2*FLAG(r)); // has flags, can flush, maybe read only + xwrite(1, toybuf, 152); + + // Simple loop, handles one request at a time with "simple" reply. + for (;;) { + // Fetch request into toybuf + xreadall(0, toybuf, 28); + if (SWAP_BE32(*uu) != 0x25609513) break; + type = SWAP_BE16(ss[3]); + handle = SWAP_BE64(ll[1]); + offset = SWAP_BE64(ll[2]); + length = SWAP_BE32(uu[6]); + + // type 0 = read, 1 = write, 2 = disconnect, 3 = flush + if (type==2 || type>3) break; // disconnect + if (type==3) { // flush + if (fdatasync(fd)) uu[1] = SWAP_BE32(errno); + } else { + xlseek(fd, offset, SEEK_SET); + if (type==1) { // write + uu[1] = copy_loop(0, fd, length); + ll[1] = SWAP_BE64(handle); + } else uu[1] = 0; // read never reports errors because send header first + } + + // Simple reply in toybuf (handle stays put) + *uu = SWAP_BE32(0x67446698); + xwrite(1, toybuf, 16); + + // Append read payload + if (!type) if (copy_loop(fd, 1, length)) break; + } +} |