diff --git a/Makefile.objs b/Makefile.objs index c05f5e59beaf8636d32ac351fc821b77024075dc..44ce368d05b04eff19e86019586b9c990ea5637c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -14,7 +14,7 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o # block-obj-y is code used by both qemu system emulation and qemu-img block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o async.o -block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o +block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o qemu-progress.o qemu-sockets.o block-obj-$(CONFIG_POSIX) += posix-aio-compat.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o @@ -94,7 +94,7 @@ common-obj-$(CONFIG_SSI_SD) += ssi-sd.o common-obj-$(CONFIG_SD) += sd.o common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o common-obj-y += bt-hci-csr.o -common-obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o +common-obj-y += buffered_file.o migration.o migration-tcp.o common-obj-y += qemu-char.o savevm.o #aio.o common-obj-y += msmouse.o ps2.o common-obj-y += qdev.o qdev-properties.o diff --git a/block-migration.c b/block-migration.c index 8218bac09c6cd30286a4ce5b826ac378700036da..576e55a6a3df0b9ec7924bcafce63fd84d6df56d 100644 --- a/block-migration.c +++ b/block-migration.c @@ -140,7 +140,7 @@ static inline void add_avg_read_time(int64_t time) static inline long double compute_read_bwidth(void) { assert(block_mig_state.total_time != 0); - return (block_mig_state.reads * BLOCK_SIZE)/ block_mig_state.total_time; + return (block_mig_state.reads / block_mig_state.total_time) * BLOCK_SIZE; } static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) diff --git a/block.c b/block.c index c8e2f97614a7217b7e1ae309bfc5ee1bf9714c49..f731c7afbfaf6467e4df66c1bb8b55aa69918c69 100644 --- a/block.c +++ b/block.c @@ -697,14 +697,22 @@ void bdrv_close_all(void) } } +/* make a BlockDriverState anonymous by removing from bdrv_state list. + Also, NULL terminate the device_name to prevent double remove */ +void bdrv_make_anon(BlockDriverState *bs) +{ + if (bs->device_name[0] != '\0') { + QTAILQ_REMOVE(&bdrv_states, bs, list); + } + bs->device_name[0] = '\0'; +} + void bdrv_delete(BlockDriverState *bs) { assert(!bs->peer); /* remove from list, if necessary */ - if (bs->device_name[0] != '\0') { - QTAILQ_REMOVE(&bdrv_states, bs, list); - } + bdrv_make_anon(bs); bdrv_close(bs); if (bs->file != NULL) { @@ -1153,14 +1161,12 @@ int64_t bdrv_getlength(BlockDriverState *bs) if (!drv) return -ENOMEDIUM; - /* Fixed size devices use the total_sectors value for speed instead of - issuing a length query (like lseek) on each call. Also, legacy block - drivers don't provide a bdrv_getlength function and must use - total_sectors. */ - if (!bs->growable || !drv->bdrv_getlength) { - return bs->total_sectors * BDRV_SECTOR_SIZE; + if (bs->growable || bs->removable) { + if (drv->bdrv_getlength) { + return drv->bdrv_getlength(bs); + } } - return drv->bdrv_getlength(bs); + return bs->total_sectors * BDRV_SECTOR_SIZE; } /* return 0 as number of sectors if no device present or error */ @@ -2809,6 +2815,8 @@ void bdrv_set_locked(BlockDriverState *bs, int locked) { BlockDriver *drv = bs->drv; + trace_bdrv_set_locked(bs, locked); + bs->locked = locked; if (drv && drv->bdrv_set_locked) { drv->bdrv_set_locked(bs, locked); diff --git a/block.h b/block.h index 5d78fc0ed7076f52e7d872f79aa012a8620d796a..52e9cad55c0e2ce50fdcfdfeca49f71cdd75a82c 100644 --- a/block.h +++ b/block.h @@ -66,6 +66,7 @@ int bdrv_create(BlockDriver *drv, const char* filename, QEMUOptionParameter *options); int bdrv_create_file(const char* filename, QEMUOptionParameter *options); BlockDriverState *bdrv_new(const char *device_name); +void bdrv_make_anon(BlockDriverState *bs); void bdrv_delete(BlockDriverState *bs); int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags); int bdrv_open(BlockDriverState *bs, const char *filename, int flags, diff --git a/block/nbd.c b/block/nbd.c index c8dc763c6bb4dd5d7f21258de1fac7edeeb9b54a..1d6b22561bea3079e68428879ebff7e043366f6c 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -29,96 +29,152 @@ #include "qemu-common.h" #include "nbd.h" #include "module.h" +#include "qemu_socket.h" #include #include #define EN_OPTSTR ":exportname=" +/* #define DEBUG_NBD */ + +#if defined(DEBUG_NBD) +#define logout(fmt, ...) \ + fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__) +#else +#define logout(fmt, ...) ((void)0) +#endif + typedef struct BDRVNBDState { int sock; off_t size; size_t blocksize; + char *export_name; /* An NBD server may export several devices */ + + /* If it begins with '/', this is a UNIX domain socket. Otherwise, + * it's a string of the form :port + */ + char *host_spec; } BDRVNBDState; -static int nbd_open(BlockDriverState *bs, const char* filename, int flags) +static int nbd_config(BDRVNBDState *s, const char *filename, int flags) { - BDRVNBDState *s = bs->opaque; - uint32_t nbdflags; - char *file; - char *name; - const char *host; + char *export_name; + const char *host_spec; const char *unixpath; - int sock; - off_t size; - size_t blocksize; - int ret; int err = -EINVAL; file = qemu_strdup(filename); - name = strstr(file, EN_OPTSTR); - if (name) { - if (name[strlen(EN_OPTSTR)] == 0) { + export_name = strstr(file, EN_OPTSTR); + if (export_name) { + if (export_name[strlen(EN_OPTSTR)] == 0) { goto out; } - name[0] = 0; - name += strlen(EN_OPTSTR); + export_name[0] = 0; /* truncate 'file' */ + export_name += strlen(EN_OPTSTR); + s->export_name = qemu_strdup(export_name); } - if (!strstart(file, "nbd:", &host)) { + /* extract the host_spec - fail if it's not nbd:... */ + if (!strstart(file, "nbd:", &host_spec)) { goto out; } - if (strstart(host, "unix:", &unixpath)) { - - if (unixpath[0] != '/') { + /* are we a UNIX or TCP socket? */ + if (strstart(host_spec, "unix:", &unixpath)) { + if (unixpath[0] != '/') { /* We demand an absolute path*/ goto out; } - - sock = unix_socket_outgoing(unixpath); - + s->host_spec = qemu_strdup(unixpath); } else { - uint16_t port = NBD_DEFAULT_PORT; - char *p, *r; - char hostname[128]; + s->host_spec = qemu_strdup(host_spec); + } - pstrcpy(hostname, 128, host); + err = 0; - p = strchr(hostname, ':'); - if (p != NULL) { - *p = '\0'; - p++; +out: + qemu_free(file); + if (err != 0) { + qemu_free(s->export_name); + qemu_free(s->host_spec); + } + return err; +} - port = strtol(p, &r, 0); - if (r == p) { - goto out; - } - } +static int nbd_establish_connection(BlockDriverState *bs) +{ + BDRVNBDState *s = bs->opaque; + int sock; + int ret; + off_t size; + size_t blocksize; + uint32_t nbdflags; - sock = tcp_socket_outgoing(hostname, port); + if (s->host_spec[0] == '/') { + sock = unix_socket_outgoing(s->host_spec); + } else { + sock = tcp_socket_outgoing_spec(s->host_spec); } + /* Failed to establish connection */ if (sock == -1) { - err = -errno; - goto out; + logout("Failed to establish connection to NBD server\n"); + return -errno; } - ret = nbd_receive_negotiate(sock, name, &nbdflags, &size, &blocksize); + /* NBD handshake */ + ret = nbd_receive_negotiate(sock, s->export_name, &nbdflags, &size, + &blocksize); if (ret == -1) { - err = -errno; - goto out; + logout("Failed to negotiate with the NBD server\n"); + closesocket(sock); + return -errno; } + /* Now that we're connected, set the socket to be non-blocking */ + socket_set_nonblock(sock); + s->sock = sock; s->size = size; s->blocksize = blocksize; - err = 0; -out: - qemu_free(file); - return err; + logout("Established connection with NBD server\n"); + return 0; +} + +static void nbd_teardown_connection(BlockDriverState *bs) +{ + BDRVNBDState *s = bs->opaque; + struct nbd_request request; + + request.type = NBD_CMD_DISC; + request.handle = (uint64_t)(intptr_t)bs; + request.from = 0; + request.len = 0; + nbd_send_request(s->sock, &request); + + closesocket(s->sock); +} + +static int nbd_open(BlockDriverState *bs, const char* filename, int flags) +{ + BDRVNBDState *s = bs->opaque; + int result; + + /* Pop the config into our state object. Exit if invalid. */ + result = nbd_config(s, filename, flags); + if (result != 0) { + return result; + } + + /* establish TCP connection, return error if it fails + * TODO: Configurable retry-until-timeout behaviour. + */ + result = nbd_establish_connection(bs); + + return result; } static int nbd_read(BlockDriverState *bs, int64_t sector_num, @@ -183,16 +239,7 @@ static int nbd_write(BlockDriverState *bs, int64_t sector_num, static void nbd_close(BlockDriverState *bs) { - BDRVNBDState *s = bs->opaque; - struct nbd_request request; - - request.type = NBD_CMD_DISC; - request.handle = (uint64_t)(intptr_t)bs; - request.from = 0; - request.len = 0; - nbd_send_request(s->sock, &request); - - close(s->sock); + nbd_teardown_connection(bs); } static int64_t nbd_getlength(BlockDriverState *bs) diff --git a/blockdev.c b/blockdev.c index bbe92fe19646745d375372db27c3784482362d03..5429621f0c29745509739f9b996a92e1d34754ef 100644 --- a/blockdev.c +++ b/blockdev.c @@ -737,8 +737,6 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *id = qdict_get_str(qdict, "id"); BlockDriverState *bs; - BlockDriverState **ptr; - Property *prop; bs = bdrv_find(id); if (!bs) { @@ -755,24 +753,17 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) bdrv_flush(bs); bdrv_close(bs); - /* clean up guest state from pointing to host resource by - * finding and removing DeviceState "drive" property */ + /* if we have a device associated with this BlockDriverState (bs->peer) + * then we need to make the drive anonymous until the device + * can be removed. If this is a drive with no device backing + * then we can just get rid of the block driver state right here. + */ if (bs->peer) { - for (prop = bs->peer->info->props; prop && prop->name; prop++) { - if (prop->info->type == PROP_TYPE_DRIVE) { - ptr = qdev_get_prop_ptr(bs->peer, prop); - if (*ptr == bs) { - bdrv_detach(bs, bs->peer); - *ptr = NULL; - break; - } - } - } + bdrv_make_anon(bs); + } else { + drive_uninit(drive_get_by_blockdev(bs)); } - /* clean up host side */ - drive_uninit(drive_get_by_blockdev(bs)); - return 0; } diff --git a/hw/fdc.c b/hw/fdc.c index 9fdbc750b501e1bf0ed407df1ab9b1b486c10969..edf0360d1bca32d9e66c636fe6acbbc1f537db6e 100644 --- a/hw/fdc.c +++ b/hw/fdc.c @@ -36,6 +36,7 @@ #include "qdev-addr.h" #include "blockdev.h" #include "sysemu.h" +#include "block_int.h" /********************************************************/ /* debug Floppy devices */ @@ -82,6 +83,7 @@ typedef struct FDrive { uint8_t max_track; /* Nb of tracks */ uint16_t bps; /* Bytes per sector */ uint8_t ro; /* Is read-only */ + uint8_t media_changed; /* Is media changed */ } FDrive; static void fd_init(FDrive *drv) @@ -533,16 +535,63 @@ static CPUWriteMemoryFunc * const fdctrl_mem_write_strict[3] = { NULL, }; +static void fdrive_media_changed_pre_save(void *opaque) +{ + FDrive *drive = opaque; + + drive->media_changed = drive->bs->media_changed; +} + +static int fdrive_media_changed_post_load(void *opaque, int version_id) +{ + FDrive *drive = opaque; + + if (drive->bs != NULL) { + drive->bs->media_changed = drive->media_changed; + } + + /* User ejected the floppy when drive->bs == NULL */ + return 0; +} + +static bool fdrive_media_changed_needed(void *opaque) +{ + FDrive *drive = opaque; + + return (drive->bs != NULL && drive->bs->media_changed != 1); +} + +static const VMStateDescription vmstate_fdrive_media_changed = { + .name = "fdrive/media_changed", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = fdrive_media_changed_pre_save, + .post_load = fdrive_media_changed_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(media_changed, FDrive), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_fdrive = { .name = "fdrive", .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, - .fields = (VMStateField []) { + .fields = (VMStateField[]) { VMSTATE_UINT8(head, FDrive), VMSTATE_UINT8(track, FDrive), VMSTATE_UINT8(sect, FDrive), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_fdrive_media_changed, + .needed = &fdrive_media_changed_needed, + } , { + /* empty */ + } } }; diff --git a/hw/ide.h b/hw/ide.h index 73fb550574b8106d63d6d7dcab9cc2234056d616..34d9394bcce3803398874f103c307920fa57848f 100644 --- a/hw/ide.h +++ b/hw/ide.h @@ -28,4 +28,7 @@ void mmio_ide_init (target_phys_addr_t membase, target_phys_addr_t membase2, void ide_get_bs(BlockDriverState *bs[], BusState *qbus); +/* ide/core.c */ +void ide_drive_get(DriveInfo **hd, int max_bus); + #endif /* HW_IDE_H */ diff --git a/hw/ide/core.c b/hw/ide/core.c index 007a4ee0c9cad3b6d728126b66f8dbf5b9dc6c95..c11d457b7ad976724dd595f5626e1a988abeea20 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2826,3 +2826,17 @@ const VMStateDescription vmstate_ide_bus = { VMSTATE_END_OF_LIST() } }; + +void ide_drive_get(DriveInfo **hd, int max_bus) +{ + int i; + + if (drive_get_max_bus(IF_IDE) >= max_bus) { + fprintf(stderr, "qemu: too many IDE bus: %d\n", max_bus); + exit(1); + } + + for(i = 0; i < max_bus * MAX_IDE_DEVS; i++) { + hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); + } +} diff --git a/hw/mips_fulong2e.c b/hw/mips_fulong2e.c index f5ae63980ccbe0d79649c7684bbacc287aaa996a..0e90d684b4d24538b958c8b27e9f0a98fb0954eb 100644 --- a/hw/mips_fulong2e.c +++ b/hw/mips_fulong2e.c @@ -338,14 +338,7 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device, pci_bus = bonito_init((qemu_irq *)&(env->irq[2])); /* South bridge */ - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } - - for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { - hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); - } + ide_drive_get(hd, MAX_IDE_BUS); via_devfn = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0)); if (via_devfn < 0) { diff --git a/hw/mips_malta.c b/hw/mips_malta.c index d8baa6d7e3ea751bc97f0b397624ce372fa1ca35..bf0d76d03d43fad645baf7f0338a462097a112fd 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -905,15 +905,7 @@ void mips_malta_init (ram_addr_t ram_size, pci_bus = gt64120_register(i8259); /* Southbridge */ - - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } - - for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { - hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); - } + ide_drive_get(hd, MAX_IDE_BUS); piix4_devfn = piix4_init(pci_bus, 80); isa_bus_irqs(i8259); diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c index 8feb46163faea44b2461edb5108ae77172abecb2..2834a46d52d8721a7615190093632ea674d859fe 100644 --- a/hw/mips_r4k.c +++ b/hw/mips_r4k.c @@ -287,15 +287,7 @@ void mips_r4k_init (ram_addr_t ram_size, if (nd_table[0].vlan) isa_ne2000_init(0x300, 9, &nd_table[0]); - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } - - for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { - hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); - } - + ide_drive_get(hd, MAX_IDE_BUS); for(i = 0; i < MAX_IDE_BUS; i++) isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[MAX_IDE_DEVS * i], diff --git a/hw/pc_piix.c b/hw/pc_piix.c index b3ede8941f3f5eb340c4c7f118526dea243729b2..4d54ca183285594c5ee3fd7423439ed42074505c 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -129,15 +129,7 @@ static void pc_init1(ram_addr_t ram_size, pci_nic_init_nofail(nd, "e1000", NULL); } - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } - - for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { - hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); - } - + ide_drive_get(hd, MAX_IDE_BUS); if (pci_enabled) { PCIDevice *dev; dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index b9245f066a12bb8ed4a37cc85a4ac462fd6981b6..86f1cfbee98c465f4a7edf350dd9487213495c15 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -325,20 +325,13 @@ static void ppc_core99_init (ram_addr_t ram_size, for(i = 0; i < nb_nics; i++) pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } + ide_drive_get(hd, MAX_IDE_BUS); dbdma = DBDMA_init(&dbdma_mem_index); /* We only emulate 2 out of 3 IDE controllers for now */ ide_mem_index[0] = -1; - hd[0] = drive_get(IF_IDE, 0, 0); - hd[1] = drive_get(IF_IDE, 0, 1); ide_mem_index[1] = pmac_ide_init(hd, pic[0x0d], dbdma, 0x16, pic[0x02]); - hd[0] = drive_get(IF_IDE, 1, 0); - hd[1] = drive_get(IF_IDE, 1, 1); - ide_mem_index[2] = pmac_ide_init(hd, pic[0x0e], dbdma, 0x1a, pic[0x02]); + ide_mem_index[2] = pmac_ide_init(&hd[MAX_IDE_DEVS], pic[0x0e], dbdma, 0x1a, pic[0x02]); /* cuda also initialize ADB */ if (machine_arch == ARCH_MAC99_U3) { diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index 8a4e088a384ff0560ea0922e5a998a7cb616be90..75a312742e3874c758021d25234eef947fa8bf6a 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -236,21 +236,16 @@ static void ppc_heathrow_init (ram_addr_t ram_size, pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } + ide_drive_get(hd, MAX_IDE_BUS); /* First IDE channel is a MAC IDE on the MacIO bus */ - hd[0] = drive_get(IF_IDE, 0, 0); - hd[1] = drive_get(IF_IDE, 0, 1); dbdma = DBDMA_init(&dbdma_mem_index); ide_mem_index[0] = -1; ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D], dbdma, 0x16, pic[0x02]); /* Second IDE channel is a CMD646 on the PCI bus */ - hd[0] = drive_get(IF_IDE, 1, 0); - hd[1] = drive_get(IF_IDE, 1, 1); + hd[0] = hd[MAX_IDE_DEVS]; + hd[1] = hd[MAX_IDE_DEVS + 1]; hd[3] = hd[2] = NULL; pci_cmd646_ide_init(pci_bus, hd, 0); diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index 5615ef9ad84e07ea99e69f30c7edb3bed42e82a9..0e9cfc24cda129e16f89f7f7709b2651b1b524d0 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -681,15 +681,7 @@ static void ppc_prep_init (ram_addr_t ram_size, } } - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } - - for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { - hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); - } - + ide_drive_get(hd, MAX_IDE_BUS); for(i = 0; i < MAX_IDE_BUS; i++) { isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[2 * i], diff --git a/hw/sun4u.c b/hw/sun4u.c index dbb5a15066572d7feb6adc18fcc8e5e8c1dec529..5eb38cf27dc5bbf806347d5418f3a9dc397d7b27 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -793,14 +793,7 @@ static void sun4uv_init(ram_addr_t RAM_size, for(i = 0; i < nb_nics; i++) pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } - for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { - hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, - i % MAX_IDE_DEVS); - } + ide_drive_get(hd, MAX_IDE_BUS); pci_cmd646_ide_init(pci_bus, hd, 1); diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index b14fb995e81994c54431599fd5fd2e32c71e11aa..91e0394af9f535a02cd6ead94f03324b4fa0409e 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -290,6 +290,10 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) virtio_blk_rw_complete(req, -EIO); return; } + if (req->qiov.size % req->dev->conf->logical_block_size) { + virtio_blk_rw_complete(req, -EIO); + return; + } if (mrb->num_writes == 32) { virtio_submit_multiwrite(req->dev->bs, mrb); @@ -317,6 +321,10 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req) virtio_blk_rw_complete(req, -EIO); return; } + if (req->qiov.size % req->dev->conf->logical_block_size) { + virtio_blk_rw_complete(req, -EIO); + return; + } acb = bdrv_aio_readv(req->dev->bs, sector, &req->qiov, req->qiov.size / BDRV_SECTOR_SIZE, diff --git a/hw/xen_disk.c b/hw/xen_disk.c index 445bf03aa0ef037f7509063a99054a636fc6a7e6..558bf8ae25d79f6d3bccbaffdf27b8c9f1525023 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -310,7 +310,7 @@ static int ioreq_runio_qemu_sync(struct ioreq *ioreq) off_t pos; if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) - goto err; + goto err_no_map; if (ioreq->presync) bdrv_flush(blkdev->bs); @@ -364,6 +364,9 @@ static int ioreq_runio_qemu_sync(struct ioreq *ioreq) return 0; err: + ioreq_unmap(ioreq); +err_no_map: + ioreq_finish(ioreq); ioreq->status = BLKIF_RSP_ERROR; return -1; } @@ -393,7 +396,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) struct XenBlkDev *blkdev = ioreq->blkdev; if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) - goto err; + goto err_no_map; ioreq->aio_inflight++; if (ioreq->presync) @@ -427,6 +430,9 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) return 0; err: + ioreq_unmap(ioreq); +err_no_map: + ioreq_finish(ioreq); ioreq->status = BLKIF_RSP_ERROR; return -1; } diff --git a/nbd.c b/nbd.c index d8ebc4298fc0f5bd85b7ea1ff7eafd1cdb723af8..0dcd86b52fba3d963a67ef873243c8b3c78d21d0 100644 --- a/nbd.c +++ b/nbd.c @@ -49,7 +49,7 @@ /* This is all part of the "official" NBD API */ -#define NBD_REPLY_SIZE (4 + 4 + 8) +#define NBD_REPLY_SIZE (4 + 4 + 8) #define NBD_REQUEST_MAGIC 0x25609513 #define NBD_REPLY_MAGIC 0x67446698 @@ -59,11 +59,11 @@ #define NBD_DO_IT _IO(0xab, 3) #define NBD_CLEAR_SOCK _IO(0xab, 4) #define NBD_CLEAR_QUE _IO(0xab, 5) -#define NBD_PRINT_DEBUG _IO(0xab, 6) -#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) +#define NBD_PRINT_DEBUG _IO(0xab, 6) +#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) #define NBD_DISCONNECT _IO(0xab, 8) -#define NBD_OPT_EXPORT_NAME (1 << 0) +#define NBD_OPT_EXPORT_NAME (1 << 0) /* That's all folks */ @@ -107,155 +107,55 @@ size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) return offset; } -int tcp_socket_outgoing(const char *address, uint16_t port) +static void combine_addr(char *buf, size_t len, const char* address, + uint16_t port) { - int s; - struct in_addr in; - struct sockaddr_in addr; - - s = socket(PF_INET, SOCK_STREAM, 0); - if (s == -1) { - return -1; - } - - if (inet_aton(address, &in) == 0) { - struct hostent *ent; - - ent = gethostbyname(address); - if (ent == NULL) { - goto error; - } - - memcpy(&in, ent->h_addr, sizeof(in)); - } - - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - memcpy(&addr.sin_addr.s_addr, &in, sizeof(in)); - - if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - goto error; + /* If the address-part contains a colon, it's an IPv6 IP so needs [] */ + if (strstr(address, ":")) { + snprintf(buf, len, "[%s]:%u", address, port); + } else { + snprintf(buf, len, "%s:%u", address, port); } - - return s; -error: - closesocket(s); - return -1; } -int tcp_socket_incoming(const char *address, uint16_t port) +int tcp_socket_outgoing(const char *address, uint16_t port) { - int s; - struct in_addr in; - struct sockaddr_in addr; - int opt; - - s = socket(PF_INET, SOCK_STREAM, 0); - if (s == -1) { - return -1; - } - - if (inet_aton(address, &in) == 0) { - struct hostent *ent; - - ent = gethostbyname(address); - if (ent == NULL) { - goto error; - } - - memcpy(&in, ent->h_addr, sizeof(in)); - } - - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - memcpy(&addr.sin_addr.s_addr, &in, sizeof(in)); - - opt = 1; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, - (const void *) &opt, sizeof(opt)) == -1) { - goto error; - } - - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - goto error; - } - - if (listen(s, 128) == -1) { - goto error; - } - - return s; -error: - closesocket(s); - return -1; + char address_and_port[128]; + combine_addr(address_and_port, 128, address, port); + return tcp_socket_outgoing_spec(address_and_port); } -#ifndef _WIN32 -int unix_socket_incoming(const char *path) +int tcp_socket_outgoing_spec(const char *address_and_port) { - int s; - struct sockaddr_un addr; - - s = socket(PF_UNIX, SOCK_STREAM, 0); - if (s == -1) { - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - pstrcpy(addr.sun_path, sizeof(addr.sun_path), path); - - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - goto error; - } - - if (listen(s, 128) == -1) { - goto error; - } - - return s; -error: - closesocket(s); - return -1; + return inet_connect(address_and_port, SOCK_STREAM); } -int unix_socket_outgoing(const char *path) +int tcp_socket_incoming(const char *address, uint16_t port) { - int s; - struct sockaddr_un addr; - - s = socket(PF_UNIX, SOCK_STREAM, 0); - if (s == -1) { - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - pstrcpy(addr.sun_path, sizeof(addr.sun_path), path); - - if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - goto error; - } + char address_and_port[128]; + combine_addr(address_and_port, 128, address, port); + return tcp_socket_incoming_spec(address_and_port); +} - return s; -error: - closesocket(s); - return -1; +int tcp_socket_incoming_spec(const char *address_and_port) +{ + char *ostr = NULL; + int olen = 0; + return inet_listen(address_and_port, ostr, olen, SOCK_STREAM, 0); } -#else + int unix_socket_incoming(const char *path) { - errno = ENOTSUP; - return -1; + char *ostr = NULL; + int olen = 0; + + return unix_listen(path, ostr, olen); } int unix_socket_outgoing(const char *path) { - errno = ENOTSUP; - return -1; + return unix_connect(path); } -#endif - /* Basic flow @@ -273,241 +173,241 @@ int unix_socket_outgoing(const char *path) int nbd_negotiate(int csock, off_t size) { - char buf[8 + 8 + 8 + 128]; - - /* Negotiate - [ 0 .. 7] passwd ("NBDMAGIC") - [ 8 .. 15] magic (0x00420281861253) - [16 .. 23] size - [24 .. 151] reserved (0) - */ - - TRACE("Beginning negotiation."); - memcpy(buf, "NBDMAGIC", 8); - cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL); - cpu_to_be64w((uint64_t*)(buf + 16), size); - memset(buf + 24, 0, 128); - - if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("write failed"); - errno = EINVAL; - return -1; - } - - TRACE("Negotation succeeded."); - - return 0; + char buf[8 + 8 + 8 + 128]; + + /* Negotiate + [ 0 .. 7] passwd ("NBDMAGIC") + [ 8 .. 15] magic (0x00420281861253) + [16 .. 23] size + [24 .. 151] reserved (0) + */ + + TRACE("Beginning negotiation."); + memcpy(buf, "NBDMAGIC", 8); + cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL); + cpu_to_be64w((uint64_t*)(buf + 16), size); + memset(buf + 24, 0, 128); + + if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("write failed"); + errno = EINVAL; + return -1; + } + + TRACE("Negotation succeeded."); + + return 0; } int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, off_t *size, size_t *blocksize) { - char buf[256]; - uint64_t magic, s; - uint16_t tmp; - - TRACE("Receiving negotation."); - - if (read_sync(csock, buf, 8) != 8) { - LOG("read failed"); - errno = EINVAL; - return -1; - } - - buf[8] = '\0'; - if (strlen(buf) == 0) { - LOG("server connection closed"); - errno = EINVAL; - return -1; - } - - TRACE("Magic is %c%c%c%c%c%c%c%c", - qemu_isprint(buf[0]) ? buf[0] : '.', - qemu_isprint(buf[1]) ? buf[1] : '.', - qemu_isprint(buf[2]) ? buf[2] : '.', - qemu_isprint(buf[3]) ? buf[3] : '.', - qemu_isprint(buf[4]) ? buf[4] : '.', - qemu_isprint(buf[5]) ? buf[5] : '.', - qemu_isprint(buf[6]) ? buf[6] : '.', - qemu_isprint(buf[7]) ? buf[7] : '.'); - - if (memcmp(buf, "NBDMAGIC", 8) != 0) { - LOG("Invalid magic received"); - errno = EINVAL; - return -1; - } - - if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { - LOG("read failed"); - errno = EINVAL; - return -1; - } - magic = be64_to_cpu(magic); - TRACE("Magic is 0x%" PRIx64, magic); - - if (name) { - uint32_t reserved = 0; - uint32_t opt; - uint32_t namesize; - - TRACE("Checking magic (opts_magic)"); - if (magic != 0x49484156454F5054LL) { - LOG("Bad magic received"); - errno = EINVAL; - return -1; - } - if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { - LOG("flags read failed"); - errno = EINVAL; - return -1; - } - *flags = be16_to_cpu(tmp) << 16; - /* reserved for future use */ - if (write_sync(csock, &reserved, sizeof(reserved)) != - sizeof(reserved)) { - LOG("write failed (reserved)"); - errno = EINVAL; - return -1; - } - /* write the export name */ - magic = cpu_to_be64(magic); - if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { - LOG("write failed (magic)"); - errno = EINVAL; - return -1; - } - opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); - if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { - LOG("write failed (opt)"); - errno = EINVAL; - return -1; - } - namesize = cpu_to_be32(strlen(name)); - if (write_sync(csock, &namesize, sizeof(namesize)) != - sizeof(namesize)) { - LOG("write failed (namesize)"); - errno = EINVAL; - return -1; - } - if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) { - LOG("write failed (name)"); - errno = EINVAL; - return -1; - } - } else { - TRACE("Checking magic (cli_magic)"); - - if (magic != 0x00420281861253LL) { - LOG("Bad magic received"); - errno = EINVAL; - return -1; - } - } - - if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) { - LOG("read failed"); - errno = EINVAL; - return -1; - } - *size = be64_to_cpu(s); - *blocksize = 1024; - TRACE("Size is %" PRIu64, *size); - - if (!name) { - if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) { - LOG("read failed (flags)"); - errno = EINVAL; - return -1; - } - *flags = be32_to_cpup(flags); - } else { - if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { - LOG("read failed (tmp)"); - errno = EINVAL; - return -1; - } - *flags |= be32_to_cpu(tmp); - } - if (read_sync(csock, &buf, 124) != 124) { - LOG("read failed (buf)"); - errno = EINVAL; - return -1; - } + char buf[256]; + uint64_t magic, s; + uint16_t tmp; + + TRACE("Receiving negotation."); + + if (read_sync(csock, buf, 8) != 8) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + + buf[8] = '\0'; + if (strlen(buf) == 0) { + LOG("server connection closed"); + errno = EINVAL; + return -1; + } + + TRACE("Magic is %c%c%c%c%c%c%c%c", + qemu_isprint(buf[0]) ? buf[0] : '.', + qemu_isprint(buf[1]) ? buf[1] : '.', + qemu_isprint(buf[2]) ? buf[2] : '.', + qemu_isprint(buf[3]) ? buf[3] : '.', + qemu_isprint(buf[4]) ? buf[4] : '.', + qemu_isprint(buf[5]) ? buf[5] : '.', + qemu_isprint(buf[6]) ? buf[6] : '.', + qemu_isprint(buf[7]) ? buf[7] : '.'); + + if (memcmp(buf, "NBDMAGIC", 8) != 0) { + LOG("Invalid magic received"); + errno = EINVAL; + return -1; + } + + if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + magic = be64_to_cpu(magic); + TRACE("Magic is 0x%" PRIx64, magic); + + if (name) { + uint32_t reserved = 0; + uint32_t opt; + uint32_t namesize; + + TRACE("Checking magic (opts_magic)"); + if (magic != 0x49484156454F5054LL) { + LOG("Bad magic received"); + errno = EINVAL; + return -1; + } + if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { + LOG("flags read failed"); + errno = EINVAL; + return -1; + } + *flags = be16_to_cpu(tmp) << 16; + /* reserved for future use */ + if (write_sync(csock, &reserved, sizeof(reserved)) != + sizeof(reserved)) { + LOG("write failed (reserved)"); + errno = EINVAL; + return -1; + } + /* write the export name */ + magic = cpu_to_be64(magic); + if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { + LOG("write failed (magic)"); + errno = EINVAL; + return -1; + } + opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); + if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { + LOG("write failed (opt)"); + errno = EINVAL; + return -1; + } + namesize = cpu_to_be32(strlen(name)); + if (write_sync(csock, &namesize, sizeof(namesize)) != + sizeof(namesize)) { + LOG("write failed (namesize)"); + errno = EINVAL; + return -1; + } + if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) { + LOG("write failed (name)"); + errno = EINVAL; + return -1; + } + } else { + TRACE("Checking magic (cli_magic)"); + + if (magic != 0x00420281861253LL) { + LOG("Bad magic received"); + errno = EINVAL; + return -1; + } + } + + if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + *size = be64_to_cpu(s); + *blocksize = 1024; + TRACE("Size is %" PRIu64, *size); + + if (!name) { + if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) { + LOG("read failed (flags)"); + errno = EINVAL; + return -1; + } + *flags = be32_to_cpup(flags); + } else { + if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { + LOG("read failed (tmp)"); + errno = EINVAL; + return -1; + } + *flags |= be32_to_cpu(tmp); + } + if (read_sync(csock, &buf, 124) != 124) { + LOG("read failed (buf)"); + errno = EINVAL; + return -1; + } return 0; } #ifndef _WIN32 int nbd_init(int fd, int csock, off_t size, size_t blocksize) { - TRACE("Setting block size to %lu", (unsigned long)blocksize); + TRACE("Setting block size to %lu", (unsigned long)blocksize); - if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) { - int serrno = errno; - LOG("Failed setting NBD block size"); - errno = serrno; - return -1; - } + if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) { + int serrno = errno; + LOG("Failed setting NBD block size"); + errno = serrno; + return -1; + } TRACE("Setting size to %zd block(s)", (size_t)(size / blocksize)); - if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) { - int serrno = errno; - LOG("Failed setting size (in blocks)"); - errno = serrno; - return -1; - } + if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) { + int serrno = errno; + LOG("Failed setting size (in blocks)"); + errno = serrno; + return -1; + } - TRACE("Clearing NBD socket"); + TRACE("Clearing NBD socket"); - if (ioctl(fd, NBD_CLEAR_SOCK) == -1) { - int serrno = errno; - LOG("Failed clearing NBD socket"); - errno = serrno; - return -1; - } + if (ioctl(fd, NBD_CLEAR_SOCK) == -1) { + int serrno = errno; + LOG("Failed clearing NBD socket"); + errno = serrno; + return -1; + } - TRACE("Setting NBD socket"); + TRACE("Setting NBD socket"); - if (ioctl(fd, NBD_SET_SOCK, csock) == -1) { - int serrno = errno; - LOG("Failed to set NBD socket"); - errno = serrno; - return -1; - } + if (ioctl(fd, NBD_SET_SOCK, csock) == -1) { + int serrno = errno; + LOG("Failed to set NBD socket"); + errno = serrno; + return -1; + } - TRACE("Negotiation ended"); + TRACE("Negotiation ended"); - return 0; + return 0; } int nbd_disconnect(int fd) { - ioctl(fd, NBD_CLEAR_QUE); - ioctl(fd, NBD_DISCONNECT); - ioctl(fd, NBD_CLEAR_SOCK); - return 0; + ioctl(fd, NBD_CLEAR_QUE); + ioctl(fd, NBD_DISCONNECT); + ioctl(fd, NBD_CLEAR_SOCK); + return 0; } int nbd_client(int fd) { - int ret; - int serrno; + int ret; + int serrno; - TRACE("Doing NBD loop"); + TRACE("Doing NBD loop"); - ret = ioctl(fd, NBD_DO_IT); - serrno = errno; + ret = ioctl(fd, NBD_DO_IT); + serrno = errno; - TRACE("NBD loop returned %d: %s", ret, strerror(serrno)); + TRACE("NBD loop returned %d: %s", ret, strerror(serrno)); - TRACE("Clearing NBD queue"); - ioctl(fd, NBD_CLEAR_QUE); + TRACE("Clearing NBD queue"); + ioctl(fd, NBD_CLEAR_QUE); - TRACE("Clearing NBD socket"); - ioctl(fd, NBD_CLEAR_SOCK); + TRACE("Clearing NBD socket"); + ioctl(fd, NBD_CLEAR_SOCK); - errno = serrno; - return ret; + errno = serrno; + return ret; } #else int nbd_init(int fd, int csock, off_t size, size_t blocksize) @@ -531,235 +431,236 @@ int nbd_client(int fd) int nbd_send_request(int csock, struct nbd_request *request) { - uint8_t buf[4 + 4 + 8 + 8 + 4]; - - cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); - cpu_to_be32w((uint32_t*)(buf + 4), request->type); - cpu_to_be64w((uint64_t*)(buf + 8), request->handle); - cpu_to_be64w((uint64_t*)(buf + 16), request->from); - cpu_to_be32w((uint32_t*)(buf + 24), request->len); - - TRACE("Sending request to client"); - - if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("writing to socket failed"); - errno = EINVAL; - return -1; - } - return 0; -} + uint8_t buf[4 + 4 + 8 + 8 + 4]; + cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); + cpu_to_be32w((uint32_t*)(buf + 4), request->type); + cpu_to_be64w((uint64_t*)(buf + 8), request->handle); + cpu_to_be64w((uint64_t*)(buf + 16), request->from); + cpu_to_be32w((uint32_t*)(buf + 24), request->len); + + TRACE("Sending request to client: " + "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}", + request->from, request->len, request->handle, request->type); + + if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("writing to socket failed"); + errno = EINVAL; + return -1; + } + return 0; +} static int nbd_receive_request(int csock, struct nbd_request *request) { - uint8_t buf[4 + 4 + 8 + 8 + 4]; - uint32_t magic; - - if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("read failed"); - errno = EINVAL; - return -1; - } - - /* Request - [ 0 .. 3] magic (NBD_REQUEST_MAGIC) - [ 4 .. 7] type (0 == READ, 1 == WRITE) - [ 8 .. 15] handle - [16 .. 23] from - [24 .. 27] len - */ - - magic = be32_to_cpup((uint32_t*)buf); - request->type = be32_to_cpup((uint32_t*)(buf + 4)); - request->handle = be64_to_cpup((uint64_t*)(buf + 8)); - request->from = be64_to_cpup((uint64_t*)(buf + 16)); - request->len = be32_to_cpup((uint32_t*)(buf + 24)); - - TRACE("Got request: " - "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }", - magic, request->type, request->from, request->len); - - if (magic != NBD_REQUEST_MAGIC) { - LOG("invalid magic (got 0x%x)", magic); - errno = EINVAL; - return -1; - } - return 0; + uint8_t buf[4 + 4 + 8 + 8 + 4]; + uint32_t magic; + + if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + + /* Request + [ 0 .. 3] magic (NBD_REQUEST_MAGIC) + [ 4 .. 7] type (0 == READ, 1 == WRITE) + [ 8 .. 15] handle + [16 .. 23] from + [24 .. 27] len + */ + + magic = be32_to_cpup((uint32_t*)buf); + request->type = be32_to_cpup((uint32_t*)(buf + 4)); + request->handle = be64_to_cpup((uint64_t*)(buf + 8)); + request->from = be64_to_cpup((uint64_t*)(buf + 16)); + request->len = be32_to_cpup((uint32_t*)(buf + 24)); + + TRACE("Got request: " + "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }", + magic, request->type, request->from, request->len); + + if (magic != NBD_REQUEST_MAGIC) { + LOG("invalid magic (got 0x%x)", magic); + errno = EINVAL; + return -1; + } + return 0; } int nbd_receive_reply(int csock, struct nbd_reply *reply) { - uint8_t buf[NBD_REPLY_SIZE]; - uint32_t magic; - - memset(buf, 0xAA, sizeof(buf)); - - if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("read failed"); - errno = EINVAL; - return -1; - } - - /* Reply - [ 0 .. 3] magic (NBD_REPLY_MAGIC) - [ 4 .. 7] error (0 == no error) - [ 7 .. 15] handle - */ - - magic = be32_to_cpup((uint32_t*)buf); - reply->error = be32_to_cpup((uint32_t*)(buf + 4)); - reply->handle = be64_to_cpup((uint64_t*)(buf + 8)); - - TRACE("Got reply: " - "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }", - magic, reply->error, reply->handle); - - if (magic != NBD_REPLY_MAGIC) { - LOG("invalid magic (got 0x%x)", magic); - errno = EINVAL; - return -1; - } - return 0; + uint8_t buf[NBD_REPLY_SIZE]; + uint32_t magic; + + memset(buf, 0xAA, sizeof(buf)); + + if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + + /* Reply + [ 0 .. 3] magic (NBD_REPLY_MAGIC) + [ 4 .. 7] error (0 == no error) + [ 7 .. 15] handle + */ + + magic = be32_to_cpup((uint32_t*)buf); + reply->error = be32_to_cpup((uint32_t*)(buf + 4)); + reply->handle = be64_to_cpup((uint64_t*)(buf + 8)); + + TRACE("Got reply: " + "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }", + magic, reply->error, reply->handle); + + if (magic != NBD_REPLY_MAGIC) { + LOG("invalid magic (got 0x%x)", magic); + errno = EINVAL; + return -1; + } + return 0; } static int nbd_send_reply(int csock, struct nbd_reply *reply) { - uint8_t buf[4 + 4 + 8]; - - /* Reply - [ 0 .. 3] magic (NBD_REPLY_MAGIC) - [ 4 .. 7] error (0 == no error) - [ 7 .. 15] handle - */ - cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC); - cpu_to_be32w((uint32_t*)(buf + 4), reply->error); - cpu_to_be64w((uint64_t*)(buf + 8), reply->handle); - - TRACE("Sending response to client"); - - if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("writing to socket failed"); - errno = EINVAL; - return -1; - } - return 0; + uint8_t buf[4 + 4 + 8]; + + /* Reply + [ 0 .. 3] magic (NBD_REPLY_MAGIC) + [ 4 .. 7] error (0 == no error) + [ 7 .. 15] handle + */ + cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC); + cpu_to_be32w((uint32_t*)(buf + 4), reply->error); + cpu_to_be64w((uint64_t*)(buf + 8), reply->handle); + + TRACE("Sending response to client"); + + if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("writing to socket failed"); + errno = EINVAL; + return -1; + } + return 0; } int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, off_t *offset, bool readonly, uint8_t *data, int data_size) { - struct nbd_request request; - struct nbd_reply reply; - - TRACE("Reading request."); - - if (nbd_receive_request(csock, &request) == -1) - return -1; - - if (request.len + NBD_REPLY_SIZE > data_size) { - LOG("len (%u) is larger than max len (%u)", - request.len + NBD_REPLY_SIZE, data_size); - errno = EINVAL; - return -1; - } - - if ((request.from + request.len) < request.from) { - LOG("integer overflow detected! " - "you're probably being attacked"); - errno = EINVAL; - return -1; - } - - if ((request.from + request.len) > size) { - LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 - ", Offset: %" PRIu64 "\n", + struct nbd_request request; + struct nbd_reply reply; + + TRACE("Reading request."); + + if (nbd_receive_request(csock, &request) == -1) + return -1; + + if (request.len + NBD_REPLY_SIZE > data_size) { + LOG("len (%u) is larger than max len (%u)", + request.len + NBD_REPLY_SIZE, data_size); + errno = EINVAL; + return -1; + } + + if ((request.from + request.len) < request.from) { + LOG("integer overflow detected! " + "you're probably being attacked"); + errno = EINVAL; + return -1; + } + + if ((request.from + request.len) > size) { + LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 + ", Offset: %" PRIu64 "\n", request.from, request.len, (uint64_t)size, dev_offset); - LOG("requested operation past EOF--bad client?"); - errno = EINVAL; - return -1; - } - - TRACE("Decoding type"); - - reply.handle = request.handle; - reply.error = 0; - - switch (request.type) { - case NBD_CMD_READ: - TRACE("Request type is READ"); - - if (bdrv_read(bs, (request.from + dev_offset) / 512, - data + NBD_REPLY_SIZE, - request.len / 512) == -1) { - LOG("reading from file failed"); - errno = EINVAL; - return -1; - } - *offset += request.len; - - TRACE("Read %u byte(s)", request.len); - - /* Reply - [ 0 .. 3] magic (NBD_REPLY_MAGIC) - [ 4 .. 7] error (0 == no error) - [ 7 .. 15] handle - */ - - cpu_to_be32w((uint32_t*)data, NBD_REPLY_MAGIC); - cpu_to_be32w((uint32_t*)(data + 4), reply.error); - cpu_to_be64w((uint64_t*)(data + 8), reply.handle); - - TRACE("Sending data to client"); - - if (write_sync(csock, data, - request.len + NBD_REPLY_SIZE) != - request.len + NBD_REPLY_SIZE) { - LOG("writing to socket failed"); - errno = EINVAL; - return -1; - } - break; - case NBD_CMD_WRITE: - TRACE("Request type is WRITE"); - - TRACE("Reading %u byte(s)", request.len); - - if (read_sync(csock, data, request.len) != request.len) { - LOG("reading from socket failed"); - errno = EINVAL; - return -1; - } - - if (readonly) { - TRACE("Server is read-only, return error"); - reply.error = 1; - } else { - TRACE("Writing to device"); - - if (bdrv_write(bs, (request.from + dev_offset) / 512, - data, request.len / 512) == -1) { - LOG("writing to file failed"); - errno = EINVAL; - return -1; - } - - *offset += request.len; - } - - if (nbd_send_reply(csock, &reply) == -1) - return -1; - break; - case NBD_CMD_DISC: - TRACE("Request type is DISCONNECT"); - errno = 0; - return 1; - default: - LOG("invalid request type (%u) received", request.type); - errno = EINVAL; - return -1; - } - - TRACE("Request/Reply complete"); - - return 0; + LOG("requested operation past EOF--bad client?"); + errno = EINVAL; + return -1; + } + + TRACE("Decoding type"); + + reply.handle = request.handle; + reply.error = 0; + + switch (request.type) { + case NBD_CMD_READ: + TRACE("Request type is READ"); + + if (bdrv_read(bs, (request.from + dev_offset) / 512, + data + NBD_REPLY_SIZE, + request.len / 512) == -1) { + LOG("reading from file failed"); + errno = EINVAL; + return -1; + } + *offset += request.len; + + TRACE("Read %u byte(s)", request.len); + + /* Reply + [ 0 .. 3] magic (NBD_REPLY_MAGIC) + [ 4 .. 7] error (0 == no error) + [ 7 .. 15] handle + */ + + cpu_to_be32w((uint32_t*)data, NBD_REPLY_MAGIC); + cpu_to_be32w((uint32_t*)(data + 4), reply.error); + cpu_to_be64w((uint64_t*)(data + 8), reply.handle); + + TRACE("Sending data to client"); + + if (write_sync(csock, data, + request.len + NBD_REPLY_SIZE) != + request.len + NBD_REPLY_SIZE) { + LOG("writing to socket failed"); + errno = EINVAL; + return -1; + } + break; + case NBD_CMD_WRITE: + TRACE("Request type is WRITE"); + + TRACE("Reading %u byte(s)", request.len); + + if (read_sync(csock, data, request.len) != request.len) { + LOG("reading from socket failed"); + errno = EINVAL; + return -1; + } + + if (readonly) { + TRACE("Server is read-only, return error"); + reply.error = 1; + } else { + TRACE("Writing to device"); + + if (bdrv_write(bs, (request.from + dev_offset) / 512, + data, request.len / 512) == -1) { + LOG("writing to file failed"); + errno = EINVAL; + return -1; + } + + *offset += request.len; + } + + if (nbd_send_reply(csock, &reply) == -1) + return -1; + break; + case NBD_CMD_DISC: + TRACE("Request type is DISCONNECT"); + errno = 0; + return 1; + default: + LOG("invalid request type (%u) received", request.type); + errno = EINVAL; + return -1; + } + + TRACE("Request/Reply complete"); + + return 0; } diff --git a/nbd.h b/nbd.h index fc3a5944f4a6b10fad89dd0c5e59b7b5135cf639..b38d0d08de2d80bdf545251f8b02921234b973c5 100644 --- a/nbd.h +++ b/nbd.h @@ -22,19 +22,22 @@ #include #include + #include "block_int.h" struct nbd_request { + uint32_t magic; uint32_t type; uint64_t handle; uint64_t from; uint32_t len; -}; +} __attribute__ ((__packed__)); struct nbd_reply { + uint32_t magic; uint32_t error; uint64_t handle; -}; +} __attribute__ ((__packed__)); enum { NBD_CMD_READ = 0, @@ -47,6 +50,8 @@ enum { size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); int tcp_socket_outgoing(const char *address, uint16_t port); int tcp_socket_incoming(const char *address, uint16_t port); +int tcp_socket_outgoing_spec(const char *address_and_port); +int tcp_socket_incoming_spec(const char *address_and_port); int unix_socket_outgoing(const char *path); int unix_socket_incoming(const char *path); diff --git a/qemu-common.h b/qemu-common.h index 8ecb48820a734fb94bd70a9429784aaacd858dde..82e27c18d315217dfeac3db25a3e13f49591839f 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -334,6 +334,10 @@ void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count); void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count, size_t skip); +void qemu_progress_init(int enabled, float min_skip); +void qemu_progress_end(void); +void qemu_progress_print(float percent, int max); + /* Convert a byte between binary and BCD. */ static inline uint8_t to_bcd(uint8_t val) { diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 6c7176f8b4643fe120f9abe4bf0258a41d558f05..3072d383cfc51a9a526d38463ce58fe531705be3 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -28,7 +28,7 @@ STEXI ETEXI DEF("convert", img_convert, - "convert [-c] [-f fmt] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename") + "convert [-c] [-p] [-f fmt] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename") STEXI @item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI @@ -46,7 +46,7 @@ STEXI ETEXI DEF("rebase", img_rebase, - "rebase [-f fmt] [-u] -b backing_file [-F backing_fmt] filename") + "rebase [-f fmt] [-p] [-u] -b backing_file [-F backing_fmt] filename") STEXI @item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} ETEXI diff --git a/qemu-img.c b/qemu-img.c index 7e3cc4cbd5cc57086703227a4121af11db722e48..d9c2c12fa0522cbe5ed950c8cff5a00ddf7bfe72 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -77,6 +77,7 @@ static void help(void) " match exactly. The image doesn't need a working backing file before\n" " rebasing in this case (useful for renaming the backing file)\n" " '-h' with or without a command shows this help and lists the supported formats\n" + " '-p' show progress of command (only certain commands)\n" "\n" "Parameters to snapshot subcommand:\n" " 'snapshot' is the name of the snapshot to create, apply or delete\n" @@ -567,6 +568,7 @@ static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n, static int img_convert(int argc, char **argv) { int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors; + int progress = 0; const char *fmt, *out_fmt, *out_baseimg, *out_filename; BlockDriver *drv, *proto_drv; BlockDriverState **bs = NULL, *out_bs = NULL; @@ -579,13 +581,14 @@ static int img_convert(int argc, char **argv) QEMUOptionParameter *out_baseimg_param; char *options = NULL; const char *snapshot_name = NULL; + float local_progress; fmt = NULL; out_fmt = "raw"; out_baseimg = NULL; compress = 0; for(;;) { - c = getopt(argc, argv, "f:O:B:s:hce6o:"); + c = getopt(argc, argv, "f:O:B:s:hce6o:p"); if (c == -1) { break; } @@ -620,6 +623,9 @@ static int img_convert(int argc, char **argv) case 's': snapshot_name = optarg; break; + case 'p': + progress = 1; + break; } } @@ -642,6 +648,9 @@ static int img_convert(int argc, char **argv) goto out; } + qemu_progress_init(progress, 2.0); + qemu_progress_print(0, 100); + bs = qemu_mallocz(bs_n * sizeof(BlockDriverState *)); total_sectors = 0; @@ -773,6 +782,11 @@ static int img_convert(int argc, char **argv) } cluster_sectors = cluster_size >> 9; sector_num = 0; + + nb_sectors = total_sectors; + local_progress = (float)100 / + (nb_sectors / MIN(nb_sectors, (cluster_sectors))); + for(;;) { int64_t bs_num; int remainder; @@ -832,6 +846,7 @@ static int img_convert(int argc, char **argv) } } sector_num += n; + qemu_progress_print(local_progress, 100); } /* signal EOF to align */ bdrv_write_compressed(out_bs, 0, NULL, 0); @@ -839,6 +854,10 @@ static int img_convert(int argc, char **argv) int has_zero_init = bdrv_has_zero_init(out_bs); sector_num = 0; // total number of sectors converted so far + nb_sectors = total_sectors - sector_num; + local_progress = (float)100 / + (nb_sectors / MIN(nb_sectors, (IO_BUF_SIZE / 512))); + for(;;) { nb_sectors = total_sectors - sector_num; if (nb_sectors <= 0) { @@ -912,9 +931,11 @@ static int img_convert(int argc, char **argv) n -= n1; buf1 += n1 * 512; } + qemu_progress_print(local_progress, 100); } } out: + qemu_progress_end(); free_option_parameters(create_options); free_option_parameters(param); qemu_free(buf); @@ -1184,6 +1205,7 @@ static int img_rebase(int argc, char **argv) const char *fmt, *out_basefmt, *out_baseimg; int c, flags, ret; int unsafe = 0; + int progress = 0; /* Parse commandline parameters */ fmt = NULL; @@ -1191,7 +1213,7 @@ static int img_rebase(int argc, char **argv) out_basefmt = NULL; for(;;) { - c = getopt(argc, argv, "uhf:F:b:"); + c = getopt(argc, argv, "uhf:F:b:p"); if (c == -1) { break; } @@ -1212,6 +1234,9 @@ static int img_rebase(int argc, char **argv) case 'u': unsafe = 1; break; + case 'p': + progress = 1; + break; } } @@ -1220,6 +1245,9 @@ static int img_rebase(int argc, char **argv) } filename = argv[optind++]; + qemu_progress_init(progress, 2.0); + qemu_progress_print(0, 100); + /* * Open the images. * @@ -1295,12 +1323,15 @@ static int img_rebase(int argc, char **argv) int n; uint8_t * buf_old; uint8_t * buf_new; + float local_progress; buf_old = qemu_malloc(IO_BUF_SIZE); buf_new = qemu_malloc(IO_BUF_SIZE); bdrv_get_geometry(bs, &num_sectors); + local_progress = (float)100 / + (num_sectors / MIN(num_sectors, (IO_BUF_SIZE / 512))); for (sector = 0; sector < num_sectors; sector += n) { /* How many sectors can we handle with the next read? */ @@ -1348,6 +1379,7 @@ static int img_rebase(int argc, char **argv) written += pnum; } + qemu_progress_print(local_progress, 100); } qemu_free(buf_old); @@ -1368,6 +1400,7 @@ static int img_rebase(int argc, char **argv) out_baseimg, strerror(-ret)); } + qemu_progress_print(100, 0); /* * TODO At this point it is possible to check if any clusters that are * allocated in the COW file are the same in the backing file. If so, they @@ -1375,10 +1408,15 @@ static int img_rebase(int argc, char **argv) * backing file, in case of a crash this would lead to corruption. */ out: + qemu_progress_end(); /* Cleanup */ if (!unsafe) { - bdrv_delete(bs_old_backing); - bdrv_delete(bs_new_backing); + if (bs_old_backing != NULL) { + bdrv_delete(bs_old_backing); + } + if (bs_new_backing != NULL) { + bdrv_delete(bs_new_backing); + } } bdrv_delete(bs); diff --git a/qemu-progress.c b/qemu-progress.c new file mode 100644 index 0000000000000000000000000000000000000000..656e065b1de8e76c8e869716657929bce2161f90 --- /dev/null +++ b/qemu-progress.c @@ -0,0 +1,89 @@ +/* + * QEMU progress printing utility functions + * + * Copyright (C) 2011 Jes Sorensen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "osdep.h" +#include "sysemu.h" +#include + +struct progress_state { + int enabled; + float current; + float last_print; + float min_skip; +}; + +static struct progress_state state; + +/* + * Simple progress print function. + * @percent relative percent of current operation + * @max percent of total operation + */ +static void progress_simple_print(void) +{ + if (state.enabled) { + printf(" (%3.2f/100%%)\r", state.current); + fflush(stdout); + } +} + +static void progress_simple_end(void) +{ + if (state.enabled) { + printf("\n"); + } +} + +void qemu_progress_init(int enabled, float min_skip) +{ + state.enabled = enabled; + state.min_skip = min_skip; +} + +void qemu_progress_end(void) +{ + progress_simple_end(); +} + +void qemu_progress_print(float percent, int max) +{ + float current; + + if (max == 0) { + current = percent; + } else { + current = state.current + percent / 100 * max; + } + if (current > 100) { + current = 100; + } + state.current = current; + + if (current > (state.last_print + state.min_skip) || + (current == 100) || (current == 0)) { + state.last_print = state.current; + progress_simple_print(); + } +} diff --git a/qemu-sockets.c b/qemu-sockets.c index c526324998b07047daec34b209707e2f817d3935..eda1850e9c6a02a1ae52af465c78fc31bf7e9573 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -627,24 +627,28 @@ int unix_connect(const char *path) int unix_listen_opts(QemuOpts *opts) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } int unix_connect_opts(QemuOpts *opts) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } int unix_listen(const char *path, char *ostr, int olen) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } int unix_connect(const char *path) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } diff --git a/trace-events b/trace-events index 06efdb7753eba170836bb94e47206e3ffad5ea81..703b745bc47ff1ad9d8bd8d5dc3b47f43b686049 100644 --- a/trace-events +++ b/trace-events @@ -54,6 +54,7 @@ disable bdrv_aio_multiwrite_latefail(void *mcb, int i) "mcb %p i %d" disable bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p" disable bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" disable bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" +disable bdrv_set_locked(void *bs, int locked) "bs %p locked %d" # hw/virtio-blk.c disable virtio_blk_req_complete(void *req, int status) "req %p status %d" diff --git a/vl.c b/vl.c index de232b75e6981931c316aec74970e1725c636346..68c3b532bce593cd1f4f452fd1b3077a6c2f2cb9 100644 --- a/vl.c +++ b/vl.c @@ -2102,7 +2102,9 @@ int main(int argc, char **argv, char **envp) HD_OPTS); break; case QEMU_OPTION_drive: - drive_def(optarg); + if (drive_def(optarg) == NULL) { + exit(1); + } break; case QEMU_OPTION_set: if (qemu_set_option(optarg) != 0)