#define NBD_OPT_ABORT (2)
#define NBD_OPT_LIST (3)
+/* NBD errors are based on errno numbers, so there is a 1:1 mapping,
+ * but only a limited set of errno values is specified in the protocol.
+ * Everything else is squashed to EINVAL.
+ */
+#define NBD_SUCCESS 0
+#define NBD_EPERM 1
+#define NBD_EIO 5
+#define NBD_ENOMEM 12
+#define NBD_EINVAL 22
+#define NBD_ENOSPC 28
+
+static int system_errno_to_nbd_errno(int err)
+{
+ switch (err) {
+ case 0:
+ return NBD_SUCCESS;
+ case EPERM:
+ return NBD_EPERM;
+ case EIO:
+ return NBD_EIO;
+ case ENOMEM:
+ return NBD_ENOMEM;
+#ifdef EDQUOT
+ case EDQUOT:
+#endif
+ case EFBIG:
+ case ENOSPC:
+ return NBD_ENOSPC;
+ case EINVAL:
+ default:
+ return NBD_EINVAL;
+ }
+}
+
+static int nbd_errno_to_system_errno(int err)
+{
+ switch (err) {
+ case NBD_SUCCESS:
+ return 0;
+ case NBD_EPERM:
+ return EPERM;
+ case NBD_EIO:
+ return EIO;
+ case NBD_ENOMEM:
+ return ENOMEM;
+ case NBD_ENOSPC:
+ return ENOSPC;
+ case NBD_EINVAL:
+ default:
+ return EINVAL;
+ }
+}
+
/* Definitions for opaque data types */
typedef struct NBDRequest NBDRequest;
return nbd_wr_sync(fd, buffer, size, true);
}
+static ssize_t drop_sync(int fd, size_t size)
+{
+ ssize_t ret, dropped = size;
+ uint8_t *buffer = g_malloc(MIN(65536, size));
+
+ while (size > 0) {
+ ret = read_sync(fd, buffer, MIN(65536, size));
+ if (ret < 0) {
+ g_free(buffer);
+ return ret;
+ }
+
+ assert(ret <= size);
+ size -= ret;
+ }
+
+ g_free(buffer);
+ return dropped;
+}
+
static ssize_t write_sync(int fd, void *buffer, size_t size)
{
int ret;
csock = client->sock;
if (length) {
+ if (drop_sync(csock, length) != length) {
+ return -EIO;
+ }
return nbd_send_rep(csock, NBD_REP_ERR_INVALID, NBD_OPT_LIST);
}
static int nbd_receive_options(NBDClient *client)
{
+ int csock = client->sock;
+ uint32_t flags;
+
+ /* Client sends:
+ [ 0 .. 3] client flags
+
+ [ 0 .. 7] NBD_OPTS_MAGIC
+ [ 8 .. 11] NBD option
+ [12 .. 15] Data length
+ ... Rest of request
+
+ [ 0 .. 7] NBD_OPTS_MAGIC
+ [ 8 .. 11] Second NBD option
+ [12 .. 15] Data length
+ ... Rest of request
+ */
+
+ if (read_sync(csock, &flags, sizeof(flags)) != sizeof(flags)) {
+ LOG("read failed");
+ return -EIO;
+ }
+ TRACE("Checking client flags");
+ be32_to_cpus(&flags);
+ if (flags != 0 && flags != NBD_FLAG_C_FIXED_NEWSTYLE) {
+ LOG("Bad client flags received");
+ return -EIO;
+ }
+
while (1) {
- int csock = client->sock;
+ int ret;
uint32_t tmp, length;
uint64_t magic;
- /* Client sends:
- [ 0 .. 3] client flags
- [ 4 .. 11] NBD_OPTS_MAGIC
- [12 .. 15] NBD option
- [16 .. 19] length
- ... Rest of request
- */
-
- if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
- LOG("read failed");
- return -EINVAL;
- }
- TRACE("Checking client flags");
- tmp = be32_to_cpu(tmp);
- if (tmp != 0 && tmp != NBD_FLAG_C_FIXED_NEWSTYLE) {
- LOG("Bad client flags received");
- return -EINVAL;
- }
-
if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
LOG("read failed");
return -EINVAL;
TRACE("Checking option");
switch (be32_to_cpu(tmp)) {
case NBD_OPT_LIST:
- if (nbd_handle_list(client, length) < 0) {
- return 1;
+ ret = nbd_handle_list(client, length);
+ if (ret < 0) {
+ return ret;
}
break;
}
int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
- off_t *size, size_t *blocksize)
+ off_t *size, Error **errp)
{
char buf[256];
uint64_t magic, s;
rc = -EINVAL;
if (read_sync(csock, buf, 8) != 8) {
- LOG("read failed");
+ error_setg(errp, "Failed to read data");
goto fail;
}
buf[8] = '\0';
if (strlen(buf) == 0) {
- LOG("server connection closed");
+ error_setg(errp, "Server connection closed unexpectedly");
goto fail;
}
qemu_isprint(buf[7]) ? buf[7] : '.');
if (memcmp(buf, "NBDMAGIC", 8) != 0) {
- LOG("Invalid magic received");
+ error_setg(errp, "Invalid magic received");
goto fail;
}
if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
- LOG("read failed");
+ error_setg(errp, "Failed to read magic");
goto fail;
}
magic = be64_to_cpu(magic);
TRACE("Checking magic (opts_magic)");
if (magic != NBD_OPTS_MAGIC) {
- LOG("Bad magic received");
+ if (magic == NBD_CLIENT_MAGIC) {
+ error_setg(errp, "Server does not support export names");
+ } else {
+ error_setg(errp, "Bad magic received");
+ }
goto fail;
}
if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
- LOG("flags read failed");
+ error_setg(errp, "Failed to read server flags");
goto fail;
}
*flags = be16_to_cpu(tmp) << 16;
/* reserved for future use */
if (write_sync(csock, &reserved, sizeof(reserved)) !=
sizeof(reserved)) {
- LOG("write failed (reserved)");
+ error_setg(errp, "Failed to read reserved field");
goto fail;
}
/* write the export name */
magic = cpu_to_be64(magic);
if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
- LOG("write failed (magic)");
+ error_setg(errp, "Failed to send export name magic");
goto fail;
}
opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
- LOG("write failed (opt)");
+ error_setg(errp, "Failed to send export name option number");
goto fail;
}
namesize = cpu_to_be32(strlen(name));
if (write_sync(csock, &namesize, sizeof(namesize)) !=
sizeof(namesize)) {
- LOG("write failed (namesize)");
+ error_setg(errp, "Failed to send export name length");
goto fail;
}
if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) {
- LOG("write failed (name)");
+ error_setg(errp, "Failed to send export name");
goto fail;
}
} else {
TRACE("Checking magic (cli_magic)");
if (magic != NBD_CLIENT_MAGIC) {
- LOG("Bad magic received");
+ if (magic == NBD_OPTS_MAGIC) {
+ error_setg(errp, "Server requires an export name");
+ } else {
+ error_setg(errp, "Bad magic received");
+ }
goto fail;
}
}
if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) {
- LOG("read failed");
+ error_setg(errp, "Failed to read export length");
goto fail;
}
*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)");
+ error_setg(errp, "Failed to read export flags");
goto fail;
}
*flags = be32_to_cpup(flags);
} else {
if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
- LOG("read failed (tmp)");
+ error_setg(errp, "Failed to read export flags");
goto fail;
}
- *flags |= be32_to_cpu(tmp);
+ *flags |= be16_to_cpu(tmp);
}
if (read_sync(csock, &buf, 124) != 124) {
- LOG("read failed (buf)");
+ error_setg(errp, "Failed to read reserved block");
goto fail;
}
rc = 0;
}
#ifdef __linux__
-int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize)
+int nbd_init(int fd, int csock, uint32_t flags, off_t size)
{
TRACE("Setting NBD socket");
return -serrno;
}
- TRACE("Setting block size to %lu", (unsigned long)blocksize);
+ TRACE("Setting block size to %lu", (unsigned long)BDRV_SECTOR_SIZE);
- if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) < 0) {
+ if (ioctl(fd, NBD_SET_BLKSIZE, (size_t)BDRV_SECTOR_SIZE) < 0) {
int serrno = errno;
LOG("Failed setting NBD block size");
return -serrno;
}
- TRACE("Setting size to %zd block(s)", (size_t)(size / blocksize));
+ TRACE("Setting size to %zd block(s)", (size_t)(size / BDRV_SECTOR_SIZE));
- if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) < 0) {
+ if (ioctl(fd, NBD_SET_SIZE_BLOCKS, (size_t)(size / BDRV_SECTOR_SIZE)) < 0) {
int serrno = errno;
LOG("Failed setting size (in blocks)");
return -serrno;
return ret;
}
#else
-int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize)
+int nbd_init(int fd, int csock, uint32_t flags, off_t size)
{
return -ENOTSUP;
}
reply->error = be32_to_cpup((uint32_t*)(buf + 4));
reply->handle = be64_to_cpup((uint64_t*)(buf + 8));
+ reply->error = nbd_errno_to_system_errno(reply->error);
+
TRACE("Got reply: "
"{ magic = 0x%x, .error = %d, handle = %" PRIu64" }",
magic, reply->error, reply->handle);
uint8_t buf[NBD_REPLY_SIZE];
ssize_t ret;
+ reply->error = system_errno_to_nbd_errno(reply->error);
+
/* Reply
[ 0 .. 3] magic (NBD_REPLY_MAGIC)
[ 4 .. 7] error (0 == no error)
{
if (--client->refcount == 0) {
/* The last reference should be dropped by client->close,
- * which is called by nbd_client_close.
+ * which is called by client_close.
*/
assert(client->closing);
}
}
-void nbd_client_close(NBDClient *client)
+static void client_close(NBDClient *client)
{
if (client->closing) {
return;
}
NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size,
- uint32_t nbdflags, void (*close)(NBDExport *))
+ uint32_t nbdflags, void (*close)(NBDExport *),
+ Error **errp)
{
NBDExport *exp = g_malloc0(sizeof(NBDExport));
exp->refcount = 1;
exp->blk = blk;
exp->dev_offset = dev_offset;
exp->nbdflags = nbdflags;
- exp->size = size == -1 ? blk_getlength(blk) : size;
+ 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;
+ }
+ exp->size -= exp->size % BDRV_SECTOR_SIZE;
+
exp->close = close;
exp->ctx = blk_get_aio_context(blk);
blk_ref(blk);
*/
blk_invalidate_cache(blk, NULL);
return exp;
+
+fail:
+ g_free(exp);
+ return NULL;
}
NBDExport *nbd_export_find(const char *name)
nbd_export_get(exp);
QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
- nbd_client_close(client);
+ client_close(client);
}
nbd_export_set_name(exp, NULL);
nbd_export_put(exp);
default:
LOG("invalid request type (%u) received", request.type);
invalid_request:
- reply.error = -EINVAL;
+ reply.error = EINVAL;
error_reply:
if (nbd_co_send_reply(req, &reply, 0) < 0) {
goto out;
out:
nbd_request_put(req);
- nbd_client_close(client);
+ client_close(client);
}
static void nbd_read(void *opaque)