1 /* nbd-server.c - network block device server
2 *
3 * Copyright 2022 Rob Landley <[email protected]>
4 *
5 * Not in SUSv4.
6 *
7 * See https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md
8
9 // Work around dash in name trying to put - in function name.
10 USE_NBD_SERVER(NEWTOY(nbd_server, "<1>1r", 0))
11 USE_NBD_SERVER(OLDTOY(nbd-server, nbd_server, TOYFLAG_USR|TOYFLAG_BIN))
12
13 config NBD_SERVER
14 bool "nbd-server"
15 default y
16 help
17 usage: nbd-server [-r] FILE
18
19 Serve a Network Block Device from FILE on stdin/out (ala inetd).
20
21 -r Read only export
22 */
23
24 // TODO: -r, block size, exit signal?
25
26 #define FOR_nbd_server
27 #include "toys.h"
28
copy_loop(int from,int to,unsigned len)29 static int copy_loop(int from, int to, unsigned len)
30 {
31 int try, rc = 0;
32
33 errno = 0;
34 while (len) {
35 xreadall(from, toybuf, try = len>4096 ? 4096 : len);
36 if (!rc && try != writeall(to, toybuf, try)) rc = errno;
37 len -= try;
38 }
39
40 return rc;
41 }
42
nbd_server_main(void)43 void nbd_server_main(void)
44 {
45 unsigned long long *ll = (void *)toybuf, offset, handle;
46 unsigned short *ss = (void *)toybuf;
47 unsigned *uu = (void *)toybuf, type, length;
48 int fd = xopen(*toys.optargs, O_RDWR*!FLAG(r));
49
50 type = 1;
51 setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &type, sizeof(int));
52
53 // Send original recipe negotiation, with device length and flags
54 memcpy(toybuf, "NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53", 16);
55 ll[2] = SWAP_BE64(fdlength(fd));
56 uu[6] = SWAP_BE32(5+2*FLAG(r)); // has flags, can flush, maybe read only
57 xwrite(1, toybuf, 152);
58
59 // Simple loop, handles one request at a time with "simple" reply.
60 for (;;) {
61 // Fetch request into toybuf
62 xreadall(0, toybuf, 28);
63 if (SWAP_BE32(*uu) != 0x25609513) break;
64 type = SWAP_BE16(ss[3]);
65 handle = SWAP_BE64(ll[1]);
66 offset = SWAP_BE64(ll[2]);
67 length = SWAP_BE32(uu[6]);
68
69 // type 0 = read, 1 = write, 2 = disconnect, 3 = flush
70 if (type==2 || type>3) break; // disconnect
71 if (type==3) { // flush
72 if (fdatasync(fd)) uu[1] = SWAP_BE32(errno);
73 } else {
74 xlseek(fd, offset, SEEK_SET);
75 if (type==1) { // write
76 uu[1] = copy_loop(0, fd, length);
77 ll[1] = SWAP_BE64(handle);
78 } else uu[1] = 0; // read never reports errors because send header first
79 }
80
81 // Simple reply in toybuf (handle stays put)
82 *uu = SWAP_BE32(0x67446698);
83 xwrite(1, toybuf, 16);
84
85 // Append read payload
86 if (!type) if (copy_loop(fd, 1, length)) break;
87 }
88 }
89