]>
Commit | Line | Data |
---|---|---|
1 | /*\ | |
2 | * Copyright (C) 2005 Anthony Liguori <[email protected]> | |
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 <[email protected]>\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 <[email protected]>.\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 | } |