Merge NBD client/server, by Laurent Vivier.
[qemu] / qemu-nbd.c
1 /*
2  *  Copyright (C) 2005  Anthony Liguori <anthony@codemonkey.ws>
3  *
4  *  Network Block Device
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; under version 2 of the License.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <qemu-common.h>
21 #include "block_int.h"
22 #include "nbd.h"
23
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <getopt.h>
27 #include <err.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netinet/tcp.h>
32 #include <arpa/inet.h>
33 #include <signal.h>
34
35 #define SOCKET_PATH    "/var/lock/qemu-nbd-%s"
36
37 int verbose;
38
39 static void usage(const char *name)
40 {
41     printf(
42 "Usage: %s [OPTIONS] FILE\n"
43 "QEMU Disk Network Block Device Server\n"
44 "\n"
45 "  -p, --port=PORT      port to listen on (default `1024')\n"
46 "  -o, --offset=OFFSET  offset into the image\n"
47 "  -b, --bind=IFACE     interface to bind to (default `0.0.0.0')\n"
48 "  -k, --socket=PATH    path to the unix socket\n"
49 "                       (default '"SOCKET_PATH"')\n"
50 "  -r, --read-only      export read-only\n"
51 "  -P, --partition=NUM  only expose partition NUM\n"
52 "  -c, --connect=DEV    connect FILE to the local NBD device DEV\n"
53 "  -d, --disconnect     disconnect the specified device\n"
54 "  -v, --verbose        display extra debugging information\n"
55 "  -h, --help           display this help and exit\n"
56 "  -V, --version        output version information and exit\n"
57 "\n"
58 "Report bugs to <anthony@codemonkey.ws>\n"
59     , name, "DEVICE");
60 }
61
62 static void version(const char *name)
63 {
64     printf(
65 "qemu-nbd version 0.0.1\n"
66 "Written by Anthony Liguori.\n"
67 "\n"
68 "Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>.\n"
69 "This is free software; see the source for copying conditions.  There is NO\n"
70 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
71     );
72 }
73
74 struct partition_record
75 {
76     uint8_t bootable;
77     uint8_t start_head;
78     uint32_t start_cylinder;
79     uint8_t start_sector;
80     uint8_t system;
81     uint8_t end_head;
82     uint8_t end_cylinder;
83     uint8_t end_sector;
84     uint32_t start_sector_abs;
85     uint32_t nb_sectors_abs;
86 };
87
88 static void read_partition(uint8_t *p, struct partition_record *r)
89 {
90     r->bootable = p[0];
91     r->start_head = p[1];
92     r->start_cylinder = p[3] | ((p[2] << 2) & 0x0300);
93     r->start_sector = p[2] & 0x3f;
94     r->system = p[4];
95     r->end_head = p[5];
96     r->end_cylinder = p[7] | ((p[6] << 2) & 0x300);
97     r->end_sector = p[6] & 0x3f;
98     r->start_sector_abs = p[8] | p[9] << 8 | p[10] << 16 | p[11] << 24;
99     r->nb_sectors_abs = p[12] | p[13] << 8 | p[14] << 16 | p[15] << 24;
100 }
101
102 static int find_partition(BlockDriverState *bs, int partition,
103                           off_t *offset, off_t *size)
104 {
105     struct partition_record mbr[4];
106     uint8_t data[512];
107     int i;
108     int ext_partnum = 4;
109
110     if (bdrv_read(bs, 0, data, 1))
111         errx(EINVAL, "error while reading");
112
113     if (data[510] != 0x55 || data[511] != 0xaa) {
114         errno = -EINVAL;
115         return -1;
116     }
117
118     for (i = 0; i < 4; i++) {
119         read_partition(&data[446 + 16 * i], &mbr[i]);
120
121         if (!mbr[i].nb_sectors_abs)
122             continue;
123
124         if (mbr[i].system == 0xF || mbr[i].system == 0x5) {
125             struct partition_record ext[4];
126             uint8_t data1[512];
127             int j;
128
129             if (bdrv_read(bs, mbr[i].start_sector_abs, data1, 1))
130                 errx(EINVAL, "error while reading");
131
132             for (j = 0; j < 4; j++) {
133                 read_partition(&data1[446 + 16 * j], &ext[j]);
134                 if (!ext[j].nb_sectors_abs)
135                     continue;
136
137                 if ((ext_partnum + j + 1) == partition) {
138                     *offset = (uint64_t)ext[j].start_sector_abs << 9;
139                     *size = (uint64_t)ext[j].nb_sectors_abs << 9;
140                     return 0;
141                 }
142             }
143             ext_partnum += 4;
144         } else if ((i + 1) == partition) {
145             *offset = (uint64_t)mbr[i].start_sector_abs << 9;
146             *size = (uint64_t)mbr[i].nb_sectors_abs << 9;
147             return 0;
148         }
149     }
150
151     errno = -ENOENT;
152     return -1;
153 }
154
155 static void show_parts(const char *device)
156 {
157     if (fork() == 0) {
158         int nbd;
159
160         /* linux just needs an open() to trigger
161          * the partition table update
162          * but remember to load the module with max_part != 0 :
163          *     modprobe nbd max_part=63
164          */
165         nbd = open(device, O_RDWR);
166         if (nbd != -1)
167               close(nbd);
168         exit(0);
169     }
170 }
171
172 int main(int argc, char **argv)
173 {
174     BlockDriverState *bs;
175     off_t dev_offset = 0;
176     off_t offset = 0;
177     bool readonly = false;
178     bool disconnect = false;
179     const char *bindto = "0.0.0.0";
180     int port = 1024;
181     int sock, csock;
182     struct sockaddr_in addr;
183     socklen_t addr_len = sizeof(addr);
184     off_t fd_size;
185     char *device = NULL;
186     char *socket = NULL;
187     char sockpath[128];
188     const char *sopt = "hVbo:p:rsP:c:dvk:";
189     struct option lopt[] = {
190         { "help", 0, 0, 'h' },
191         { "version", 0, 0, 'V' },
192         { "bind", 1, 0, 'b' },
193         { "port", 1, 0, 'p' },
194         { "socket", 1, 0, 'k' },
195         { "offset", 1, 0, 'o' },
196         { "read-only", 0, 0, 'r' },
197         { "partition", 1, 0, 'P' },
198         { "connect", 1, 0, 'c' },
199         { "disconnect", 0, 0, 'd' },
200         { "snapshot", 0, 0, 's' },
201         { "verbose", 0, 0, 'v' },
202         { NULL, 0, 0, 0 }
203     };
204     int ch;
205     int opt_ind = 0;
206     int li;
207     char *end;
208     bool snapshot = false;
209     int partition = -1;
210     int fd;
211     int ret;
212
213     while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
214         switch (ch) {
215         case 's':
216             snapshot = true;
217             break;
218         case 'b':
219             bindto = optarg;
220             break;
221         case 'p':
222             li = strtol(optarg, &end, 0);
223             if (*end) {
224                 errx(EINVAL, "Invalid port `%s'", optarg);
225             }
226             if (li < 1 || li > 65535) {
227                 errx(EINVAL, "Port out of range `%s'", optarg);
228             }
229             port = (uint16_t)li;
230             break;
231         case 'o':
232                 dev_offset = strtoll (optarg, &end, 0);
233             if (*end) {
234                 errx(EINVAL, "Invalid offset `%s'", optarg);
235             }
236             if (dev_offset < 0) {
237                 errx(EINVAL, "Offset must be positive `%s'", optarg);
238             }
239             break;
240         case 'r':
241             readonly = true;
242             break;
243         case 'P':
244             partition = strtol(optarg, &end, 0);
245             if (*end)
246                 errx(EINVAL, "Invalid partition `%s'", optarg);
247             if (partition < 1 || partition > 8)
248                 errx(EINVAL, "Invalid partition %d", partition);
249             break;
250         case 'k':
251             socket = optarg;
252             if (socket[0] != '/')
253                 errx(EINVAL, "socket path must be absolute\n");
254             break;
255         case 'd':
256             disconnect = true;
257             break;
258         case 'c':
259             device = optarg;
260             break;
261         case 'v':
262             verbose = 1;
263             break;
264         case 'V':
265             version(argv[0]);
266             exit(0);
267             break;
268         case 'h':
269             usage(argv[0]);
270             exit(0);
271             break;
272         case '?':
273             errx(EINVAL, "Try `%s --help' for more information.",
274                  argv[0]);
275         }
276     }
277
278     if ((argc - optind) != 1) {
279         errx(EINVAL, "Invalid number of argument.\n"
280              "Try `%s --help' for more information.",
281              argv[0]);
282     }
283
284     if (disconnect) {
285         fd = open(argv[optind], O_RDWR);
286         if (fd == -1)
287             errx(errno, "Cannot open %s", argv[optind]);
288
289         nbd_disconnect(fd);
290
291         close(fd);
292
293         printf("%s disconnected\n", argv[optind]);
294
295         return 0;
296     }
297
298     bdrv_init();
299
300     bs = bdrv_new("hda");
301     if (bs == NULL)
302         return 1;
303
304     if (bdrv_open(bs, argv[optind], snapshot) == -1)
305         return 1;
306
307     fd_size = bs->total_sectors * 512;
308
309     if (partition != -1 &&
310         find_partition(bs, partition, &dev_offset, &fd_size))
311         errx(errno, "Could not find partition %d", partition);
312
313     if (device) {
314         pid_t pid;
315         if (!verbose)
316             daemon(0, 0);       /* detach client and server */
317
318         if (socket == NULL) {
319             sprintf(sockpath, SOCKET_PATH, basename(device));
320             socket = sockpath;
321         }
322
323         pid = fork();
324         if (pid < 0)
325             return 1;
326         if (pid != 0) {
327             off_t size;
328             size_t blocksize;
329
330             ret = 0;
331             bdrv_close(bs);
332
333             do {
334                 sock = unix_socket_outgoing(socket);
335                 if (sock == -1) {
336                     if (errno != ENOENT && errno != ECONNREFUSED)
337                         goto out;
338                     sleep(1);   /* wait children */
339                 }
340             } while (sock == -1);
341
342             fd = open(device, O_RDWR);
343             if (fd == -1) {
344                 ret = 1;
345                 goto out;
346             }
347
348             ret = nbd_receive_negotiate(sock, &size, &blocksize);
349             if (ret == -1) {
350                 ret = 1;
351                 goto out;
352             }
353
354             ret = nbd_init(fd, sock, size, blocksize);
355             if (ret == -1) {
356                 ret = 1;
357                 goto out;
358             }
359
360             printf("NBD device %s is now connected to file %s\n",
361                     device, argv[optind]);
362
363             /* update partition table */
364
365             show_parts(device);
366
367             nbd_client(fd, sock);
368             close(fd);
369  out:
370             kill(pid, SIGTERM);
371             unlink(socket);
372
373             return ret;
374         }
375         /* children */
376     }
377
378     if (socket) {
379         sock = unix_socket_incoming(socket);
380     } else {
381         sock = tcp_socket_incoming(bindto, port);
382     }
383
384     if (sock == -1)
385         return 1;
386
387     csock = accept(sock,
388                (struct sockaddr *)&addr,
389                &addr_len);
390     if (csock == -1)
391         return 1;
392
393     /* new fd_size is calculated by find_partition */
394     if (nbd_negotiate(bs, csock, fd_size) == -1)
395         return 1;
396
397     while (nbd_trip(bs, csock, fd_size, dev_offset, &offset, readonly) == 0);
398
399     close(csock);
400     close(sock);
401     bdrv_close(bs);
402     if (socket)
403         unlink(socket);
404
405     return 0;
406 }