BlockBackend *blk;
char *name;
char *description;
- off_t dev_offset;
- off_t size;
+ uint64_t dev_offset;
+ uint64_t size;
uint16_t nbdflags;
QTAILQ_HEAD(, NBDClient) clients;
QTAILQ_ENTRY(NBDExport) next;
if (ret <= 0) {
return ret;
}
- cpu_to_be32s(&len);
+ len = cpu_to_be32(len);
if (len > NBD_MAX_NAME_SIZE) {
return nbd_opt_invalid(client, errp,
if (rc < 0) {
return rc;
}
- cpu_to_be16s(&info);
+ info = cpu_to_be16(info);
if (nbd_write(client->ioc, &info, sizeof(info), errp) < 0) {
return -EIO;
}
if (rc <= 0) {
return rc;
}
- be16_to_cpus(&requests);
+ requests = be16_to_cpu(requests);
trace_nbd_negotiate_handle_info_requests(requests);
while (requests--) {
rc = nbd_opt_read(client, &request, sizeof(request), errp);
if (rc <= 0) {
return rc;
}
- be16_to_cpus(&request);
+ request = be16_to_cpu(request);
trace_nbd_negotiate_handle_info_request(request,
nbd_info_lookup(request));
/* We care about NBD_INFO_NAME and NBD_INFO_BLOCK_SIZE;
/* maximum - At most 32M, but smaller as appropriate. */
sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE);
trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]);
- cpu_to_be32s(&sizes[0]);
- cpu_to_be32s(&sizes[1]);
- cpu_to_be32s(&sizes[2]);
+ sizes[0] = cpu_to_be32(sizes[0]);
+ sizes[1] = cpu_to_be32(sizes[1]);
+ sizes[2] = cpu_to_be32(sizes[2]);
rc = nbd_negotiate_send_info(client, NBD_INFO_BLOCK_SIZE,
sizeof(sizes), sizes, errp);
if (rc < 0) {
if (ret <= 0) {
return ret;
}
- cpu_to_be32s(&len);
+ len = cpu_to_be32(len);
if (len < ns_len) {
trace_nbd_negotiate_meta_query_skip("length too short");
if (ret <= 0) {
return ret;
}
- cpu_to_be32s(&nb_queries);
+ nb_queries = cpu_to_be32(nb_queries);
trace_nbd_negotiate_meta_context(nbd_opt_lookup(client->opt),
export_name, nb_queries);
if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) {
/* enable all known contexts */
meta->base_allocation = true;
+ meta->bitmap = !!meta->exp->export_bitmap;
} else {
for (i = 0; i < nb_queries; ++i) {
ret = nbd_negotiate_meta_query(client, meta, errp);
error_prepend(errp, "read failed: ");
return -EIO;
}
- be32_to_cpus(&flags);
+ flags = be32_to_cpu(flags);
trace_nbd_negotiate_options_flags(flags);
if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) {
fixedNewstyle = true;
return -EINVAL;
default:
- ret = nbd_opt_drop(client, NBD_REP_ERR_TLS_REQD, errp,
- "Option 0x%" PRIx32
- "not permitted before TLS", option);
/* Let the client keep trying, unless they asked to
- * quit. In this mode, we've already sent an error, so
- * we can't ack the abort. */
+ * quit. Always try to give an error back to the
+ * client; but when replying to OPT_ABORT, be aware
+ * that the client may hang up before receiving the
+ * error, in which case we are fine ignoring the
+ * resulting EPIPE. */
+ ret = nbd_opt_drop(client, NBD_REP_ERR_TLS_REQD,
+ option == NBD_OPT_ABORT ? NULL : errp,
+ "Option 0x%" PRIx32
+ " not permitted before TLS", option);
if (option == NBD_OPT_ABORT) {
return 1;
}
const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA |
NBD_FLAG_SEND_WRITE_ZEROES | NBD_FLAG_SEND_CACHE);
- bool oldStyle;
/* Old style negotiation header, no room for options
[ 0 .. 7] passwd ("NBDMAGIC")
trace_nbd_negotiate_begin();
memcpy(buf, "NBDMAGIC", 8);
- oldStyle = client->exp != NULL && !client->tlscreds;
- if (oldStyle) {
- trace_nbd_negotiate_old_style(client->exp->size,
- client->exp->nbdflags | myflags);
- stq_be_p(buf + 8, NBD_CLIENT_MAGIC);
- stq_be_p(buf + 16, client->exp->size);
- stl_be_p(buf + 24, client->exp->nbdflags | myflags);
-
- if (nbd_write(client->ioc, buf, sizeof(buf), errp) < 0) {
- error_prepend(errp, "write failed: ");
- return -EINVAL;
- }
- } else {
- stq_be_p(buf + 8, NBD_OPTS_MAGIC);
- stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES);
+ stq_be_p(buf + 8, NBD_OPTS_MAGIC);
+ stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES);
- if (nbd_write(client->ioc, buf, 18, errp) < 0) {
- error_prepend(errp, "write failed: ");
- return -EINVAL;
- }
- ret = nbd_negotiate_options(client, myflags, errp);
- if (ret != 0) {
- if (ret < 0) {
- error_prepend(errp, "option negotiation failed: ");
- }
- return ret;
+ if (nbd_write(client->ioc, buf, 18, errp) < 0) {
+ error_prepend(errp, "write failed: ");
+ return -EINVAL;
+ }
+ ret = nbd_negotiate_options(client, myflags, errp);
+ if (ret != 0) {
+ if (ret < 0) {
+ error_prepend(errp, "option negotiation failed: ");
}
+ return ret;
}
assert(!client->optlen);
nbd_export_close(exp);
}
-NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
- uint16_t nbdflags, void (*close)(NBDExport *),
- bool writethrough, BlockBackend *on_eject_blk,
- Error **errp)
+NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
+ uint64_t size, const char *name, const char *desc,
+ const char *bitmap, uint16_t nbdflags,
+ void (*close)(NBDExport *), bool writethrough,
+ BlockBackend *on_eject_blk, Error **errp)
{
AioContext *ctx;
BlockBackend *blk;
* that BDRV_O_INACTIVE is cleared and the image is ready for write
* access since the export could be available before migration handover.
*/
+ assert(name);
ctx = bdrv_get_aio_context(bs);
aio_context_acquire(ctx);
bdrv_invalidate_cache(bs, NULL);
exp->refcount = 1;
QTAILQ_INIT(&exp->clients);
exp->blk = blk;
+ assert(dev_offset <= INT64_MAX);
exp->dev_offset = dev_offset;
+ exp->name = g_strdup(name);
+ exp->description = g_strdup(desc);
exp->nbdflags = nbdflags;
- exp->size = size < 0 ? blk_getlength(blk) : size;
- if (exp->size < 0) {
- error_setg_errno(errp, -exp->size,
- "Failed to determine the NBD export's length");
- goto fail;
+ assert(size <= INT64_MAX - dev_offset);
+ exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE);
+
+ if (bitmap) {
+ BdrvDirtyBitmap *bm = NULL;
+ BlockDriverState *bs = blk_bs(blk);
+
+ while (true) {
+ bm = bdrv_find_dirty_bitmap(bs, bitmap);
+ if (bm != NULL || bs->backing == NULL) {
+ break;
+ }
+
+ bs = bs->backing->bs;
+ }
+
+ if (bm == NULL) {
+ error_setg(errp, "Bitmap '%s' is not found", bitmap);
+ goto fail;
+ }
+
+ if ((nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) &&
+ bdrv_dirty_bitmap_enabled(bm)) {
+ error_setg(errp,
+ "Enabled bitmap '%s' incompatible with readonly export",
+ bitmap);
+ goto fail;
+ }
+
+ if (bdrv_dirty_bitmap_user_locked(bm)) {
+ error_setg(errp, "Bitmap '%s' is in use", bitmap);
+ goto fail;
+ }
+
+ bdrv_dirty_bitmap_set_qmp_locked(bm, true);
+ exp->export_bitmap = bm;
+ exp->export_bitmap_context = g_strdup_printf("qemu:dirty-bitmap:%s",
+ bitmap);
}
- exp->size -= exp->size % BDRV_SECTOR_SIZE;
exp->close = close;
exp->ctx = blk_get_aio_context(blk);
exp->eject_notifier.notify = nbd_eject_notifier;
blk_add_remove_bs_notifier(on_eject_blk, &exp->eject_notifier);
}
+ QTAILQ_INSERT_TAIL(&exports, exp, next);
+ nbd_export_get(exp);
return exp;
fail:
blk_unref(blk);
+ g_free(exp->name);
+ g_free(exp->description);
g_free(exp);
return NULL;
}
return NULL;
}
-void nbd_export_set_name(NBDExport *exp, const char *name)
-{
- if (exp->name == name) {
- return;
- }
-
- nbd_export_get(exp);
- if (exp->name != NULL) {
- g_free(exp->name);
- exp->name = NULL;
- QTAILQ_REMOVE(&exports, exp, next);
- nbd_export_put(exp);
- }
- if (name != NULL) {
- nbd_export_get(exp);
- exp->name = g_strdup(name);
- QTAILQ_INSERT_TAIL(&exports, exp, next);
- }
- nbd_export_put(exp);
-}
-
-void nbd_export_set_description(NBDExport *exp, const char *description)
-{
- g_free(exp->description);
- exp->description = g_strdup(description);
-}
-
void nbd_export_close(NBDExport *exp)
{
NBDClient *client, *next;
nbd_export_get(exp);
+ /*
+ * TODO: Should we expand QMP NbdServerRemoveNode enum to allow a
+ * close mode that stops advertising the export to new clients but
+ * still permits existing clients to run to completion? Because of
+ * that possibility, nbd_export_close() can be called more than
+ * once on an export.
+ */
QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
client_close(client, true);
}
- nbd_export_set_name(exp, NULL);
- nbd_export_set_description(exp, NULL);
+ if (exp->name) {
+ nbd_export_put(exp);
+ g_free(exp->name);
+ exp->name = NULL;
+ QTAILQ_REMOVE(&exports, exp, next);
+ }
+ g_free(exp->description);
+ exp->description = NULL;
nbd_export_put(exp);
}
extents_end = extent + 1;
for (extent = extents; extent < extents_end; extent++) {
- cpu_to_be32s(&extent->flags);
- cpu_to_be32s(&extent->length);
+ extent->flags = cpu_to_be32(extent->flags);
+ extent->length = cpu_to_be32(extent->length);
}
*bytes -= remaining_bytes;
bool next_dirty = !dirty;
if (dirty) {
- end = bdrv_dirty_bitmap_next_zero(bitmap, begin);
+ end = bdrv_dirty_bitmap_next_zero(bitmap, begin, UINT64_MAX);
} else {
bdrv_set_dirty_iter(it, begin);
end = bdrv_dirty_iter_next(it);
return -EROFS;
}
if (request->from > client->exp->size ||
- request->from + request->len > client->exp->size) {
+ request->len > client->exp->size - request->from) {
error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32
", Size: %" PRIu64, request->from, request->len,
- (uint64_t)client->exp->size);
+ client->exp->size);
return (request->type == NBD_CMD_WRITE ||
request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL;
}
}
if (client->structured_reply && !(request->flags & NBD_CMD_FLAG_DF) &&
- request->len) {
+ request->len && request->type != NBD_CMD_CACHE)
+ {
return nbd_co_send_sparse_read(client, request->handle, request->from,
data, request->len, errp);
}
static coroutine_fn void nbd_co_client_start(void *opaque)
{
NBDClient *client = opaque;
- NBDExport *exp = client->exp;
Error *local_err = NULL;
- if (exp) {
- nbd_export_get(exp);
- QTAILQ_INSERT_TAIL(&exp->clients, client, next);
- }
qemu_co_mutex_init(&client->send_lock);
if (nbd_negotiate(client, &local_err)) {
}
/*
- * Create a new client listener on the given export @exp, using the
- * given channel @sioc. Begin servicing it in a coroutine. When the
- * connection closes, call @close_fn with an indication of whether the
- * client completed negotiation.
+ * Create a new client listener using the given channel @sioc.
+ * Begin servicing it in a coroutine. When the connection closes, call
+ * @close_fn with an indication of whether the client completed negotiation.
*/
-void nbd_client_new(NBDExport *exp,
- QIOChannelSocket *sioc,
+void nbd_client_new(QIOChannelSocket *sioc,
QCryptoTLSCreds *tlscreds,
const char *tlsaclname,
void (*close_fn)(NBDClient *, bool))
client = g_new0(NBDClient, 1);
client->refcount = 1;
- client->exp = exp;
client->tlscreds = tlscreds;
if (tlscreds) {
object_ref(OBJECT(client->tlscreds));
co = qemu_coroutine_create(nbd_co_client_start, client);
qemu_coroutine_enter(co);
}
-
-void nbd_export_bitmap(NBDExport *exp, const char *bitmap,
- const char *bitmap_export_name, Error **errp)
-{
- BdrvDirtyBitmap *bm = NULL;
- BlockDriverState *bs = blk_bs(exp->blk);
-
- if (exp->export_bitmap) {
- error_setg(errp, "Export bitmap is already set");
- return;
- }
-
- while (true) {
- bm = bdrv_find_dirty_bitmap(bs, bitmap);
- if (bm != NULL || bs->backing == NULL) {
- break;
- }
-
- bs = bs->backing->bs;
- }
-
- if (bm == NULL) {
- error_setg(errp, "Bitmap '%s' is not found", bitmap);
- return;
- }
-
- if (bdrv_dirty_bitmap_enabled(bm)) {
- error_setg(errp, "Bitmap '%s' is enabled", bitmap);
- return;
- }
-
- if (bdrv_dirty_bitmap_qmp_locked(bm)) {
- error_setg(errp, "Bitmap '%s' is locked", bitmap);
- return;
- }
-
- bdrv_dirty_bitmap_set_qmp_locked(bm, true);
- exp->export_bitmap = bm;
- exp->export_bitmap_context =
- g_strdup_printf("qemu:dirty-bitmap:%s", bitmap_export_name);
-}