}
qdict_put(options, "path", qstring_from_str(qp->p[0].value));
} else {
- /* nbd[+tcp]://host:port/export */
- char *port_str;
-
+ QString *host;
+ /* nbd[+tcp]://host[:port]/export */
if (!uri->server) {
ret = -EINVAL;
goto out;
}
- if (!uri->port) {
- uri->port = NBD_DEFAULT_PORT;
+
+ /* strip braces from literal IPv6 address */
+ if (uri->server[0] == '[') {
+ host = qstring_from_substr(uri->server, 1,
+ strlen(uri->server) - 2);
+ } else {
+ host = qstring_from_str(uri->server);
}
- port_str = g_strdup_printf("%d", uri->port);
- qdict_put(options, "host", qstring_from_str(uri->server));
- qdict_put(options, "port", qstring_from_str(port_str));
- g_free(port_str);
+ qdict_put(options, "host", host);
+ if (uri->port) {
+ char* port_str = g_strdup_printf("%d", uri->port);
+ qdict_put(options, "port", qstring_from_str(port_str));
+ g_free(port_str);
+ }
}
out:
return ret;
}
-static int nbd_parse_filename(const char *filename, QDict *options)
+static void nbd_parse_filename(const char *filename, QDict *options,
+ Error **errp)
{
char *file;
char *export_name;
const char *host_spec;
const char *unixpath;
- int ret = -EINVAL;
- Error *local_err = NULL;
+
+ if (qdict_haskey(options, "host")
+ || qdict_haskey(options, "port")
+ || qdict_haskey(options, "path"))
+ {
+ error_setg(errp, "host/port/path and a file name may not be specified "
+ "at the same time");
+ return;
+ }
if (strstr(filename, "://")) {
- return nbd_parse_uri(filename, options);
+ int ret = nbd_parse_uri(filename, options);
+ if (ret < 0) {
+ error_setg(errp, "No valid URL specified");
+ }
+ return;
}
file = g_strdup(filename);
/* extract the host_spec - fail if it's not nbd:... */
if (!strstart(file, "nbd:", &host_spec)) {
+ error_setg(errp, "File name string for NBD must start with 'nbd:'");
goto out;
}
if (!*host_spec) {
- ret = 1;
goto out;
}
} else {
InetSocketAddress *addr = NULL;
- addr = inet_parse(host_spec, &local_err);
- if (local_err != NULL) {
- qerror_report_err(local_err);
- error_free(local_err);
+ addr = inet_parse(host_spec, errp);
+ if (error_is_set(errp)) {
goto out;
}
qapi_free_InetSocketAddress(addr);
}
- ret = 1;
out:
g_free(file);
- return ret;
}
static int nbd_config(BDRVNBDState *s, QDict *options)
Error *local_err = NULL;
if (qdict_haskey(options, "path")) {
+ if (qdict_haskey(options, "host")) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR, "path and host may not "
+ "be used at the same time.");
+ return -EINVAL;
+ }
s->is_unix = true;
} else if (qdict_haskey(options, "host")) {
s->is_unix = false;
return -EINVAL;
}
+ if (!qemu_opt_get(s->socket_opts, "port")) {
+ qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT);
+ }
+
s->export_name = g_strdup(qdict_get_try_str(options, "export"));
if (s->export_name) {
qdict_del(options, "export");
request->handle = INDEX_TO_HANDLE(s, i);
}
-static int nbd_have_request(void *opaque)
-{
- BDRVNBDState *s = opaque;
-
- return s->in_flight > 0;
-}
-
static void nbd_reply_ready(void *opaque)
{
BDRVNBDState *s = opaque;
qemu_co_mutex_lock(&s->send_mutex);
s->send_coroutine = qemu_coroutine_self();
- qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write,
- nbd_have_request, s);
- rc = nbd_send_request(s->sock, request);
- if (rc >= 0 && qiov) {
- ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
- offset, request->len);
- if (ret != request->len) {
- return -EIO;
+ qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write, s);
+ if (qiov) {
+ if (!s->is_unix) {
+ socket_set_cork(s->sock, 1);
+ }
+ rc = nbd_send_request(s->sock, request);
+ if (rc >= 0) {
+ ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
+ offset, request->len);
+ if (ret != request->len) {
+ rc = -EIO;
+ }
}
+ if (!s->is_unix) {
+ socket_set_cork(s->sock, 0);
+ }
+ } else {
+ rc = nbd_send_request(s->sock, request);
}
- qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL,
- nbd_have_request, s);
+ qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, s);
s->send_coroutine = NULL;
qemu_co_mutex_unlock(&s->send_mutex);
return rc;
sock = unix_socket_outgoing(qemu_opt_get(s->socket_opts, "path"));
} else {
sock = tcp_socket_outgoing_opts(s->socket_opts);
+ if (sock >= 0) {
+ socket_set_nodelay(sock);
+ }
}
/* Failed to establish connection */
/* Now that we're connected, set the socket to be non-blocking and
* kick the reply mechanism. */
- socket_set_nonblock(sock);
- qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL,
- nbd_have_request, s);
+ qemu_set_nonblock(sock);
+ qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL, s);
s->sock = sock;
s->size = size;
request.len = 0;
nbd_send_request(s->sock, &request);
- qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL);
+ qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL);
closesocket(s->sock);
}
-static int nbd_open(BlockDriverState *bs, const char* filename,
- QDict *options, int flags)
+static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
+ Error **errp)
{
BDRVNBDState *s = bs->opaque;
int result;
qemu_co_mutex_init(&s->free_sema);
/* Pop the config into our state object. Exit if invalid. */
- result = nbd_parse_filename(filename, options);
- if (result < 0) {
- return result;
- }
-
result = nbd_config(s, options);
if (result != 0) {
return result;
return 0;
}
request.type = NBD_CMD_TRIM;
- request.from = sector_num * 512;;
+ request.from = sector_num * 512;
request.len = nb_sectors * 512;
nbd_coroutine_start(s, &request);
.format_name = "nbd",
.protocol_name = "nbd",
.instance_size = sizeof(BDRVNBDState),
+ .bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv,
.bdrv_co_writev = nbd_co_writev,
.format_name = "nbd",
.protocol_name = "nbd+tcp",
.instance_size = sizeof(BDRVNBDState),
+ .bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv,
.bdrv_co_writev = nbd_co_writev,
.format_name = "nbd",
.protocol_name = "nbd+unix",
.instance_size = sizeof(BDRVNBDState),
+ .bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv,
.bdrv_co_writev = nbd_co_writev,