xref: /aosp_15_r20/external/toybox/toys/other/nbd_server.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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