diff options
Diffstat (limited to 'nbd')
| -rw-r--r-- | nbd/client.c | 63 | ||||
| -rw-r--r-- | nbd/common.c | 5 | ||||
| -rw-r--r-- | nbd/nbd-internal.h | 15 | ||||
| -rw-r--r-- | nbd/server.c | 11 |
4 files changed, 70 insertions, 24 deletions
diff --git a/nbd/client.c b/nbd/client.c index d9b7a9b07e..6777e589d1 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -73,16 +73,46 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); */ -static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp) +/* If type represents success, return 1 without further action. + * If type represents an error reply, consume the rest 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, uint32_t opt, uint32_t type, + Error **errp) { + uint32_t len; + char *msg = NULL; + int result = -1; + if (!(type & (1 << 31))) { - return 0; + return 1; + } + + if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) { + error_setg(errp, "failed to read option length"); + return -1; + } + len = be32_to_cpu(len); + if (len) { + if (len > NBD_MAX_BUFFER_SIZE) { + error_setg(errp, "server's error message is too long"); + goto cleanup; + } + msg = g_malloc(len + 1); + if (read_sync(ioc, msg, len) != len) { + error_setg(errp, "failed to read option error message"); + goto cleanup; + } + msg[len] = '\0'; } switch (type) { case NBD_REP_ERR_UNSUP: - error_setg(errp, "Unsupported option type %x", opt); - break; + TRACE("server doesn't understand request %d, attempting fallback", + opt); + result = 0; + goto cleanup; case NBD_REP_ERR_POLICY: error_setg(errp, "Denied by server for option %x", opt); @@ -101,7 +131,13 @@ static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp) break; } - return -1; + if (msg) { + error_append_hint(errp, "%s\n", msg); + } + + cleanup: + g_free(msg); + return result; } static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp) @@ -111,6 +147,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp) uint32_t type; uint32_t len; uint32_t namelen; + int error; *name = NULL; if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { @@ -138,11 +175,9 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp) return -1; } type = be32_to_cpu(type); - if (type == NBD_REP_ERR_UNSUP) { - return 0; - } - if (nbd_handle_reply_err(opt, type, errp) < 0) { - return -1; + error = nbd_handle_reply_err(ioc, opt, type, errp); + if (error <= 0) { + return error; } if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) { @@ -628,16 +663,16 @@ ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request) uint8_t buf[NBD_REQUEST_SIZE]; ssize_t ret; + TRACE("Sending request to server: " + "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}", + request->from, request->len, request->handle, request->type); + cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); cpu_to_be32w((uint32_t*)(buf + 4), request->type); cpu_to_be64w((uint64_t*)(buf + 8), request->handle); cpu_to_be64w((uint64_t*)(buf + 16), request->from); cpu_to_be32w((uint32_t*)(buf + 24), request->len); - TRACE("Sending request to server: " - "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}", - request->from, request->len, request->handle, request->type); - ret = write_sync(ioc, buf, sizeof(buf)); if (ret < 0) { return ret; diff --git a/nbd/common.c b/nbd/common.c index a44718ce58..8ddb2dd2f0 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -50,9 +50,12 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc, * qio_channel_yield() that works with AIO contexts * and consider using that in this branch */ qemu_coroutine_yield(); - } else { + } else if (done) { + /* XXX this is needed by nbd_reply_ready. */ qio_channel_wait(ioc, do_read ? G_IO_IN : G_IO_OUT); + } else { + return -EAGAIN; } continue; } diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index d09b4ee308..379153561d 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -33,18 +33,21 @@ /* #define DEBUG_NBD */ #ifdef DEBUG_NBD -#define TRACE(msg, ...) do { \ - LOG(msg, ## __VA_ARGS__); \ -} while(0) +#define DEBUG_NBD_PRINT 1 #else -#define TRACE(msg, ...) \ - do { } while (0) +#define DEBUG_NBD_PRINT 0 #endif +#define TRACE(msg, ...) do { \ + if (DEBUG_NBD_PRINT) { \ + LOG(msg, ## __VA_ARGS__); \ + } \ +} while (0) + #define LOG(msg, ...) do { \ fprintf(stderr, "%s:%s():L%d: " msg "\n", \ __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \ -} while(0) +} while (0) /* This is all part of the "official" NBD API. * diff --git a/nbd/server.c b/nbd/server.c index b95571bdf5..2a4dd10f52 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -26,6 +26,7 @@ static int system_errno_to_nbd_errno(int err) case 0: return NBD_SUCCESS; case EPERM: + case EROFS: return NBD_EPERM; case EIO: return NBD_EIO; @@ -482,9 +483,12 @@ static int nbd_negotiate_options(NBDClient *client) return -EINVAL; default: TRACE("Unsupported option 0x%x", clientflags); + if (nbd_negotiate_drop_sync(client->ioc, length) != length) { + return -EIO; + } nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_UNSUP, clientflags); - return -EINVAL; + break; } } else { /* @@ -655,6 +659,9 @@ static ssize_t nbd_send_reply(QIOChannel *ioc, struct nbd_reply *reply) reply->error = system_errno_to_nbd_errno(reply->error); + TRACE("Sending response to client: { .error = %d, handle = %" PRIu64 " }", + reply->error, reply->handle); + /* Reply [ 0 .. 3] magic (NBD_REPLY_MAGIC) [ 4 .. 7] error (0 == no error) @@ -664,8 +671,6 @@ static ssize_t nbd_send_reply(QIOChannel *ioc, struct nbd_reply *reply) stl_be_p(buf + 4, reply->error); stq_be_p(buf + 8, reply->handle); - TRACE("Sending response to client"); - ret = write_sync(ioc, buf, sizeof(buf)); if (ret < 0) { return ret; |