#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, Error **errp)
+ off_t *size, Error **errp)
{
char buf[256];
uint64_t magic, s;
goto fail;
}
*size = be64_to_cpu(s);
- *blocksize = 1024;
TRACE("Size is %" PRIu64, *size);
if (!name) {
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) {
error_setg(errp, "Failed to read reserved block");
}
#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)
}
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)
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;