/* Send an option request. * * The request is for option @opt, with @data containing @len bytes of * additional payload for the request (@len may be -1 to treat @data as * a C string; and @data may be NULL if @len is 0). * Return 0 if successful, -1 with errp set if it is impossible to * continue. */ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt, uint32_t len, const char *data, Error **errp) { nbd_option req; QEMU_BUILD_BUG_ON(sizeof(req) != 16); if (len == -1) { req.length = len = strlen(data); } trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); stq_be_p(&req.magic, NBD_OPTS_MAGIC); stl_be_p(&req.option, opt); stl_be_p(&req.length, len); if (nbd_write(ioc, &req, sizeof(req), errp) < 0) { error_prepend(errp, "Failed to send option request header"); return -1; } if (len && nbd_write(ioc, (char *) data, len, errp) < 0) { error_prepend(errp, "Failed to send option request data"); return -1; } return 0; }
/* Receive the header of an option reply, which should match the given * opt. Read through the length field, but NOT the length bytes of * payload. Return 0 if successful, -1 with errp set if it is * impossible to continue. */ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, nbd_opt_reply *reply, Error **errp) { QEMU_BUILD_BUG_ON(sizeof(*reply) != 20); if (nbd_read(ioc, reply, sizeof(*reply), errp) < 0) { error_prepend(errp, "failed to read option reply"); nbd_send_opt_abort(ioc); return -1; } be64_to_cpus(&reply->magic); be32_to_cpus(&reply->option); be32_to_cpus(&reply->type); be32_to_cpus(&reply->length); trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option), reply->type, nbd_rep_lookup(reply->type), reply->length); if (reply->magic != NBD_REP_MAGIC) { error_setg(errp, "Unexpected option reply magic"); nbd_send_opt_abort(ioc); return -1; } if (reply->option != opt) { error_setg(errp, "Unexpected option type %x expected %x", reply->option, opt); nbd_send_opt_abort(ioc); return -1; } return 0; }
static void vhost_scsi_realize(DeviceState *dev, Error **errp) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); VHostSCSI *s = VHOST_SCSI(dev); Error *err = NULL; int vhostfd = -1; int ret; if (!vs->conf.wwpn) { error_setg(errp, "vhost-scsi: missing wwpn"); return; } if (vs->conf.vhostfd) { vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp); if (vhostfd == -1) { error_prepend(errp, "vhost-scsi: unable to parse vhostfd: "); return; } } else { vhostfd = open("/dev/vhost-scsi", O_RDWR); if (vhostfd < 0) { error_setg(errp, "vhost-scsi: open vhost char device failed: %s", strerror(errno)); return; } } virtio_scsi_common_realize(dev, &err, vhost_dummy_handle_output, vhost_dummy_handle_output, vhost_dummy_handle_output); if (err != NULL) { error_propagate(errp, err); close(vhostfd); return; } s->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues; s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs); s->dev.vq_index = 0; s->dev.backend_features = 0; ret = vhost_dev_init(&s->dev, (void *)(uintptr_t)vhostfd, VHOST_BACKEND_TYPE_KERNEL); if (ret < 0) { error_setg(errp, "vhost-scsi: vhost initialization failed: %s", strerror(-ret)); return; } /* At present, channel and lun both are 0 for bootable vhost-scsi disk */ s->channel = 0; s->lun = 0; /* Note: we can also get the minimum tpgt from kernel */ s->target = vs->conf.boot_tpgt; error_setg(&s->migration_blocker, "vhost-scsi does not support migration"); migrate_add_blocker(s->migration_blocker); }
static void spapr_irq_init_xics(sPAPRMachineState *spapr, Error **errp) { MachineState *machine = MACHINE(spapr); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); int nr_irqs = smc->irq->nr_irqs; Error *local_err = NULL; if (kvm_enabled()) { if (machine_kernel_irqchip_allowed(machine) && !xics_kvm_init(spapr, &local_err)) { spapr->icp_type = TYPE_KVM_ICP; spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, &local_err); } if (machine_kernel_irqchip_required(machine) && !spapr->ics) { error_prepend(&local_err, "kernel_irqchip requested but unavailable: "); goto error; } error_free(local_err); local_err = NULL; } if (!spapr->ics) { xics_spapr_init(spapr); spapr->icp_type = TYPE_ICP; spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, &local_err); } error: error_propagate(errp, local_err); }
/* Context: QEMU global mutex held */ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, VirtIOBlockDataPlane **dataplane, Error **errp) { VirtIOBlockDataPlane *s; BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); *dataplane = NULL; if (!conf->iothread) { return; } /* Don't try if transport does not support notifiers. */ if (!k->set_guest_notifiers || !k->set_host_notifier) { error_setg(errp, "device is incompatible with dataplane " "(transport does not support notifiers)"); return; } /* If dataplane is (re-)enabled while the guest is running there could be * block jobs that can conflict. */ if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { error_prepend(errp, "cannot start dataplane thread: "); return; } s = g_new0(VirtIOBlockDataPlane, 1); s->vdev = vdev; s->conf = conf; if (conf->iothread) { s->iothread = conf->iothread; object_ref(OBJECT(s->iothread)); } s->ctx = iothread_get_aio_context(s->iothread); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); error_setg(&s->blocker, "block device is in use by data plane"); blk_op_block_all(conf->conf.blk, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); *dataplane = s; }
static void check_compatibility(const S390CPUModel *max_model, const S390CPUModel *model, Error **errp) { S390FeatBitmap missing; if (model->def->gen > max_model->def->gen) { error_setg(errp, "Selected CPU generation is too new. Maximum " "supported model in the configuration: \'%s\'", max_model->def->name); return; } else if (model->def->gen == max_model->def->gen && model->def->ec_ga > max_model->def->ec_ga) { error_setg(errp, "Selected CPU GA level is too new. Maximum " "supported model in the configuration: \'%s\'", max_model->def->name); return; } /* detect the missing features to properly report them */ bitmap_andnot(missing, model->features, max_model->features, S390_FEAT_MAX); if (bitmap_empty(missing, S390_FEAT_MAX)) { return; } error_setg(errp, " "); s390_feat_bitmap_to_ascii(missing, errp, error_prepend_missing_feat); error_prepend(errp, "Some features requested in the CPU model are not " "available in the configuration: "); }
int audio_write(struct mad_pcm *pcm, error_t *error) { if (!audio_initialized) { audio.channels = pcm->channels; audio.samplerate = pcm->samplerate; if (!audio_init(error)) { error_prepend(error, "Could not initialize audio"); return 0; } } if ((audio.channels != pcm->channels) || (audio.samplerate != pcm->samplerate)) { /* XXX */ error_set(error, "Changing the audio parameters is not supported"); return 0; } if (pcm->length != 1152) { error_printf(error, "Unknown number of samples in the mad buffer: %d", pcm->length); return 0; } if (pcm->channels != 2) { error_set(error, "Only stereo PCM data supported"); return 0; } int ret; float buf[1152 * pcm->channels]; float *ptr = buf; int i; mad_fixed_t const *left_ch, *right_ch; left_ch = pcm->samples[0]; right_ch = pcm->samples[1]; for (i = 0; i < pcm->length; i++) { signed int sample; *ptr++ = mad_scale(*left_ch++) / 32768.0; *ptr++ = mad_scale(*right_ch++) / 32768.0; } ret = rb_enqueue(&audio.rb, buf, 1152 * pcm->channels); if (ret == 0) { error_set(error, "Could not enqueue the PCM samples"); return 0; } if (!audio_started) { ret = AudioDeviceStart(audio.device, audio_play_proc); if (ret) { error_set(error, "Could not start the audio playback"); return 0; } audio_started = 1; } return 1; }
/* Context: QEMU global mutex held */ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, VirtIOBlockDataPlane **dataplane, Error **errp) { VirtIOBlockDataPlane *s; BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); *dataplane = NULL; if (!conf->iothread) { return; } /* Don't try if transport does not support notifiers. */ if (!k->set_guest_notifiers || !k->set_host_notifier) { error_setg(errp, "device is incompatible with dataplane " "(transport does not support notifiers)"); return; } /* If dataplane is (re-)enabled while the guest is running there could be * block jobs that can conflict. */ if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { error_prepend(errp, "cannot start dataplane thread: "); return; } s = g_new0(VirtIOBlockDataPlane, 1); s->vdev = vdev; s->conf = conf; if (conf->iothread) { s->iothread = conf->iothread; object_ref(OBJECT(s->iothread)); } s->ctx = iothread_get_aio_context(s->iothread); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); s->insert_notifier.notify = data_plane_blk_insert_notifier; s->remove_notifier.notify = data_plane_blk_remove_notifier; blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier); blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier); data_plane_set_up_op_blockers(s); *dataplane = s; }
void s390_realize_cpu_model(CPUState *cs, Error **errp) { S390CPUClass *xcc = S390_CPU_GET_CLASS(cs); S390CPU *cpu = S390_CPU(cs); const S390CPUModel *max_model; if (xcc->kvm_required && !kvm_enabled()) { error_setg(errp, "CPU definition requires KVM"); return; } if (!cpu->model) { /* no host model support -> perform compatibility stuff */ apply_cpu_model(NULL, errp); return; } max_model = get_max_cpu_model(errp); if (*errp) { error_prepend(errp, "CPU models are not available: "); return; } /* copy over properties that can vary */ cpu->model->lowest_ibc = max_model->lowest_ibc; cpu->model->cpu_id = max_model->cpu_id; cpu->model->cpu_id_format = max_model->cpu_id_format; cpu->model->cpu_ver = max_model->cpu_ver; check_consistency(cpu->model); check_compatibility(max_model, cpu->model, errp); if (*errp) { return; } apply_cpu_model(cpu->model, errp); cpu->env.cpuid = s390_cpuid_from_cpu_model(cpu->model); if (tcg_enabled()) { /* basic mode, write the cpu address into the first 4 bit of the ID */ cpu->env.cpuid = deposit64(cpu->env.cpuid, 54, 4, cpu->env.cpu_num); } }
static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { DeviceState *dev = DEVICE(obj); Error *local_err = NULL; Property *prop = opaque; CharBackend *be = qdev_get_prop_ptr(dev, prop); CharDriverState *s; char *str; if (dev->realized) { qdev_prop_set_after_realize(dev, name, errp); return; } visit_type_str(v, name, &str, &local_err); if (local_err) { error_propagate(errp, local_err); return; } if (!*str) { g_free(str); be->chr = NULL; return; } s = qemu_chr_find(str); if (s == NULL) { error_setg(errp, "Property '%s.%s' can't find value '%s'", object_get_typename(obj), prop->name, str); } else if (!qemu_chr_fe_init(be, s, errp)) { error_prepend(errp, "Property '%s.%s' can't take value '%s': ", object_get_typename(obj), prop->name, str); } g_free(str); }
static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) { ptm_res res; Error *err = NULL; int fds[2] = { -1, -1 }; if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { error_report("tpm-emulator: Failed to create socketpair"); return -1; } qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0, sizeof(res)) < 0 || res != 0) { error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", strerror(errno)); goto err_exit; } tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); if (err) { error_prepend(&err, "tpm-emulator: Failed to create io channel: "); error_report_err(err); goto err_exit; } closesocket(fds[1]); return 0; err_exit: closesocket(fds[0]); closesocket(fds[1]); return -1; }
void s390_realize_cpu_model(CPUState *cs, Error **errp) { S390CPUClass *xcc = S390_CPU_GET_CLASS(cs); S390CPU *cpu = S390_CPU(cs); const S390CPUModel *max_model; if (xcc->kvm_required && !kvm_enabled()) { error_setg(errp, "CPU definition requires KVM"); return; } if (!cpu->model) { /* no host model support -> perform compatibility stuff */ apply_cpu_model(NULL, errp); return; } max_model = get_max_cpu_model(errp); if (*errp) { error_prepend(errp, "CPU models are not available: "); return; } /* copy over properties that can vary */ cpu->model->lowest_ibc = max_model->lowest_ibc; cpu->model->cpu_id = max_model->cpu_id; cpu->model->cpu_ver = max_model->cpu_ver; check_consistency(cpu->model); check_compatibility(max_model, cpu->model, errp); if (*errp) { return; } apply_cpu_model(cpu->model, errp); }
static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostVSock *vsock = VHOST_VSOCK(dev); int vhostfd; int ret; /* Refuse to use reserved CID numbers */ if (vsock->conf.guest_cid <= 2) { error_setg(errp, "guest-cid property must be greater than 2"); return; } if (vsock->conf.guest_cid > UINT32_MAX) { error_setg(errp, "guest-cid property must be a 32-bit number"); return; } if (vsock->conf.vhostfd) { vhostfd = monitor_fd_param(cur_mon, vsock->conf.vhostfd, errp); if (vhostfd == -1) { error_prepend(errp, "vhost-vsock: unable to parse vhostfd: "); return; } } else { vhostfd = open("/dev/vhost-vsock", O_RDWR); if (vhostfd < 0) { error_setg_errno(errp, -errno, "vhost-vsock: failed to open vhost device"); return; } } virtio_init(vdev, "vhost-vsock", VIRTIO_ID_VSOCK, sizeof(struct virtio_vsock_config)); /* Receive and transmit queues belong to vhost */ virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output); virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output); /* The event queue belongs to QEMU */ vsock->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output); vsock->vhost_dev.nvqs = ARRAY_SIZE(vsock->vhost_vqs); vsock->vhost_dev.vqs = vsock->vhost_vqs; ret = vhost_dev_init(&vsock->vhost_dev, (void *)(uintptr_t)vhostfd, VHOST_BACKEND_TYPE_KERNEL, 0); if (ret < 0) { error_setg_errno(errp, -ret, "vhost-vsock: vhost_dev_init failed"); goto err_virtio; } ret = vhost_vsock_set_guest_cid(vsock); if (ret < 0) { error_setg_errno(errp, -ret, "vhost-vsock: unable to set guest cid"); goto err_vhost_dev; } vsock->post_load_timer = NULL; return; err_vhost_dev: vhost_dev_cleanup(&vsock->vhost_dev); err_virtio: virtio_cleanup(vdev); close(vhostfd); return; }
static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) { const char *value; value = qemu_opt_get(opts, "chardev"); if (value) { Error *err = NULL; Chardev *dev = qemu_chr_find(value); if (!dev) { error_report("tpm-emulator: tpm chardev '%s' not found.", value); goto err; } if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", value); error_report_err(err); goto err; } tpm_emu->options->chardev = g_strdup(value); } if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { goto err; } /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used * by passthrough driver, which not yet using GIOChannel. */ if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, &tpm_emu->tpm_version)) { error_report("'%s' is not emulating TPM device. Error: %s", tpm_emu->options->chardev, strerror(errno)); goto err; } switch (tpm_emu->tpm_version) { case TPM_VERSION_1_2: trace_tpm_emulator_handle_device_opts_tpm12(); break; case TPM_VERSION_2_0: trace_tpm_emulator_handle_device_opts_tpm2(); break; default: trace_tpm_emulator_handle_device_opts_unspec(); } if (tpm_emulator_probe_caps(tpm_emu) || tpm_emulator_check_caps(tpm_emu)) { goto err; } return tpm_emulator_block_migration(tpm_emu); err: trace_tpm_emulator_handle_device_opts_startup_error(); return -1; }
static void error_prepend_missing_feat(const char *name, void *opaque) { error_prepend((Error **) opaque, "%s ", name); }
/* If reply represents success, return 1 without further action. * If reply represents an error, consume the optional payload of * the packet on ioc. Then return 0 for unsupported (so the client * can fall back to other approaches), or -1 with errp set for other * errors. */ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply, Error **errp) { char *msg = NULL; int result = -1; if (!(reply->type & (1 << 31))) { return 1; } if (reply->length) { if (reply->length > NBD_MAX_BUFFER_SIZE) { error_setg(errp, "server error 0x%" PRIx32 " (%s) message is too long", reply->type, nbd_rep_lookup(reply->type)); goto cleanup; } msg = g_malloc(reply->length + 1); if (nbd_read(ioc, msg, reply->length, errp) < 0) { error_prepend(errp, "failed to read option error 0x%" PRIx32 " (%s) message", reply->type, nbd_rep_lookup(reply->type)); goto cleanup; } msg[reply->length] = '\0'; } switch (reply->type) { case NBD_REP_ERR_UNSUP: trace_nbd_reply_err_unsup(reply->option, nbd_opt_lookup(reply->option)); result = 0; goto cleanup; case NBD_REP_ERR_POLICY: error_setg(errp, "Denied by server for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_INVALID: error_setg(errp, "Invalid data length for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_PLATFORM: error_setg(errp, "Server lacks support for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_TLS_REQD: error_setg(errp, "TLS negotiation required before option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_UNKNOWN: error_setg(errp, "Requested export not available"); break; case NBD_REP_ERR_SHUTDOWN: error_setg(errp, "Server shutting down before option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_BLOCK_SIZE_REQD: error_setg(errp, "Server requires INFO_BLOCK_SIZE for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; default: error_setg(errp, "Unknown error code when asking for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; } if (msg) { error_append_hint(errp, "server reported: %s\n", msg); } cleanup: g_free(msg); if (result < 0) { nbd_send_opt_abort(ioc); } return result; }
int nbd_receive_negotiate(QIOChannel *ioc, const char *name, QCryptoTLSCreds *tlscreds, const char *hostname, QIOChannel **outioc, NBDExportInfo *info, Error **errp) { char buf[256]; uint64_t magic; int rc; bool zeroes = true; trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : "<null>"); rc = -EINVAL; if (outioc) { *outioc = NULL; } if (tlscreds && !outioc) { error_setg(errp, "Output I/O channel required for TLS"); goto fail; } if (nbd_read(ioc, buf, 8, errp) < 0) { error_prepend(errp, "Failed to read data"); goto fail; } buf[8] = '\0'; if (strlen(buf) == 0) { error_setg(errp, "Server connection closed unexpectedly"); goto fail; } magic = ldq_be_p(buf); trace_nbd_receive_negotiate_magic(magic); if (memcmp(buf, "NBDMAGIC", 8) != 0) { error_setg(errp, "Invalid magic received"); goto fail; } if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) { error_prepend(errp, "Failed to read magic"); goto fail; } magic = be64_to_cpu(magic); trace_nbd_receive_negotiate_magic(magic); if (magic == NBD_OPTS_MAGIC) { uint32_t clientflags = 0; uint16_t globalflags; bool fixedNewStyle = false; if (nbd_read(ioc, &globalflags, sizeof(globalflags), errp) < 0) { error_prepend(errp, "Failed to read server flags"); goto fail; } globalflags = be16_to_cpu(globalflags); trace_nbd_receive_negotiate_server_flags(globalflags); if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) { fixedNewStyle = true; clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; } if (globalflags & NBD_FLAG_NO_ZEROES) { zeroes = false; clientflags |= NBD_FLAG_C_NO_ZEROES; } /* client requested flags */ clientflags = cpu_to_be32(clientflags); if (nbd_write(ioc, &clientflags, sizeof(clientflags), errp) < 0) { error_prepend(errp, "Failed to send clientflags field"); goto fail; } if (tlscreds) { if (fixedNewStyle) { *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp); if (!*outioc) { goto fail; } ioc = *outioc; } else { error_setg(errp, "Server does not support STARTTLS"); goto fail; } } if (!name) { trace_nbd_receive_negotiate_default_name(); name = ""; } if (fixedNewStyle) { int result; /* Try NBD_OPT_GO first - if it works, we are done (it * also gives us a good message if the server requires * TLS). If it is not available, fall back to * NBD_OPT_LIST for nicer error messages about a missing * export, then use NBD_OPT_EXPORT_NAME. */ result = nbd_opt_go(ioc, name, info, errp); if (result < 0) { goto fail; } if (result > 0) { return 0; } /* Check our desired export is present in the * server export list. Since NBD_OPT_EXPORT_NAME * cannot return an error message, running this * query gives us better error reporting if the * export name is not available. */ if (nbd_receive_query_exports(ioc, name, errp) < 0) { goto fail; } } /* write the export name request */ if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, name, errp) < 0) { goto fail; } /* Read the response */ if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { error_prepend(errp, "Failed to read export length"); goto fail; } be64_to_cpus(&info->size); if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { error_prepend(errp, "Failed to read export flags"); goto fail; } be16_to_cpus(&info->flags); } else if (magic == NBD_CLIENT_MAGIC) { uint32_t oldflags; if (name) { error_setg(errp, "Server does not support export names"); goto fail; } if (tlscreds) { error_setg(errp, "Server does not support STARTTLS"); goto fail; } if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { error_prepend(errp, "Failed to read export length"); goto fail; } be64_to_cpus(&info->size); if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) { error_prepend(errp, "Failed to read export flags"); goto fail; } be32_to_cpus(&oldflags); if (oldflags & ~0xffff) { error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); goto fail; } info->flags = oldflags; } else { error_setg(errp, "Bad magic received"); goto fail; } trace_nbd_receive_negotiate_size_flags(info->size, info->flags); if (zeroes && nbd_drop(ioc, 124, errp) < 0) { error_prepend(errp, "Failed to read reserved block"); goto fail; } rc = 0; fail: return rc; }
/* Returns -1 if NBD_OPT_GO proves the export @wantname cannot be * used, 0 if NBD_OPT_GO is unsupported (fall back to NBD_OPT_LIST and * NBD_OPT_EXPORT_NAME in that case), and > 0 if the export is good to * go (with @info populated). */ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, NBDExportInfo *info, Error **errp) { nbd_opt_reply reply; uint32_t len = strlen(wantname); uint16_t type; int error; char *buf; /* The protocol requires that the server send NBD_INFO_EXPORT with * a non-zero flags (at least NBD_FLAG_HAS_FLAGS must be set); so * flags still 0 is a witness of a broken server. */ info->flags = 0; trace_nbd_opt_go_start(wantname); buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1); stl_be_p(buf, len); memcpy(buf + 4, wantname, len); /* At most one request, everything else up to server */ stw_be_p(buf + 4 + len, info->request_sizes); if (info->request_sizes) { stw_be_p(buf + 4 + len + 2, NBD_INFO_BLOCK_SIZE); } error = nbd_send_option_request(ioc, NBD_OPT_GO, 4 + len + 2 + 2 * info->request_sizes, buf, errp); g_free(buf); if (error < 0) { return -1; } while (1) { if (nbd_receive_option_reply(ioc, NBD_OPT_GO, &reply, errp) < 0) { return -1; } error = nbd_handle_reply_err(ioc, &reply, errp); if (error <= 0) { return error; } len = reply.length; if (reply.type == NBD_REP_ACK) { /* Server is done sending info and moved into transmission phase, but make sure it sent flags */ if (len) { error_setg(errp, "server sent invalid NBD_REP_ACK"); return -1; } if (!info->flags) { error_setg(errp, "broken server omitted NBD_INFO_EXPORT"); return -1; } trace_nbd_opt_go_success(); return 1; } if (reply.type != NBD_REP_INFO) { error_setg(errp, "unexpected reply type %" PRIx32 " (%s), expected %x", reply.type, nbd_rep_lookup(reply.type), NBD_REP_INFO); nbd_send_opt_abort(ioc); return -1; } if (len < sizeof(type)) { error_setg(errp, "NBD_REP_INFO length %" PRIu32 " is too short", len); nbd_send_opt_abort(ioc); return -1; } if (nbd_read(ioc, &type, sizeof(type), errp) < 0) { error_prepend(errp, "failed to read info type"); nbd_send_opt_abort(ioc); return -1; } len -= sizeof(type); be16_to_cpus(&type); switch (type) { case NBD_INFO_EXPORT: if (len != sizeof(info->size) + sizeof(info->flags)) { error_setg(errp, "remaining export info len %" PRIu32 " is unexpected size", len); nbd_send_opt_abort(ioc); return -1; } if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { error_prepend(errp, "failed to read info size"); nbd_send_opt_abort(ioc); return -1; } be64_to_cpus(&info->size); if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { error_prepend(errp, "failed to read info flags"); nbd_send_opt_abort(ioc); return -1; } be16_to_cpus(&info->flags); trace_nbd_receive_negotiate_size_flags(info->size, info->flags); break; case NBD_INFO_BLOCK_SIZE: if (len != sizeof(info->min_block) * 3) { error_setg(errp, "remaining export info len %" PRIu32 " is unexpected size", len); nbd_send_opt_abort(ioc); return -1; } if (nbd_read(ioc, &info->min_block, sizeof(info->min_block), errp) < 0) { error_prepend(errp, "failed to read info minimum block size"); nbd_send_opt_abort(ioc); return -1; } be32_to_cpus(&info->min_block); if (!is_power_of_2(info->min_block)) { error_setg(errp, "server minimum block size %" PRId32 "is not a power of two", info->min_block); nbd_send_opt_abort(ioc); return -1; } if (nbd_read(ioc, &info->opt_block, sizeof(info->opt_block), errp) < 0) { error_prepend(errp, "failed to read info preferred block size"); nbd_send_opt_abort(ioc); return -1; } be32_to_cpus(&info->opt_block); if (!is_power_of_2(info->opt_block) || info->opt_block < info->min_block) { error_setg(errp, "server preferred block size %" PRId32 "is not valid", info->opt_block); nbd_send_opt_abort(ioc); return -1; } if (nbd_read(ioc, &info->max_block, sizeof(info->max_block), errp) < 0) { error_prepend(errp, "failed to read info maximum block size"); nbd_send_opt_abort(ioc); return -1; } be32_to_cpus(&info->max_block); trace_nbd_opt_go_info_block_size(info->min_block, info->opt_block, info->max_block); break; default: trace_nbd_opt_go_info_unknown(type, nbd_info_lookup(type)); if (nbd_drop(ioc, len, errp) < 0) { error_prepend(errp, "Failed to read info payload"); nbd_send_opt_abort(ioc); return -1; } break; } } }
/* Process another portion of the NBD_OPT_LIST reply. Set *@match if * the current reply matches @want or if the server does not support * NBD_OPT_LIST, otherwise leave @match alone. Return 0 if iteration * is complete, positive if more replies are expected, or negative * with @errp set if an unrecoverable error occurred. */ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, Error **errp) { nbd_opt_reply reply; uint32_t len; uint32_t namelen; char name[NBD_MAX_NAME_SIZE + 1]; int error; if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) { return -1; } error = nbd_handle_reply_err(ioc, &reply, errp); if (error <= 0) { /* The server did not support NBD_OPT_LIST, so set *match on * the assumption that any name will be accepted. */ *match = true; return error; } len = reply.length; if (reply.type == NBD_REP_ACK) { if (len != 0) { error_setg(errp, "length too long for option end"); nbd_send_opt_abort(ioc); return -1; } return 0; } else if (reply.type != NBD_REP_SERVER) { error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x", reply.type, NBD_REP_SERVER); nbd_send_opt_abort(ioc); return -1; } if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) { error_setg(errp, "incorrect option length %" PRIu32, len); nbd_send_opt_abort(ioc); return -1; } if (nbd_read(ioc, &namelen, sizeof(namelen), errp) < 0) { error_prepend(errp, "failed to read option name length"); nbd_send_opt_abort(ioc); return -1; } namelen = be32_to_cpu(namelen); len -= sizeof(namelen); if (len < namelen) { error_setg(errp, "incorrect option name length"); nbd_send_opt_abort(ioc); return -1; } if (namelen != strlen(want)) { if (nbd_drop(ioc, len, errp) < 0) { error_prepend(errp, "failed to skip export name with wrong length"); nbd_send_opt_abort(ioc); return -1; } return 1; } assert(namelen < sizeof(name)); if (nbd_read(ioc, name, namelen, errp) < 0) { error_prepend(errp, "failed to read export name"); nbd_send_opt_abort(ioc); return -1; } name[namelen] = '\0'; len -= namelen; if (nbd_drop(ioc, len, errp) < 0) { error_prepend(errp, "failed to read export description"); nbd_send_opt_abort(ioc); return -1; } if (!strcmp(name, want)) { *match = true; } return 1; }