Revision cd831bd7
b/nbd.c | ||
---|---|---|
25 | 25 |
#include <ctype.h> |
26 | 26 |
#include <inttypes.h> |
27 | 27 |
#include <sys/socket.h> |
28 |
#include <sys/un.h> |
|
28 | 29 |
#include <netinet/in.h> |
29 | 30 |
#include <netinet/tcp.h> |
30 | 31 |
#include <arpa/inet.h> |
... | ... | |
183 | 184 |
return -1; |
184 | 185 |
} |
185 | 186 |
|
187 |
int unix_socket_incoming(const char *path) |
|
188 |
{ |
|
189 |
int s; |
|
190 |
struct sockaddr_un addr; |
|
191 |
int serrno; |
|
192 |
|
|
193 |
s = socket(PF_UNIX, SOCK_STREAM, 0); |
|
194 |
if (s == -1) { |
|
195 |
return -1; |
|
196 |
} |
|
197 |
|
|
198 |
memset(&addr, 0, sizeof(addr)); |
|
199 |
addr.sun_family = AF_UNIX; |
|
200 |
pstrcpy(addr.sun_path, sizeof(addr.sun_path), path); |
|
201 |
|
|
202 |
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { |
|
203 |
goto error; |
|
204 |
} |
|
205 |
|
|
206 |
if (listen(s, 128) == -1) { |
|
207 |
goto error; |
|
208 |
} |
|
209 |
|
|
210 |
return s; |
|
211 |
error: |
|
212 |
serrno = errno; |
|
213 |
close(s); |
|
214 |
errno = serrno; |
|
215 |
return -1; |
|
216 |
} |
|
217 |
|
|
218 |
int unix_socket_outgoing(const char *path) |
|
219 |
{ |
|
220 |
int s; |
|
221 |
struct sockaddr_un addr; |
|
222 |
int serrno; |
|
223 |
|
|
224 |
s = socket(PF_UNIX, SOCK_STREAM, 0); |
|
225 |
if (s == -1) { |
|
226 |
return -1; |
|
227 |
} |
|
228 |
|
|
229 |
memset(&addr, 0, sizeof(addr)); |
|
230 |
addr.sun_family = AF_UNIX; |
|
231 |
pstrcpy(addr.sun_path, sizeof(addr.sun_path), path); |
|
232 |
|
|
233 |
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { |
|
234 |
goto error; |
|
235 |
} |
|
236 |
|
|
237 |
return s; |
|
238 |
error: |
|
239 |
serrno = errno; |
|
240 |
close(s); |
|
241 |
errno = serrno; |
|
242 |
return -1; |
|
243 |
} |
|
244 |
|
|
245 |
|
|
186 | 246 |
/* Basic flow |
187 | 247 |
|
188 | 248 |
Server Client |
... | ... | |
225 | 285 |
return 0; |
226 | 286 |
} |
227 | 287 |
|
228 |
int nbd_receive_negotiate(int fd, int csock)
|
|
288 |
int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
|
|
229 | 289 |
{ |
230 | 290 |
char buf[8 + 8 + 8 + 128]; |
231 | 291 |
uint64_t magic; |
232 |
off_t size; |
|
233 |
size_t blocksize; |
|
234 | 292 |
|
235 | 293 |
TRACE("Receiving negotation."); |
236 | 294 |
|
... | ... | |
241 | 299 |
} |
242 | 300 |
|
243 | 301 |
magic = be64_to_cpup((uint64_t*)(buf + 8)); |
244 |
size = be64_to_cpup((uint64_t*)(buf + 16)); |
|
245 |
blocksize = 1024; |
|
302 |
*size = be64_to_cpup((uint64_t*)(buf + 16));
|
|
303 |
*blocksize = 1024;
|
|
246 | 304 |
|
247 | 305 |
TRACE("Magic is %c%c%c%c%c%c%c%c", |
248 | 306 |
isprint(buf[0]) ? buf[0] : '.', |
... | ... | |
254 | 312 |
isprint(buf[6]) ? buf[6] : '.', |
255 | 313 |
isprint(buf[7]) ? buf[7] : '.'); |
256 | 314 |
TRACE("Magic is 0x%" PRIx64, magic); |
257 |
TRACE("Size is %" PRIu64, size); |
|
315 |
TRACE("Size is %" PRIu64, *size);
|
|
258 | 316 |
|
259 | 317 |
if (memcmp(buf, "NBDMAGIC", 8) != 0) { |
260 | 318 |
LOG("Invalid magic received"); |
... | ... | |
269 | 327 |
errno = EINVAL; |
270 | 328 |
return -1; |
271 | 329 |
} |
330 |
return 0; |
|
331 |
} |
|
272 | 332 |
|
333 |
int nbd_init(int fd, int csock, off_t size, size_t blocksize) |
|
334 |
{ |
|
273 | 335 |
TRACE("Setting block size to %lu", (unsigned long)blocksize); |
274 | 336 |
|
275 | 337 |
if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) { |
b/nbd.h | ||
---|---|---|
27 | 27 |
#include "block_int.h" |
28 | 28 |
|
29 | 29 |
int tcp_socket_incoming(const char *address, uint16_t port); |
30 |
int unix_socket_outgoing(const char *path); |
|
31 |
int unix_socket_incoming(const char *path); |
|
30 | 32 |
|
31 | 33 |
int nbd_negotiate(BlockDriverState *bs, int csock, off_t size); |
32 |
int nbd_receive_negotiate(int fd, int csock); |
|
34 |
int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize); |
|
35 |
int nbd_init(int fd, int csock, off_t size, size_t blocksize); |
|
33 | 36 |
int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, off_t *offset, bool readonly); |
34 | 37 |
int nbd_client(int fd, int csock); |
35 | 38 |
int nbd_disconnect(int fd); |
b/qemu-nbd.c | ||
---|---|---|
1 |
/*\
|
|
1 |
/* |
|
2 | 2 |
* Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws> |
3 | 3 |
* |
4 | 4 |
* Network Block Device |
... | ... | |
25 | 25 |
#include <stdio.h> |
26 | 26 |
#include <getopt.h> |
27 | 27 |
#include <err.h> |
28 |
#include <sys/types.h> |
|
28 | 29 |
#include <sys/socket.h> |
29 | 30 |
#include <netinet/in.h> |
30 | 31 |
#include <netinet/tcp.h> |
31 | 32 |
#include <arpa/inet.h> |
33 |
#include <signal.h> |
|
34 |
|
|
35 |
#define SOCKET_PATH "/var/lock/qemu-nbd-%s" |
|
32 | 36 |
|
33 | 37 |
int verbose; |
34 | 38 |
|
... | ... | |
41 | 45 |
" -p, --port=PORT port to listen on (default `1024')\n" |
42 | 46 |
" -o, --offset=OFFSET offset into the image\n" |
43 | 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" |
|
44 | 50 |
" -r, --read-only export read-only\n" |
45 | 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" |
|
46 | 54 |
" -v, --verbose display extra debugging information\n" |
47 | 55 |
" -h, --help display this help and exit\n" |
48 | 56 |
" -V, --version output version information and exit\n" |
49 | 57 |
"\n" |
50 | 58 |
"Report bugs to <anthony@codemonkey.ws>\n" |
51 |
, name); |
|
59 |
, name, "DEVICE");
|
|
52 | 60 |
} |
53 | 61 |
|
54 | 62 |
static void version(const char *name) |
... | ... | |
144 | 152 |
return -1; |
145 | 153 |
} |
146 | 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 |
|
|
147 | 172 |
int main(int argc, char **argv) |
148 | 173 |
{ |
149 | 174 |
BlockDriverState *bs; |
150 | 175 |
off_t dev_offset = 0; |
151 | 176 |
off_t offset = 0; |
152 | 177 |
bool readonly = false; |
178 |
bool disconnect = false; |
|
153 | 179 |
const char *bindto = "0.0.0.0"; |
154 | 180 |
int port = 1024; |
155 | 181 |
int sock, csock; |
156 | 182 |
struct sockaddr_in addr; |
157 | 183 |
socklen_t addr_len = sizeof(addr); |
158 | 184 |
off_t fd_size; |
159 |
const char *sopt = "hVbo:p:rsP:v"; |
|
185 |
char *device = NULL; |
|
186 |
char *socket = NULL; |
|
187 |
char sockpath[128]; |
|
188 |
const char *sopt = "hVbo:p:rsP:c:dvk:"; |
|
160 | 189 |
struct option lopt[] = { |
161 | 190 |
{ "help", 0, 0, 'h' }, |
162 | 191 |
{ "version", 0, 0, 'V' }, |
163 | 192 |
{ "bind", 1, 0, 'b' }, |
164 | 193 |
{ "port", 1, 0, 'p' }, |
194 |
{ "socket", 1, 0, 'k' }, |
|
165 | 195 |
{ "offset", 1, 0, 'o' }, |
166 | 196 |
{ "read-only", 0, 0, 'r' }, |
167 | 197 |
{ "partition", 1, 0, 'P' }, |
198 |
{ "connect", 1, 0, 'c' }, |
|
199 |
{ "disconnect", 0, 0, 'd' }, |
|
168 | 200 |
{ "snapshot", 0, 0, 's' }, |
169 | 201 |
{ "verbose", 0, 0, 'v' }, |
170 | 202 |
{ NULL, 0, 0, 0 } |
... | ... | |
175 | 207 |
char *end; |
176 | 208 |
bool snapshot = false; |
177 | 209 |
int partition = -1; |
210 |
int fd; |
|
211 |
int ret; |
|
178 | 212 |
|
179 | 213 |
while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { |
180 | 214 |
switch (ch) { |
... | ... | |
213 | 247 |
if (partition < 1 || partition > 8) |
214 | 248 |
errx(EINVAL, "Invalid partition %d", partition); |
215 | 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; |
|
216 | 261 |
case 'v': |
217 | 262 |
verbose = 1; |
218 | 263 |
break; |
... | ... | |
236 | 281 |
argv[0]); |
237 | 282 |
} |
238 | 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 |
|
|
239 | 298 |
bdrv_init(); |
240 | 299 |
|
241 | 300 |
bs = bdrv_new("hda"); |
... | ... | |
251 | 310 |
find_partition(bs, partition, &dev_offset, &fd_size)) |
252 | 311 |
errx(errno, "Could not find partition %d", partition); |
253 | 312 |
|
254 |
sock = tcp_socket_incoming(bindto, port); |
|
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 |
|
|
255 | 384 |
if (sock == -1) |
256 | 385 |
return 1; |
257 | 386 |
|
... | ... | |
270 | 399 |
close(csock); |
271 | 400 |
close(sock); |
272 | 401 |
bdrv_close(bs); |
402 |
if (socket) |
|
403 |
unlink(socket); |
|
273 | 404 |
|
274 | 405 |
return 0; |
275 | 406 |
} |
b/qemu-nbd.texi | ||
---|---|---|
20 | 20 |
offset into the image |
21 | 21 |
@item -b, --bind=IFACE |
22 | 22 |
interface to bind to (default `0.0.0.0') |
23 |
@item -k, --socket=PATH |
|
24 |
Use a unix socket with path PATH |
|
23 | 25 |
@item -r, --read-only |
24 | 26 |
export read-only |
25 | 27 |
@item -P, --partition=NUM |
26 | 28 |
only expose partition NUM |
29 |
@item -c, --connect |
|
30 |
connect FILE to NBD device DEV |
|
31 |
@item -d, --disconnect |
|
32 |
disconnect the specified device |
|
27 | 33 |
@item -v, --verbose |
28 | 34 |
display extra debugging information |
29 | 35 |
@item -h, --help |
Also available in: Unified diff