mirror of https://github.com/xqemu/xqemu.git
pull-nbd-2018-01-17
- Vladimir Sementsov-Ogievskiy/Eric Blake: 0/6 NBD server refactoring -----BEGIN PGP SIGNATURE----- Comment: Public key at http://people.redhat.com/eblake/eblake.gpg iQEcBAABCAAGBQJaYATDAAoJEKeha0olJ0NqfrsH/R2TNocSairWstSAMB934vwi /TlkIypL1PEVfse9ZEGj++moII1LXJyABkDfjoMU4/S5EmUcM7LvabQOYSeHERi4 renxLx/wMtbZFBILUHm05FlGK2X8BEBz1qxVnIJ1vBtSLjWtG3tQltj5BKrx0hrI wSUzPJIa7QPxWrihlPEpk9oZt3EDckJxRCsTxYM4PYCUHpRrEBpagxFlJ8I71Zik oGH+mOn9Ngg28sC5Mskw31vL+oJGvsQpCNbxfndyZSy2zc7Lgaigse15NLTALFK1 yXJB5RH0Hcz/KHqzByiJtXT2uOD6fjmAFdjOzUiRNKTNrUVpdo83w20MSgZqaRA= =6G6p -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2018-01-17' into staging pull-nbd-2018-01-17 - Vladimir Sementsov-Ogievskiy/Eric Blake: 0/6 NBD server refactoring # gpg: Signature made Thu 18 Jan 2018 02:21:55 GMT # gpg: using RSA key 0xA7A16B4A2527436A # gpg: Good signature from "Eric Blake <eblake@redhat.com>" # gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" # gpg: aka "[jpeg image of size 6874]" # Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A * remotes/ericb/tags/pull-nbd-2018-01-17: nbd/server: structurize option reply sending nbd/server: Add helper functions for parsing option payload nbd/server: Add va_list form of nbd_negotiate_send_rep_err() nbd/server: Better error for NBD_OPT_EXPORT_NAME failure nbd/server: refactor negotiation functions parameters nbd/server: Hoist nbd_reject_length() earlier Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
b4d6ed1c5a
342
nbd/server.c
342
nbd/server.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2016-2017 Red Hat, Inc.
|
||||
* Copyright (C) 2016-2018 Red Hat, Inc.
|
||||
* Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
|
||||
*
|
||||
* Network Block Device Server Side
|
||||
|
@ -102,9 +102,11 @@ struct NBDClient {
|
|||
bool closing;
|
||||
|
||||
bool structured_reply;
|
||||
};
|
||||
|
||||
/* That's all folks */
|
||||
uint32_t opt; /* Current option being negotiated */
|
||||
uint32_t optlen; /* remaining length of data in ioc for the option being
|
||||
negotiated now */
|
||||
};
|
||||
|
||||
static void nbd_client_receive_next_request(NBDClient *client);
|
||||
|
||||
|
@ -135,73 +137,58 @@ static void nbd_client_receive_next_request(NBDClient *client);
|
|||
|
||||
*/
|
||||
|
||||
static inline void set_be_option_rep(NBDOptionReply *rep, uint32_t option,
|
||||
uint32_t type, uint32_t length)
|
||||
{
|
||||
stq_be_p(&rep->magic, NBD_REP_MAGIC);
|
||||
stl_be_p(&rep->option, option);
|
||||
stl_be_p(&rep->type, type);
|
||||
stl_be_p(&rep->length, length);
|
||||
}
|
||||
|
||||
/* Send a reply header, including length, but no payload.
|
||||
* Return -errno on error, 0 on success. */
|
||||
static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type,
|
||||
uint32_t opt, uint32_t len, Error **errp)
|
||||
static int nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type,
|
||||
uint32_t len, Error **errp)
|
||||
{
|
||||
uint64_t magic;
|
||||
NBDOptionReply rep;
|
||||
|
||||
trace_nbd_negotiate_send_rep_len(opt, nbd_opt_lookup(opt),
|
||||
trace_nbd_negotiate_send_rep_len(client->opt, nbd_opt_lookup(client->opt),
|
||||
type, nbd_rep_lookup(type), len);
|
||||
|
||||
assert(len < NBD_MAX_BUFFER_SIZE);
|
||||
magic = cpu_to_be64(NBD_REP_MAGIC);
|
||||
if (nbd_write(ioc, &magic, sizeof(magic), errp) < 0) {
|
||||
error_prepend(errp, "write failed (rep magic): ");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
opt = cpu_to_be32(opt);
|
||||
if (nbd_write(ioc, &opt, sizeof(opt), errp) < 0) {
|
||||
error_prepend(errp, "write failed (rep opt): ");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
type = cpu_to_be32(type);
|
||||
if (nbd_write(ioc, &type, sizeof(type), errp) < 0) {
|
||||
error_prepend(errp, "write failed (rep type): ");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len = cpu_to_be32(len);
|
||||
if (nbd_write(ioc, &len, sizeof(len), errp) < 0) {
|
||||
error_prepend(errp, "write failed (rep data length): ");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
set_be_option_rep(&rep, client->opt, type, len);
|
||||
return nbd_write(client->ioc, &rep, sizeof(rep), errp);
|
||||
}
|
||||
|
||||
/* Send a reply header with default 0 length.
|
||||
* Return -errno on error, 0 on success. */
|
||||
static int nbd_negotiate_send_rep(QIOChannel *ioc, uint32_t type, uint32_t opt,
|
||||
static int nbd_negotiate_send_rep(NBDClient *client, uint32_t type,
|
||||
Error **errp)
|
||||
{
|
||||
return nbd_negotiate_send_rep_len(ioc, type, opt, 0, errp);
|
||||
return nbd_negotiate_send_rep_len(client, type, 0, errp);
|
||||
}
|
||||
|
||||
/* Send an error reply.
|
||||
* Return -errno on error, 0 on success. */
|
||||
static int GCC_FMT_ATTR(5, 6)
|
||||
nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type,
|
||||
uint32_t opt, Error **errp, const char *fmt, ...)
|
||||
static int GCC_FMT_ATTR(4, 0)
|
||||
nbd_negotiate_send_rep_verr(NBDClient *client, uint32_t type,
|
||||
Error **errp, const char *fmt, va_list va)
|
||||
{
|
||||
va_list va;
|
||||
char *msg;
|
||||
int ret;
|
||||
size_t len;
|
||||
|
||||
va_start(va, fmt);
|
||||
msg = g_strdup_vprintf(fmt, va);
|
||||
va_end(va);
|
||||
len = strlen(msg);
|
||||
assert(len < 4096);
|
||||
trace_nbd_negotiate_send_rep_err(msg);
|
||||
ret = nbd_negotiate_send_rep_len(ioc, type, opt, len, errp);
|
||||
ret = nbd_negotiate_send_rep_len(client, type, len, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
if (nbd_write(ioc, msg, len, errp) < 0) {
|
||||
if (nbd_write(client->ioc, msg, len, errp) < 0) {
|
||||
error_prepend(errp, "write failed (error message): ");
|
||||
ret = -EIO;
|
||||
} else {
|
||||
|
@ -213,23 +200,72 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Send an error reply.
|
||||
* Return -errno on error, 0 on success. */
|
||||
static int GCC_FMT_ATTR(4, 5)
|
||||
nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type,
|
||||
Error **errp, const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
int ret;
|
||||
|
||||
va_start(va, fmt);
|
||||
ret = nbd_negotiate_send_rep_verr(client, type, errp, fmt, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Drop remainder of the current option, and send a reply with the
|
||||
* given error type and message. Return -errno on read or write
|
||||
* failure; or 0 if connection is still live. */
|
||||
static int GCC_FMT_ATTR(4, 5)
|
||||
nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
int ret = nbd_drop(client->ioc, client->optlen, errp);
|
||||
va_list va;
|
||||
|
||||
client->optlen = 0;
|
||||
if (!ret) {
|
||||
va_start(va, fmt);
|
||||
ret = nbd_negotiate_send_rep_verr(client, type, errp, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read size bytes from the unparsed payload of the current option.
|
||||
* Return -errno on I/O error, 0 if option was completely handled by
|
||||
* sending a reply about inconsistent lengths, or 1 on success. */
|
||||
static int nbd_opt_read(NBDClient *client, void *buffer, size_t size,
|
||||
Error **errp)
|
||||
{
|
||||
if (size > client->optlen) {
|
||||
return nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp,
|
||||
"Inconsistent lengths in option %s",
|
||||
nbd_opt_lookup(client->opt));
|
||||
}
|
||||
client->optlen -= size;
|
||||
return qio_channel_read_all(client->ioc, buffer, size, errp) < 0 ? -EIO : 1;
|
||||
}
|
||||
|
||||
/* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload.
|
||||
* Return -errno on error, 0 on success. */
|
||||
static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp,
|
||||
static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp,
|
||||
Error **errp)
|
||||
{
|
||||
size_t name_len, desc_len;
|
||||
uint32_t len;
|
||||
const char *name = exp->name ? exp->name : "";
|
||||
const char *desc = exp->description ? exp->description : "";
|
||||
QIOChannel *ioc = client->ioc;
|
||||
int ret;
|
||||
|
||||
trace_nbd_negotiate_send_rep_list(name, desc);
|
||||
name_len = strlen(name);
|
||||
desc_len = strlen(desc);
|
||||
len = name_len + desc_len + sizeof(len);
|
||||
ret = nbd_negotiate_send_rep_len(ioc, NBD_REP_SERVER, NBD_OPT_LIST, len,
|
||||
errp);
|
||||
ret = nbd_negotiate_send_rep_len(client, NBD_REP_SERVER, len, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -258,20 +294,21 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp,
|
|||
static int nbd_negotiate_handle_list(NBDClient *client, Error **errp)
|
||||
{
|
||||
NBDExport *exp;
|
||||
assert(client->opt == NBD_OPT_LIST);
|
||||
|
||||
/* For each export, send a NBD_REP_SERVER reply. */
|
||||
QTAILQ_FOREACH(exp, &exports, next) {
|
||||
if (nbd_negotiate_send_rep_list(client->ioc, exp, errp)) {
|
||||
if (nbd_negotiate_send_rep_list(client, exp, errp)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
/* Finish with a NBD_REP_ACK. */
|
||||
return nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_LIST, errp);
|
||||
return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
|
||||
}
|
||||
|
||||
/* Send a reply to NBD_OPT_EXPORT_NAME.
|
||||
* Return -errno on error, 0 on success. */
|
||||
static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length,
|
||||
static int nbd_negotiate_handle_export_name(NBDClient *client,
|
||||
uint16_t myflags, bool no_zeroes,
|
||||
Error **errp)
|
||||
{
|
||||
|
@ -288,15 +325,16 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length,
|
|||
[10 .. 133] reserved (0) [unless no_zeroes]
|
||||
*/
|
||||
trace_nbd_negotiate_handle_export_name();
|
||||
if (length >= sizeof(name)) {
|
||||
if (client->optlen >= sizeof(name)) {
|
||||
error_setg(errp, "Bad length received");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (nbd_read(client->ioc, name, length, errp) < 0) {
|
||||
if (nbd_read(client->ioc, name, client->optlen, errp) < 0) {
|
||||
error_prepend(errp, "read failed: ");
|
||||
return -EINVAL;
|
||||
return -EIO;
|
||||
}
|
||||
name[length] = '\0';
|
||||
name[client->optlen] = '\0';
|
||||
client->optlen = 0;
|
||||
|
||||
trace_nbd_negotiate_handle_export_name_request(name);
|
||||
|
||||
|
@ -326,14 +364,14 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length,
|
|||
/* Send a single NBD_REP_INFO, with a buffer @buf of @length bytes.
|
||||
* The buffer does NOT include the info type prefix.
|
||||
* Return -errno on error, 0 if ready to send more. */
|
||||
static int nbd_negotiate_send_info(NBDClient *client, uint32_t opt,
|
||||
static int nbd_negotiate_send_info(NBDClient *client,
|
||||
uint16_t info, uint32_t length, void *buf,
|
||||
Error **errp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
trace_nbd_negotiate_send_info(info, nbd_info_lookup(info), length);
|
||||
rc = nbd_negotiate_send_rep_len(client->ioc, NBD_REP_INFO, opt,
|
||||
rc = nbd_negotiate_send_rep_len(client, NBD_REP_INFO,
|
||||
sizeof(info) + length, errp);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
|
@ -348,11 +386,33 @@ static int nbd_negotiate_send_info(NBDClient *client, uint32_t opt,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* nbd_reject_length: Handle any unexpected payload.
|
||||
* @fatal requests that we quit talking to the client, even if we are able
|
||||
* to successfully send an error reply.
|
||||
* Return:
|
||||
* -errno transmission error occurred or @fatal was requested, errp is set
|
||||
* 0 error message successfully sent to client, errp is not set
|
||||
*/
|
||||
static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(client->optlen);
|
||||
ret = nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp,
|
||||
"option '%s' has unexpected length",
|
||||
nbd_opt_lookup(client->opt));
|
||||
if (fatal && !ret) {
|
||||
error_setg(errp, "option '%s' has unexpected length",
|
||||
nbd_opt_lookup(client->opt));
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Handle NBD_OPT_INFO and NBD_OPT_GO.
|
||||
* Return -errno on error, 0 if ready for next option, and 1 to move
|
||||
* into transmission phase. */
|
||||
static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
|
||||
uint32_t opt, uint16_t myflags,
|
||||
static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags,
|
||||
Error **errp)
|
||||
{
|
||||
int rc;
|
||||
|
@ -365,7 +425,6 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
|
|||
bool blocksize = false;
|
||||
uint32_t sizes[3];
|
||||
char buf[sizeof(uint64_t) + sizeof(uint16_t)];
|
||||
const char *msg;
|
||||
|
||||
/* Client sends:
|
||||
4 bytes: L, name length (can be 0)
|
||||
|
@ -373,46 +432,34 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
|
|||
2 bytes: N, number of requests (can be 0)
|
||||
N * 2 bytes: N requests
|
||||
*/
|
||||
if (length < sizeof(namelen) + sizeof(requests)) {
|
||||
msg = "overall request too short";
|
||||
goto invalid;
|
||||
}
|
||||
if (nbd_read(client->ioc, &namelen, sizeof(namelen), errp) < 0) {
|
||||
return -EIO;
|
||||
rc = nbd_opt_read(client, &namelen, sizeof(namelen), errp);
|
||||
if (rc <= 0) {
|
||||
return rc;
|
||||
}
|
||||
be32_to_cpus(&namelen);
|
||||
length -= sizeof(namelen);
|
||||
if (namelen > length - sizeof(requests) || (length - namelen) % 2) {
|
||||
msg = "name length is incorrect";
|
||||
goto invalid;
|
||||
}
|
||||
if (namelen >= sizeof(name)) {
|
||||
msg = "name too long for qemu";
|
||||
goto invalid;
|
||||
return nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp,
|
||||
"name too long for qemu");
|
||||
}
|
||||
if (nbd_read(client->ioc, name, namelen, errp) < 0) {
|
||||
return -EIO;
|
||||
rc = nbd_opt_read(client, name, namelen, errp);
|
||||
if (rc <= 0) {
|
||||
return rc;
|
||||
}
|
||||
name[namelen] = '\0';
|
||||
length -= namelen;
|
||||
trace_nbd_negotiate_handle_export_name_request(name);
|
||||
|
||||
if (nbd_read(client->ioc, &requests, sizeof(requests), errp) < 0) {
|
||||
return -EIO;
|
||||
rc = nbd_opt_read(client, &requests, sizeof(requests), errp);
|
||||
if (rc <= 0) {
|
||||
return rc;
|
||||
}
|
||||
be16_to_cpus(&requests);
|
||||
length -= sizeof(requests);
|
||||
trace_nbd_negotiate_handle_info_requests(requests);
|
||||
if (requests != length / sizeof(request)) {
|
||||
msg = "incorrect number of requests for overall length";
|
||||
goto invalid;
|
||||
}
|
||||
while (requests--) {
|
||||
if (nbd_read(client->ioc, &request, sizeof(request), errp) < 0) {
|
||||
return -EIO;
|
||||
rc = nbd_opt_read(client, &request, sizeof(request), errp);
|
||||
if (rc <= 0) {
|
||||
return rc;
|
||||
}
|
||||
be16_to_cpus(&request);
|
||||
length -= sizeof(request);
|
||||
trace_nbd_negotiate_handle_info_request(request,
|
||||
nbd_info_lookup(request));
|
||||
/* We care about NBD_INFO_NAME and NBD_INFO_BLOCK_SIZE;
|
||||
|
@ -427,18 +474,20 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
|
|||
break;
|
||||
}
|
||||
}
|
||||
assert(length == 0);
|
||||
if (client->optlen) {
|
||||
return nbd_reject_length(client, false, errp);
|
||||
}
|
||||
|
||||
exp = nbd_export_find(name);
|
||||
if (!exp) {
|
||||
return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_UNKNOWN,
|
||||
opt, errp, "export '%s' not present",
|
||||
return nbd_negotiate_send_rep_err(client, NBD_REP_ERR_UNKNOWN,
|
||||
errp, "export '%s' not present",
|
||||
name);
|
||||
}
|
||||
|
||||
/* Don't bother sending NBD_INFO_NAME unless client requested it */
|
||||
if (sendname) {
|
||||
rc = nbd_negotiate_send_info(client, opt, NBD_INFO_NAME, namelen, name,
|
||||
rc = nbd_negotiate_send_info(client, NBD_INFO_NAME, namelen, name,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
|
@ -450,7 +499,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
|
|||
if (exp->description) {
|
||||
size_t len = strlen(exp->description);
|
||||
|
||||
rc = nbd_negotiate_send_info(client, opt, NBD_INFO_DESCRIPTION,
|
||||
rc = nbd_negotiate_send_info(client, NBD_INFO_DESCRIPTION,
|
||||
len, exp->description, errp);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
|
@ -462,7 +511,8 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
|
|||
* whether this is OPT_INFO or OPT_GO. */
|
||||
/* minimum - 1 for back-compat, or 512 if client is new enough.
|
||||
* TODO: consult blk_bs(blk)->bl.request_alignment? */
|
||||
sizes[0] = (opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1;
|
||||
sizes[0] =
|
||||
(client->opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1;
|
||||
/* preferred - Hard-code to 4096 for now.
|
||||
* TODO: is blk_bs(blk)->bl.opt_transfer appropriate? */
|
||||
sizes[1] = 4096;
|
||||
|
@ -472,7 +522,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
|
|||
cpu_to_be32s(&sizes[0]);
|
||||
cpu_to_be32s(&sizes[1]);
|
||||
cpu_to_be32s(&sizes[2]);
|
||||
rc = nbd_negotiate_send_info(client, opt, NBD_INFO_BLOCK_SIZE,
|
||||
rc = nbd_negotiate_send_info(client, NBD_INFO_BLOCK_SIZE,
|
||||
sizeof(sizes), sizes, errp);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
|
@ -483,7 +533,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
|
|||
exp->nbdflags | myflags);
|
||||
stq_be_p(buf, exp->size);
|
||||
stw_be_p(buf + 8, exp->nbdflags | myflags);
|
||||
rc = nbd_negotiate_send_info(client, opt, NBD_INFO_EXPORT,
|
||||
rc = nbd_negotiate_send_info(client, NBD_INFO_EXPORT,
|
||||
sizeof(buf), buf, errp);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
|
@ -493,34 +543,27 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
|
|||
* request block sizes, return an error.
|
||||
* TODO: consult blk_bs(blk)->request_align, and only error if it
|
||||
* is not 1? */
|
||||
if (opt == NBD_OPT_INFO && !blocksize) {
|
||||
return nbd_negotiate_send_rep_err(client->ioc,
|
||||
NBD_REP_ERR_BLOCK_SIZE_REQD, opt,
|
||||
if (client->opt == NBD_OPT_INFO && !blocksize) {
|
||||
return nbd_negotiate_send_rep_err(client,
|
||||
NBD_REP_ERR_BLOCK_SIZE_REQD,
|
||||
errp,
|
||||
"request NBD_INFO_BLOCK_SIZE to "
|
||||
"use this export");
|
||||
}
|
||||
|
||||
/* Final reply */
|
||||
rc = nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, opt, errp);
|
||||
rc = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (opt == NBD_OPT_GO) {
|
||||
if (client->opt == NBD_OPT_GO) {
|
||||
client->exp = exp;
|
||||
QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);
|
||||
nbd_export_get(client->exp);
|
||||
rc = 1;
|
||||
}
|
||||
return rc;
|
||||
|
||||
invalid:
|
||||
if (nbd_drop(client->ioc, length, errp) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_INVALID, opt,
|
||||
errp, "%s", msg);
|
||||
}
|
||||
|
||||
|
||||
|
@ -533,11 +576,12 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
|
|||
QIOChannelTLS *tioc;
|
||||
struct NBDTLSHandshakeData data = { 0 };
|
||||
|
||||
assert(client->opt == NBD_OPT_STARTTLS);
|
||||
|
||||
trace_nbd_negotiate_handle_starttls();
|
||||
ioc = client->ioc;
|
||||
|
||||
if (nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK,
|
||||
NBD_OPT_STARTTLS, errp) < 0) {
|
||||
if (nbd_negotiate_send_rep(client, NBD_REP_ACK, errp) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -570,34 +614,6 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
|
|||
return QIO_CHANNEL(tioc);
|
||||
}
|
||||
|
||||
/* nbd_reject_length: Handle any unexpected payload.
|
||||
* @fatal requests that we quit talking to the client, even if we are able
|
||||
* to successfully send an error to the guest.
|
||||
* Return:
|
||||
* -errno transmission error occurred or @fatal was requested, errp is set
|
||||
* 0 error message successfully sent to client, errp is not set
|
||||
*/
|
||||
static int nbd_reject_length(NBDClient *client, uint32_t length,
|
||||
uint32_t option, bool fatal, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(length);
|
||||
if (nbd_drop(client->ioc, length, errp) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
ret = nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_INVALID,
|
||||
option, errp,
|
||||
"option '%s' should have zero length",
|
||||
nbd_opt_lookup(option));
|
||||
if (fatal && !ret) {
|
||||
error_setg(errp, "option '%s' should have zero length",
|
||||
nbd_opt_lookup(option));
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* nbd_negotiate_options
|
||||
* Process all NBD_OPT_* client option commands, during fixed newstyle
|
||||
* negotiation.
|
||||
|
@ -670,12 +686,15 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
|
|||
return -EINVAL;
|
||||
}
|
||||
option = be32_to_cpu(option);
|
||||
client->opt = option;
|
||||
|
||||
if (nbd_read(client->ioc, &length, sizeof(length), errp) < 0) {
|
||||
error_prepend(errp, "read failed: ");
|
||||
return -EINVAL;
|
||||
}
|
||||
length = be32_to_cpu(length);
|
||||
assert(!client->optlen);
|
||||
client->optlen = length;
|
||||
|
||||
if (length > NBD_MAX_BUFFER_SIZE) {
|
||||
error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)",
|
||||
|
@ -697,8 +716,7 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
|
|||
if (length) {
|
||||
/* Unconditionally drop the connection if the client
|
||||
* can't start a TLS negotiation correctly */
|
||||
return nbd_reject_length(client, length, option, true,
|
||||
errp);
|
||||
return nbd_reject_length(client, true, errp);
|
||||
}
|
||||
tioc = nbd_negotiate_handle_starttls(client, errp);
|
||||
if (!tioc) {
|
||||
|
@ -716,15 +734,9 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
|
|||
return -EINVAL;
|
||||
|
||||
default:
|
||||
if (nbd_drop(client->ioc, length, errp) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
ret = nbd_negotiate_send_rep_err(client->ioc,
|
||||
NBD_REP_ERR_TLS_REQD,
|
||||
option, errp,
|
||||
"Option 0x%" PRIx32
|
||||
"not permitted before TLS",
|
||||
option);
|
||||
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. */
|
||||
|
@ -737,8 +749,7 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
|
|||
switch (option) {
|
||||
case NBD_OPT_LIST:
|
||||
if (length) {
|
||||
ret = nbd_reject_length(client, length, option, false,
|
||||
errp);
|
||||
ret = nbd_reject_length(client, false, errp);
|
||||
} else {
|
||||
ret = nbd_negotiate_handle_list(client, errp);
|
||||
}
|
||||
|
@ -748,18 +759,17 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
|
|||
/* NBD spec says we must try to reply before
|
||||
* disconnecting, but that we must also tolerate
|
||||
* guests that don't wait for our reply. */
|
||||
nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, option, NULL);
|
||||
nbd_negotiate_send_rep(client, NBD_REP_ACK, NULL);
|
||||
return 1;
|
||||
|
||||
case NBD_OPT_EXPORT_NAME:
|
||||
return nbd_negotiate_handle_export_name(client, length,
|
||||
return nbd_negotiate_handle_export_name(client,
|
||||
myflags, no_zeroes,
|
||||
errp);
|
||||
|
||||
case NBD_OPT_INFO:
|
||||
case NBD_OPT_GO:
|
||||
ret = nbd_negotiate_handle_info(client, length, option,
|
||||
myflags, errp);
|
||||
ret = nbd_negotiate_handle_info(client, myflags, errp);
|
||||
if (ret == 1) {
|
||||
assert(option == NBD_OPT_GO);
|
||||
return 0;
|
||||
|
@ -768,47 +778,36 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
|
|||
|
||||
case NBD_OPT_STARTTLS:
|
||||
if (length) {
|
||||
ret = nbd_reject_length(client, length, option, false,
|
||||
errp);
|
||||
ret = nbd_reject_length(client, false, errp);
|
||||
} else if (client->tlscreds) {
|
||||
ret = nbd_negotiate_send_rep_err(client->ioc,
|
||||
NBD_REP_ERR_INVALID,
|
||||
option, errp,
|
||||
ret = nbd_negotiate_send_rep_err(client,
|
||||
NBD_REP_ERR_INVALID, errp,
|
||||
"TLS already enabled");
|
||||
} else {
|
||||
ret = nbd_negotiate_send_rep_err(client->ioc,
|
||||
NBD_REP_ERR_POLICY,
|
||||
option, errp,
|
||||
ret = nbd_negotiate_send_rep_err(client,
|
||||
NBD_REP_ERR_POLICY, errp,
|
||||
"TLS not configured");
|
||||
}
|
||||
break;
|
||||
|
||||
case NBD_OPT_STRUCTURED_REPLY:
|
||||
if (length) {
|
||||
ret = nbd_reject_length(client, length, option, false,
|
||||
errp);
|
||||
ret = nbd_reject_length(client, false, errp);
|
||||
} else if (client->structured_reply) {
|
||||
ret = nbd_negotiate_send_rep_err(
|
||||
client->ioc, NBD_REP_ERR_INVALID, option, errp,
|
||||
client, NBD_REP_ERR_INVALID, errp,
|
||||
"structured reply already negotiated");
|
||||
} else {
|
||||
ret = nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK,
|
||||
option, errp);
|
||||
ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
|
||||
client->structured_reply = true;
|
||||
myflags |= NBD_FLAG_SEND_DF;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (nbd_drop(client->ioc, length, errp) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
ret = nbd_negotiate_send_rep_err(client->ioc,
|
||||
NBD_REP_ERR_UNSUP,
|
||||
option, errp,
|
||||
"Unsupported option 0x%"
|
||||
PRIx32 " (%s)", option,
|
||||
nbd_opt_lookup(option));
|
||||
ret = nbd_opt_drop(client, NBD_REP_ERR_UNSUP, errp,
|
||||
"Unsupported option 0x%" PRIx32 " (%s)",
|
||||
option, nbd_opt_lookup(option));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -818,7 +817,7 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
|
|||
*/
|
||||
switch (option) {
|
||||
case NBD_OPT_EXPORT_NAME:
|
||||
return nbd_negotiate_handle_export_name(client, length,
|
||||
return nbd_negotiate_handle_export_name(client,
|
||||
myflags, no_zeroes,
|
||||
errp);
|
||||
|
||||
|
@ -898,6 +897,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp)
|
|||
}
|
||||
}
|
||||
|
||||
assert(!client->optlen);
|
||||
trace_nbd_negotiate_success();
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue