This sends the last error saved in the connection handle back to the
NBD client. This is informational and best effort.
Errors are isolated by connection, so they shouldn't leak between
connections (which would be a security issue).
However there are unavoidable cases where we leak errors across
callbacks:
* When there are multiple callbacks running in parallel on a single
connection (the parallel thread model), nbdkit_error might be called
in another callback before we get to report the first error. In
theory this could be solved by storing errors both by connection &
in thread-local storage, but that's complicated to implement.
* If the plugin does not consistently call nbdkit_error along error
paths then an error from an earlier callback might be reported in an
unrelated callback that does not call nbdkit_error. We could get
around this by resetting the error at the beginning of a callback,
although that is itself problematic in the parallel case because of
the previous point.
---
server/protocol-handshake-newstyle.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/server/protocol-handshake-newstyle.c b/server/protocol-handshake-newstyle.c
index 6b3bc76f0f..15e90e0325 100644
--- a/server/protocol-handshake-newstyle.c
+++ b/server/protocol-handshake-newstyle.c
@@ -57,16 +57,31 @@ send_newstyle_option_reply (uint32_t option, uint32_t reply)
{
GET_CONN;
struct nbd_fixed_new_option_reply fixed_new_option_reply;
+ CLEANUP_FREE char *last_error = NULL;
+ uint32_t replylen = 0;
+
+ if (NBD_REP_IS_ERR (reply)) {
+ size_t len;
+
+ last_error = connection_get_last_error ();
+ if (last_error) {
+ len = strlen (last_error);
+ if (len <= NBD_MAX_STRING)
+ replylen = len;
+ }
+ }
fixed_new_option_reply.magic = htobe64 (NBD_REP_MAGIC);
fixed_new_option_reply.option = htobe32 (option);
fixed_new_option_reply.reply = htobe32 (reply);
- fixed_new_option_reply.replylen = htobe32 (0);
+ fixed_new_option_reply.replylen = htobe32 (replylen);
debug ("replying to %s with %s", name_of_nbd_opt (option),
name_of_nbd_rep (reply));
if (conn->send (&fixed_new_option_reply,
- sizeof fixed_new_option_reply, 0) == -1) {
+ sizeof fixed_new_option_reply,
+ replylen > 0 ? SEND_MORE : 0) == -1) {
+ err:
/* The protocol document says that the client is allowed to simply
* drop the connection after sending NBD_OPT_ABORT, or may read
* the reply.
@@ -77,6 +92,8 @@ send_newstyle_option_reply (uint32_t option, uint32_t reply)
nbdkit_error ("write: %s: %m", name_of_nbd_opt (option));
return -1;
}
+ if (replylen > 0 && conn->send (last_error, replylen, 0) == -1)
+ goto err;
return 0;
}
--
2.44.0