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 <malloc.h> 25 #include <stdarg.h> 26 #include <stdio.h> 27 #include <getopt.h> 28 #include <err.h> 29 #include <sys/socket.h> 30 #include <netinet/in.h> 31 #include <netinet/tcp.h> 32 #include <arpa/inet.h> 33 34 int verbose; 35 36 static void usage(const char *name) 37 { 38 printf( 39 "Usage: %s [OPTIONS] FILE\n" 40 "QEMU Disk Network Block Device Server\n" 41 "\n" 42 " -p, --port=PORT port to listen on (default `1024')\n" 43 " -o, --offset=OFFSET offset into the image\n" 44 " -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n" 45 " -r, --read-only export read-only\n" 46 " -P, --partition=NUM only expose partition NUM\n" 47 " -v, --verbose display extra debugging information\n" 48 " -h, --help display this help and exit\n" 49 " -V, --version output version information and exit\n" 50 "\n" 51 "Report bugs to <anthony@codemonkey.ws>\n" 52 , name); 53 } 54 55 static void version(const char *name) 56 { 57 printf( 58 "qemu-nbd version 0.0.1\n" 59 "Written by Anthony Liguori.\n" 60 "\n" 61 "Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>.\n" 62 "This is free software; see the source for copying conditions. There is NO\n" 63 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" 64 ); 65 } 66 67 struct partition_record 68 { 69 uint8_t bootable; 70 uint8_t start_head; 71 uint32_t start_cylinder; 72 uint8_t start_sector; 73 uint8_t system; 74 uint8_t end_head; 75 uint8_t end_cylinder; 76 uint8_t end_sector; 77 uint32_t start_sector_abs; 78 uint32_t nb_sectors_abs; 79 }; 80 81 static void read_partition(uint8_t *p, struct partition_record *r) 82 { 83 r->bootable = p[0]; 84 r->start_head = p[1]; 85 r->start_cylinder = p[3] | ((p[2] << 2) & 0x0300); 86 r->start_sector = p[2] & 0x3f; 87 r->system = p[4]; 88 r->end_head = p[5]; 89 r->end_cylinder = p[7] | ((p[6] << 2) & 0x300); 90 r->end_sector = p[6] & 0x3f; 91 r->start_sector_abs = p[8] | p[9] << 8 | p[10] << 16 | p[11] << 24; 92 r->nb_sectors_abs = p[12] | p[13] << 8 | p[14] << 16 | p[15] << 24; 93 } 94 95 static int find_partition(BlockDriverState *bs, int partition, 96 off_t *offset, off_t *size) 97 { 98 struct partition_record mbr[4]; 99 uint8_t data[512]; 100 int i; 101 int ext_partnum = 4; 102 103 if (bdrv_read(bs, 0, data, 1)) 104 errx(EINVAL, "error while reading"); 105 106 if (data[510] != 0x55 || data[511] != 0xaa) { 107 errno = -EINVAL; 108 return -1; 109 } 110 111 for (i = 0; i < 4; i++) { 112 read_partition(&data[446 + 16 * i], &mbr[i]); 113 114 if (!mbr[i].nb_sectors_abs) 115 continue; 116 117 if (mbr[i].system == 0xF || mbr[i].system == 0x5) { 118 struct partition_record ext[4]; 119 uint8_t data1[512]; 120 int j; 121 122 if (bdrv_read(bs, mbr[i].start_sector_abs, data1, 1)) 123 errx(EINVAL, "error while reading"); 124 125 for (j = 0; j < 4; j++) { 126 read_partition(&data1[446 + 16 * j], &ext[j]); 127 if (!ext[j].nb_sectors_abs) 128 continue; 129 130 if ((ext_partnum + j + 1) == partition) { 131 *offset = (uint64_t)ext[j].start_sector_abs << 9; 132 *size = (uint64_t)ext[j].nb_sectors_abs << 9; 133 return 0; 134 } 135 } 136 ext_partnum += 4; 137 } else if ((i + 1) == partition) { 138 *offset = (uint64_t)mbr[i].start_sector_abs << 9; 139 *size = (uint64_t)mbr[i].nb_sectors_abs << 9; 140 return 0; 141 } 142 } 143 144 errno = -ENOENT; 145 return -1; 146 } 147 148 int main(int argc, char **argv) 149 { 150 BlockDriverState *bs; 151 off_t dev_offset = 0; 152 off_t offset = 0; 153 bool readonly = false; 154 const char *bindto = "0.0.0.0"; 155 int port = 1024; 156 int sock, csock; 157 struct sockaddr_in addr; 158 socklen_t addr_len = sizeof(addr); 159 off_t fd_size; 160 const char *sopt = "hVbo:p:rsP:v"; 161 struct option lopt[] = { 162 { "help", 0, 0, 'h' }, 163 { "version", 0, 0, 'V' }, 164 { "bind", 1, 0, 'b' }, 165 { "port", 1, 0, 'p' }, 166 { "offset", 1, 0, 'o' }, 167 { "read-only", 0, 0, 'r' }, 168 { "partition", 1, 0, 'P' }, 169 { "snapshot", 0, 0, 's' }, 170 { "verbose", 0, 0, 'v' }, 171 }; 172 int ch; 173 int opt_ind = 0; 174 int li; 175 char *end; 176 bool snapshot = false; 177 int partition = -1; 178 179 while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { 180 switch (ch) { 181 case 's': 182 snapshot = true; 183 break; 184 case 'b': 185 bindto = optarg; 186 break; 187 case 'p': 188 li = strtol(optarg, &end, 0); 189 if (*end) { 190 errx(EINVAL, "Invalid port `%s'", optarg); 191 } 192 if (li < 1 || li > 65535) { 193 errx(EINVAL, "Port out of range `%s'", optarg); 194 } 195 port = (uint16_t)li; 196 break; 197 case 'o': 198 dev_offset = strtoll (optarg, &end, 0); 199 if (*end) { 200 errx(EINVAL, "Invalid offset `%s'", optarg); 201 } 202 if (dev_offset < 0) { 203 errx(EINVAL, "Offset must be positive `%s'", optarg); 204 } 205 break; 206 case 'r': 207 readonly = true; 208 break; 209 case 'P': 210 partition = strtol(optarg, &end, 0); 211 if (*end) 212 errx(EINVAL, "Invalid partition `%s'", optarg); 213 if (partition < 1 || partition > 8) 214 errx(EINVAL, "Invalid partition %d", partition); 215 break; 216 case 'v': 217 verbose = 1; 218 break; 219 case 'V': 220 version(argv[0]); 221 exit(0); 222 break; 223 case 'h': 224 usage(argv[0]); 225 exit(0); 226 break; 227 case '?': 228 errx(EINVAL, "Try `%s --help' for more information.", 229 argv[0]); 230 } 231 } 232 233 if ((argc - optind) != 1) { 234 errx(EINVAL, "Invalid number of argument.\n" 235 "Try `%s --help' for more information.", 236 argv[0]); 237 } 238 239 bdrv_init(); 240 241 bs = bdrv_new("hda"); 242 if (bs == NULL) 243 return 1; 244 245 if (bdrv_open(bs, argv[optind], snapshot) == -1) 246 return 1; 247 248 fd_size = bs->total_sectors * 512; 249 250 if (partition != -1 && 251 find_partition(bs, partition, &dev_offset, &fd_size)) 252 errx(errno, "Could not find partition %d", partition); 253 254 sock = tcp_socket_incoming(bindto, port); 255 if (sock == -1) 256 return 1; 257 258 csock = accept(sock, 259 (struct sockaddr *)&addr, 260 &addr_len); 261 if (csock == -1) 262 return 1; 263 264 /* new fd_size is calculated by find_partition */ 265 if (nbd_negotiate(bs, csock, fd_size) == -1) 266 return 1; 267 268 while (nbd_trip(bs, csock, fd_size, dev_offset, &offset, readonly) == 0); 269 270 close(csock); 271 close(sock); 272 bdrv_close(bs); 273 274 return 0; 275 } 276