ths | 7581825 | 2008-07-03 13:41:03 +0000 | [diff] [blame] | 1 | /* |
Eric Blake | a16a790 | 2018-01-10 17:08:20 -0600 | [diff] [blame] | 2 | * Copyright (C) 2016-2018 Red Hat, Inc. |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 3 | * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws> |
| 4 | * |
Fam Zheng | 798bfe0 | 2016-01-14 16:41:02 +0800 | [diff] [blame] | 5 | * Network Block Device Server Side |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; under version 2 of the License. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
Blue Swirl | 8167ee8 | 2009-07-16 20:47:01 +0000 | [diff] [blame] | 17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
ths | 7581825 | 2008-07-03 13:41:03 +0000 | [diff] [blame] | 18 | */ |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 19 | |
Peter Maydell | d38ea87 | 2016-01-29 17:50:05 +0000 | [diff] [blame] | 20 | #include "qemu/osdep.h" |
Markus Armbruster | da34e65 | 2016-03-14 09:01:28 +0100 | [diff] [blame] | 21 | #include "qapi/error.h" |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 22 | #include "trace.h" |
Fam Zheng | 798bfe0 | 2016-01-14 16:41:02 +0800 | [diff] [blame] | 23 | #include "nbd-internal.h" |
Paolo Bonzini | ca44148 | 2015-05-07 17:25:10 +0200 | [diff] [blame] | 24 | |
| 25 | static int system_errno_to_nbd_errno(int err) |
| 26 | { |
| 27 | switch (err) { |
| 28 | case 0: |
| 29 | return NBD_SUCCESS; |
| 30 | case EPERM: |
Eric Blake | c0301fc | 2016-04-05 21:35:02 -0600 | [diff] [blame] | 31 | case EROFS: |
Paolo Bonzini | ca44148 | 2015-05-07 17:25:10 +0200 | [diff] [blame] | 32 | return NBD_EPERM; |
| 33 | case EIO: |
| 34 | return NBD_EIO; |
| 35 | case ENOMEM: |
| 36 | return NBD_ENOMEM; |
| 37 | #ifdef EDQUOT |
| 38 | case EDQUOT: |
| 39 | #endif |
| 40 | case EFBIG: |
| 41 | case ENOSPC: |
| 42 | return NBD_ENOSPC; |
Eric Blake | bae245d | 2017-10-27 12:40:28 +0200 | [diff] [blame] | 43 | case EOVERFLOW: |
| 44 | return NBD_EOVERFLOW; |
Eric Blake | b6f5d3b | 2016-10-14 13:33:16 -0500 | [diff] [blame] | 45 | case ESHUTDOWN: |
| 46 | return NBD_ESHUTDOWN; |
Paolo Bonzini | ca44148 | 2015-05-07 17:25:10 +0200 | [diff] [blame] | 47 | case EINVAL: |
| 48 | default: |
| 49 | return NBD_EINVAL; |
| 50 | } |
| 51 | } |
| 52 | |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 53 | /* Definitions for opaque data types */ |
| 54 | |
Eric Blake | 315f78a | 2016-10-14 13:33:05 -0500 | [diff] [blame] | 55 | typedef struct NBDRequestData NBDRequestData; |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 56 | |
Eric Blake | 315f78a | 2016-10-14 13:33:05 -0500 | [diff] [blame] | 57 | struct NBDRequestData { |
| 58 | QSIMPLEQ_ENTRY(NBDRequestData) entry; |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 59 | NBDClient *client; |
| 60 | uint8_t *data; |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 61 | bool complete; |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 62 | }; |
| 63 | |
| 64 | struct NBDExport { |
Paolo Bonzini | 2c8d9f0 | 2012-09-18 13:26:25 +0200 | [diff] [blame] | 65 | int refcount; |
Paolo Bonzini | 0ddf08d | 2012-09-18 13:59:03 +0200 | [diff] [blame] | 66 | void (*close)(NBDExport *exp); |
| 67 | |
Max Reitz | aadf99a | 2014-11-18 12:21:18 +0100 | [diff] [blame] | 68 | BlockBackend *blk; |
Paolo Bonzini | ee0a19e | 2012-08-22 15:59:23 +0200 | [diff] [blame] | 69 | char *name; |
Eric Blake | b1a75b3 | 2016-10-14 13:33:03 -0500 | [diff] [blame] | 70 | char *description; |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 71 | off_t dev_offset; |
| 72 | off_t size; |
Eric Blake | 7423f41 | 2016-07-21 13:34:46 -0600 | [diff] [blame] | 73 | uint16_t nbdflags; |
Paolo Bonzini | 4b9441f | 2012-09-18 13:58:25 +0200 | [diff] [blame] | 74 | QTAILQ_HEAD(, NBDClient) clients; |
Paolo Bonzini | ee0a19e | 2012-08-22 15:59:23 +0200 | [diff] [blame] | 75 | QTAILQ_ENTRY(NBDExport) next; |
Max Reitz | 958c717 | 2014-06-20 21:57:32 +0200 | [diff] [blame] | 76 | |
| 77 | AioContext *ctx; |
Max Reitz | 741cc43 | 2016-01-29 16:36:06 +0100 | [diff] [blame] | 78 | |
Kevin Wolf | cd7fca9 | 2016-07-06 11:22:39 +0200 | [diff] [blame] | 79 | BlockBackend *eject_notifier_blk; |
Max Reitz | 741cc43 | 2016-01-29 16:36:06 +0100 | [diff] [blame] | 80 | Notifier eject_notifier; |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 81 | }; |
| 82 | |
Paolo Bonzini | ee0a19e | 2012-08-22 15:59:23 +0200 | [diff] [blame] | 83 | static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); |
| 84 | |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 85 | struct NBDClient { |
| 86 | int refcount; |
Eric Blake | 0c9390d | 2017-06-08 17:26:17 -0500 | [diff] [blame] | 87 | void (*close_fn)(NBDClient *client, bool negotiated); |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 88 | |
| 89 | NBDExport *exp; |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 90 | QCryptoTLSCreds *tlscreds; |
| 91 | char *tlsaclname; |
Daniel P. Berrange | 1c778ef | 2016-02-10 18:41:04 +0000 | [diff] [blame] | 92 | QIOChannelSocket *sioc; /* The underlying data channel */ |
| 93 | QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 94 | |
| 95 | Coroutine *recv_coroutine; |
| 96 | |
| 97 | CoMutex send_lock; |
| 98 | Coroutine *send_coroutine; |
| 99 | |
Paolo Bonzini | 4b9441f | 2012-09-18 13:58:25 +0200 | [diff] [blame] | 100 | QTAILQ_ENTRY(NBDClient) next; |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 101 | int nb_requests; |
Paolo Bonzini | ff2b68a | 2012-08-22 18:45:12 +0200 | [diff] [blame] | 102 | bool closing; |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 103 | |
| 104 | bool structured_reply; |
Paolo Bonzini | 9a304d2 | 2012-08-22 15:30:31 +0200 | [diff] [blame] | 105 | |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 106 | uint32_t opt; /* Current option being negotiated */ |
| 107 | uint32_t optlen; /* remaining length of data in ioc for the option being |
| 108 | negotiated now */ |
| 109 | }; |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 110 | |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 111 | static void nbd_client_receive_next_request(NBDClient *client); |
Max Reitz | 958c717 | 2014-06-20 21:57:32 +0200 | [diff] [blame] | 112 | |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 113 | /* Basic flow for negotiation |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 114 | |
| 115 | Server Client |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 116 | Negotiate |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 117 | |
| 118 | or |
| 119 | |
| 120 | Server Client |
| 121 | Negotiate #1 |
| 122 | Option |
| 123 | Negotiate #2 |
| 124 | |
| 125 | ---- |
| 126 | |
| 127 | followed by |
| 128 | |
| 129 | Server Client |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 130 | Request |
| 131 | Response |
| 132 | Request |
| 133 | Response |
| 134 | ... |
| 135 | ... |
| 136 | Request (type == 2) |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 137 | |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 138 | */ |
| 139 | |
Vladimir Sementsov-Ogievskiy | 1d17922 | 2018-01-10 17:08:25 -0600 | [diff] [blame] | 140 | static inline void set_be_option_rep(NBDOptionReply *rep, uint32_t option, |
| 141 | uint32_t type, uint32_t length) |
| 142 | { |
| 143 | stq_be_p(&rep->magic, NBD_REP_MAGIC); |
| 144 | stl_be_p(&rep->option, option); |
| 145 | stl_be_p(&rep->type, type); |
| 146 | stl_be_p(&rep->length, length); |
| 147 | } |
| 148 | |
Eric Blake | 526e5c6 | 2016-10-14 13:33:08 -0500 | [diff] [blame] | 149 | /* Send a reply header, including length, but no payload. |
| 150 | * Return -errno on error, 0 on success. */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 151 | static int nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type, |
| 152 | uint32_t len, Error **errp) |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 153 | { |
Vladimir Sementsov-Ogievskiy | 1d17922 | 2018-01-10 17:08:25 -0600 | [diff] [blame] | 154 | NBDOptionReply rep; |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 155 | |
Vladimir Sementsov-Ogievskiy | 1d17922 | 2018-01-10 17:08:25 -0600 | [diff] [blame] | 156 | trace_nbd_negotiate_send_rep_len(client->opt, nbd_opt_lookup(client->opt), |
Eric Blake | 3736cc5 | 2017-07-07 15:30:43 -0500 | [diff] [blame] | 157 | type, nbd_rep_lookup(type), len); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 158 | |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 159 | assert(len < NBD_MAX_BUFFER_SIZE); |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 160 | |
Vladimir Sementsov-Ogievskiy | 1d17922 | 2018-01-10 17:08:25 -0600 | [diff] [blame] | 161 | set_be_option_rep(&rep, client->opt, type, len); |
| 162 | return nbd_write(client->ioc, &rep, sizeof(rep), errp); |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 163 | } |
| 164 | |
Eric Blake | 526e5c6 | 2016-10-14 13:33:08 -0500 | [diff] [blame] | 165 | /* Send a reply header with default 0 length. |
| 166 | * Return -errno on error, 0 on success. */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 167 | static int nbd_negotiate_send_rep(NBDClient *client, uint32_t type, |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 168 | Error **errp) |
Eric Blake | 526e5c6 | 2016-10-14 13:33:08 -0500 | [diff] [blame] | 169 | { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 170 | return nbd_negotiate_send_rep_len(client, type, 0, errp); |
Eric Blake | 526e5c6 | 2016-10-14 13:33:08 -0500 | [diff] [blame] | 171 | } |
| 172 | |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 173 | /* Send an error reply. |
| 174 | * Return -errno on error, 0 on success. */ |
Eric Blake | 41f5dfa | 2018-01-10 17:08:23 -0600 | [diff] [blame] | 175 | static int GCC_FMT_ATTR(4, 0) |
| 176 | nbd_negotiate_send_rep_verr(NBDClient *client, uint32_t type, |
| 177 | Error **errp, const char *fmt, va_list va) |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 178 | { |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 179 | char *msg; |
| 180 | int ret; |
| 181 | size_t len; |
| 182 | |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 183 | msg = g_strdup_vprintf(fmt, va); |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 184 | len = strlen(msg); |
| 185 | assert(len < 4096); |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 186 | trace_nbd_negotiate_send_rep_err(msg); |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 187 | ret = nbd_negotiate_send_rep_len(client, type, len, errp); |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 188 | if (ret < 0) { |
| 189 | goto out; |
| 190 | } |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 191 | if (nbd_write(client->ioc, msg, len, errp) < 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 192 | error_prepend(errp, "write failed (error message): "); |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 193 | ret = -EIO; |
| 194 | } else { |
| 195 | ret = 0; |
| 196 | } |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 197 | |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 198 | out: |
| 199 | g_free(msg); |
| 200 | return ret; |
| 201 | } |
| 202 | |
Eric Blake | 41f5dfa | 2018-01-10 17:08:23 -0600 | [diff] [blame] | 203 | /* Send an error reply. |
| 204 | * Return -errno on error, 0 on success. */ |
| 205 | static int GCC_FMT_ATTR(4, 5) |
| 206 | nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type, |
| 207 | Error **errp, const char *fmt, ...) |
| 208 | { |
| 209 | va_list va; |
| 210 | int ret; |
| 211 | |
| 212 | va_start(va, fmt); |
| 213 | ret = nbd_negotiate_send_rep_verr(client, type, errp, fmt, va); |
| 214 | va_end(va); |
| 215 | return ret; |
| 216 | } |
| 217 | |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 218 | /* Drop remainder of the current option, and send a reply with the |
| 219 | * given error type and message. Return -errno on read or write |
| 220 | * failure; or 0 if connection is still live. */ |
| 221 | static int GCC_FMT_ATTR(4, 5) |
| 222 | nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp, |
| 223 | const char *fmt, ...) |
| 224 | { |
| 225 | int ret = nbd_drop(client->ioc, client->optlen, errp); |
| 226 | va_list va; |
| 227 | |
| 228 | client->optlen = 0; |
| 229 | if (!ret) { |
| 230 | va_start(va, fmt); |
| 231 | ret = nbd_negotiate_send_rep_verr(client, type, errp, fmt, va); |
| 232 | va_end(va); |
| 233 | } |
| 234 | return ret; |
| 235 | } |
| 236 | |
| 237 | /* Read size bytes from the unparsed payload of the current option. |
| 238 | * Return -errno on I/O error, 0 if option was completely handled by |
| 239 | * sending a reply about inconsistent lengths, or 1 on success. */ |
| 240 | static int nbd_opt_read(NBDClient *client, void *buffer, size_t size, |
| 241 | Error **errp) |
| 242 | { |
| 243 | if (size > client->optlen) { |
| 244 | return nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp, |
| 245 | "Inconsistent lengths in option %s", |
| 246 | nbd_opt_lookup(client->opt)); |
| 247 | } |
| 248 | client->optlen -= size; |
| 249 | return qio_channel_read_all(client->ioc, buffer, size, errp) < 0 ? -EIO : 1; |
| 250 | } |
| 251 | |
Eric Blake | 526e5c6 | 2016-10-14 13:33:08 -0500 | [diff] [blame] | 252 | /* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload. |
| 253 | * Return -errno on error, 0 on success. */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 254 | static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp, |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 255 | Error **errp) |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 256 | { |
Eric Blake | b1a75b3 | 2016-10-14 13:33:03 -0500 | [diff] [blame] | 257 | size_t name_len, desc_len; |
Eric Blake | 526e5c6 | 2016-10-14 13:33:08 -0500 | [diff] [blame] | 258 | uint32_t len; |
Eric Blake | b1a75b3 | 2016-10-14 13:33:03 -0500 | [diff] [blame] | 259 | const char *name = exp->name ? exp->name : ""; |
| 260 | const char *desc = exp->description ? exp->description : ""; |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 261 | QIOChannel *ioc = client->ioc; |
Vladimir Sementsov-Ogievskiy | 2e5c9ad | 2017-06-02 18:01:49 +0300 | [diff] [blame] | 262 | int ret; |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 263 | |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 264 | trace_nbd_negotiate_send_rep_list(name, desc); |
Eric Blake | b1a75b3 | 2016-10-14 13:33:03 -0500 | [diff] [blame] | 265 | name_len = strlen(name); |
| 266 | desc_len = strlen(desc); |
Eric Blake | 526e5c6 | 2016-10-14 13:33:08 -0500 | [diff] [blame] | 267 | len = name_len + desc_len + sizeof(len); |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 268 | ret = nbd_negotiate_send_rep_len(client, NBD_REP_SERVER, len, errp); |
Vladimir Sementsov-Ogievskiy | 2e5c9ad | 2017-06-02 18:01:49 +0300 | [diff] [blame] | 269 | if (ret < 0) { |
| 270 | return ret; |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 271 | } |
Eric Blake | 526e5c6 | 2016-10-14 13:33:08 -0500 | [diff] [blame] | 272 | |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 273 | len = cpu_to_be32(name_len); |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 274 | if (nbd_write(ioc, &len, sizeof(len), errp) < 0) { |
| 275 | error_prepend(errp, "write failed (name length): "); |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 276 | return -EINVAL; |
| 277 | } |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 278 | |
| 279 | if (nbd_write(ioc, name, name_len, errp) < 0) { |
| 280 | error_prepend(errp, "write failed (name buffer): "); |
Eric Blake | b1a75b3 | 2016-10-14 13:33:03 -0500 | [diff] [blame] | 281 | return -EINVAL; |
| 282 | } |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 283 | |
| 284 | if (nbd_write(ioc, desc, desc_len, errp) < 0) { |
| 285 | error_prepend(errp, "write failed (description buffer): "); |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 286 | return -EINVAL; |
| 287 | } |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 288 | |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 289 | return 0; |
| 290 | } |
| 291 | |
Eric Blake | 526e5c6 | 2016-10-14 13:33:08 -0500 | [diff] [blame] | 292 | /* Process the NBD_OPT_LIST command, with a potential series of replies. |
| 293 | * Return -errno on error, 0 on success. */ |
Eric Blake | e68c35c | 2017-10-27 12:40:31 +0200 | [diff] [blame] | 294 | static int nbd_negotiate_handle_list(NBDClient *client, Error **errp) |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 295 | { |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 296 | NBDExport *exp; |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 297 | assert(client->opt == NBD_OPT_LIST); |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 298 | |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 299 | /* For each export, send a NBD_REP_SERVER reply. */ |
| 300 | QTAILQ_FOREACH(exp, &exports, next) { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 301 | if (nbd_negotiate_send_rep_list(client, exp, errp)) { |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 302 | return -EINVAL; |
| 303 | } |
| 304 | } |
| 305 | /* Finish with a NBD_REP_ACK. */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 306 | return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 307 | } |
| 308 | |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 309 | /* Send a reply to NBD_OPT_EXPORT_NAME. |
| 310 | * Return -errno on error, 0 on success. */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 311 | static int nbd_negotiate_handle_export_name(NBDClient *client, |
Eric Blake | 23e099c | 2017-07-07 15:30:45 -0500 | [diff] [blame] | 312 | uint16_t myflags, bool no_zeroes, |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 313 | Error **errp) |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 314 | { |
Eric Blake | 943cec8 | 2016-05-11 16:39:44 -0600 | [diff] [blame] | 315 | char name[NBD_MAX_NAME_SIZE + 1]; |
Eric Blake | 5f66d06 | 2017-07-17 14:26:35 -0500 | [diff] [blame] | 316 | char buf[NBD_REPLY_EXPORT_NAME_SIZE] = ""; |
Eric Blake | 23e099c | 2017-07-07 15:30:45 -0500 | [diff] [blame] | 317 | size_t len; |
| 318 | int ret; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 319 | |
| 320 | /* Client sends: |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 321 | [20 .. xx] export name (length bytes) |
Eric Blake | 5f66d06 | 2017-07-17 14:26:35 -0500 | [diff] [blame] | 322 | Server replies: |
| 323 | [ 0 .. 7] size |
| 324 | [ 8 .. 9] export flags |
| 325 | [10 .. 133] reserved (0) [unless no_zeroes] |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 326 | */ |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 327 | trace_nbd_negotiate_handle_export_name(); |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 328 | if (client->optlen >= sizeof(name)) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 329 | error_setg(errp, "Bad length received"); |
Vladimir Sementsov-Ogievskiy | d9faeed | 2017-06-02 18:01:48 +0300 | [diff] [blame] | 330 | return -EINVAL; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 331 | } |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 332 | if (nbd_read(client->ioc, name, client->optlen, errp) < 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 333 | error_prepend(errp, "read failed: "); |
Eric Blake | 32f158a | 2018-01-10 17:08:22 -0600 | [diff] [blame] | 334 | return -EIO; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 335 | } |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 336 | name[client->optlen] = '\0'; |
| 337 | client->optlen = 0; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 338 | |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 339 | trace_nbd_negotiate_handle_export_name_request(name); |
Daniel P. Berrange | 9344e5f | 2016-02-10 18:41:09 +0000 | [diff] [blame] | 340 | |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 341 | client->exp = nbd_export_find(name); |
| 342 | if (!client->exp) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 343 | error_setg(errp, "export not found"); |
Vladimir Sementsov-Ogievskiy | d9faeed | 2017-06-02 18:01:48 +0300 | [diff] [blame] | 344 | return -EINVAL; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 345 | } |
| 346 | |
Eric Blake | 23e099c | 2017-07-07 15:30:45 -0500 | [diff] [blame] | 347 | trace_nbd_negotiate_new_style_size_flags(client->exp->size, |
| 348 | client->exp->nbdflags | myflags); |
| 349 | stq_be_p(buf, client->exp->size); |
| 350 | stw_be_p(buf + 8, client->exp->nbdflags | myflags); |
| 351 | len = no_zeroes ? 10 : sizeof(buf); |
| 352 | ret = nbd_write(client->ioc, buf, len, errp); |
| 353 | if (ret < 0) { |
| 354 | error_prepend(errp, "write failed: "); |
| 355 | return ret; |
| 356 | } |
| 357 | |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 358 | QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); |
| 359 | nbd_export_get(client->exp); |
Vladimir Sementsov-Ogievskiy | d9faeed | 2017-06-02 18:01:48 +0300 | [diff] [blame] | 360 | |
| 361 | return 0; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 362 | } |
| 363 | |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 364 | /* Send a single NBD_REP_INFO, with a buffer @buf of @length bytes. |
| 365 | * The buffer does NOT include the info type prefix. |
| 366 | * Return -errno on error, 0 if ready to send more. */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 367 | static int nbd_negotiate_send_info(NBDClient *client, |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 368 | uint16_t info, uint32_t length, void *buf, |
| 369 | Error **errp) |
| 370 | { |
| 371 | int rc; |
| 372 | |
| 373 | trace_nbd_negotiate_send_info(info, nbd_info_lookup(info), length); |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 374 | rc = nbd_negotiate_send_rep_len(client, NBD_REP_INFO, |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 375 | sizeof(info) + length, errp); |
| 376 | if (rc < 0) { |
| 377 | return rc; |
| 378 | } |
| 379 | cpu_to_be16s(&info); |
| 380 | if (nbd_write(client->ioc, &info, sizeof(info), errp) < 0) { |
| 381 | return -EIO; |
| 382 | } |
| 383 | if (nbd_write(client->ioc, buf, length, errp) < 0) { |
| 384 | return -EIO; |
| 385 | } |
| 386 | return 0; |
| 387 | } |
| 388 | |
Eric Blake | a16a790 | 2018-01-10 17:08:20 -0600 | [diff] [blame] | 389 | /* nbd_reject_length: Handle any unexpected payload. |
| 390 | * @fatal requests that we quit talking to the client, even if we are able |
| 391 | * to successfully send an error reply. |
| 392 | * Return: |
| 393 | * -errno transmission error occurred or @fatal was requested, errp is set |
| 394 | * 0 error message successfully sent to client, errp is not set |
| 395 | */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 396 | static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp) |
Eric Blake | a16a790 | 2018-01-10 17:08:20 -0600 | [diff] [blame] | 397 | { |
| 398 | int ret; |
| 399 | |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 400 | assert(client->optlen); |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 401 | ret = nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp, |
| 402 | "option '%s' has unexpected length", |
| 403 | nbd_opt_lookup(client->opt)); |
Eric Blake | a16a790 | 2018-01-10 17:08:20 -0600 | [diff] [blame] | 404 | if (fatal && !ret) { |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 405 | error_setg(errp, "option '%s' has unexpected length", |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 406 | nbd_opt_lookup(client->opt)); |
Eric Blake | a16a790 | 2018-01-10 17:08:20 -0600 | [diff] [blame] | 407 | return -EINVAL; |
| 408 | } |
| 409 | return ret; |
| 410 | } |
| 411 | |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 412 | /* Handle NBD_OPT_INFO and NBD_OPT_GO. |
| 413 | * Return -errno on error, 0 if ready for next option, and 1 to move |
| 414 | * into transmission phase. */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 415 | static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 416 | Error **errp) |
| 417 | { |
| 418 | int rc; |
| 419 | char name[NBD_MAX_NAME_SIZE + 1]; |
| 420 | NBDExport *exp; |
| 421 | uint16_t requests; |
| 422 | uint16_t request; |
| 423 | uint32_t namelen; |
| 424 | bool sendname = false; |
Eric Blake | 0c1d50b | 2017-07-07 15:30:48 -0500 | [diff] [blame] | 425 | bool blocksize = false; |
| 426 | uint32_t sizes[3]; |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 427 | char buf[sizeof(uint64_t) + sizeof(uint16_t)]; |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 428 | |
| 429 | /* Client sends: |
| 430 | 4 bytes: L, name length (can be 0) |
| 431 | L bytes: export name |
| 432 | 2 bytes: N, number of requests (can be 0) |
| 433 | N * 2 bytes: N requests |
| 434 | */ |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 435 | rc = nbd_opt_read(client, &namelen, sizeof(namelen), errp); |
| 436 | if (rc <= 0) { |
| 437 | return rc; |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 438 | } |
| 439 | be32_to_cpus(&namelen); |
Eric Blake | 51ae4f8 | 2017-11-22 15:07:22 -0600 | [diff] [blame] | 440 | if (namelen >= sizeof(name)) { |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 441 | return nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp, |
| 442 | "name too long for qemu"); |
Eric Blake | 51ae4f8 | 2017-11-22 15:07:22 -0600 | [diff] [blame] | 443 | } |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 444 | rc = nbd_opt_read(client, name, namelen, errp); |
| 445 | if (rc <= 0) { |
| 446 | return rc; |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 447 | } |
| 448 | name[namelen] = '\0'; |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 449 | trace_nbd_negotiate_handle_export_name_request(name); |
| 450 | |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 451 | rc = nbd_opt_read(client, &requests, sizeof(requests), errp); |
| 452 | if (rc <= 0) { |
| 453 | return rc; |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 454 | } |
| 455 | be16_to_cpus(&requests); |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 456 | trace_nbd_negotiate_handle_info_requests(requests); |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 457 | while (requests--) { |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 458 | rc = nbd_opt_read(client, &request, sizeof(request), errp); |
| 459 | if (rc <= 0) { |
| 460 | return rc; |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 461 | } |
| 462 | be16_to_cpus(&request); |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 463 | trace_nbd_negotiate_handle_info_request(request, |
| 464 | nbd_info_lookup(request)); |
Eric Blake | 0c1d50b | 2017-07-07 15:30:48 -0500 | [diff] [blame] | 465 | /* We care about NBD_INFO_NAME and NBD_INFO_BLOCK_SIZE; |
| 466 | * everything else is either a request we don't know or |
| 467 | * something we send regardless of request */ |
| 468 | switch (request) { |
| 469 | case NBD_INFO_NAME: |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 470 | sendname = true; |
Eric Blake | 0c1d50b | 2017-07-07 15:30:48 -0500 | [diff] [blame] | 471 | break; |
| 472 | case NBD_INFO_BLOCK_SIZE: |
| 473 | blocksize = true; |
| 474 | break; |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 475 | } |
| 476 | } |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 477 | if (client->optlen) { |
| 478 | return nbd_reject_length(client, false, errp); |
| 479 | } |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 480 | |
| 481 | exp = nbd_export_find(name); |
| 482 | if (!exp) { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 483 | return nbd_negotiate_send_rep_err(client, NBD_REP_ERR_UNKNOWN, |
| 484 | errp, "export '%s' not present", |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 485 | name); |
| 486 | } |
| 487 | |
| 488 | /* Don't bother sending NBD_INFO_NAME unless client requested it */ |
| 489 | if (sendname) { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 490 | rc = nbd_negotiate_send_info(client, NBD_INFO_NAME, namelen, name, |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 491 | errp); |
| 492 | if (rc < 0) { |
| 493 | return rc; |
| 494 | } |
| 495 | } |
| 496 | |
| 497 | /* Send NBD_INFO_DESCRIPTION only if available, regardless of |
| 498 | * client request */ |
| 499 | if (exp->description) { |
| 500 | size_t len = strlen(exp->description); |
| 501 | |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 502 | rc = nbd_negotiate_send_info(client, NBD_INFO_DESCRIPTION, |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 503 | len, exp->description, errp); |
| 504 | if (rc < 0) { |
| 505 | return rc; |
| 506 | } |
| 507 | } |
| 508 | |
Eric Blake | 0c1d50b | 2017-07-07 15:30:48 -0500 | [diff] [blame] | 509 | /* Send NBD_INFO_BLOCK_SIZE always, but tweak the minimum size |
| 510 | * according to whether the client requested it, and according to |
| 511 | * whether this is OPT_INFO or OPT_GO. */ |
| 512 | /* minimum - 1 for back-compat, or 512 if client is new enough. |
| 513 | * TODO: consult blk_bs(blk)->bl.request_alignment? */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 514 | sizes[0] = |
| 515 | (client->opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1; |
Eric Blake | 0c1d50b | 2017-07-07 15:30:48 -0500 | [diff] [blame] | 516 | /* preferred - Hard-code to 4096 for now. |
| 517 | * TODO: is blk_bs(blk)->bl.opt_transfer appropriate? */ |
| 518 | sizes[1] = 4096; |
| 519 | /* maximum - At most 32M, but smaller as appropriate. */ |
| 520 | sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE); |
| 521 | trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]); |
| 522 | cpu_to_be32s(&sizes[0]); |
| 523 | cpu_to_be32s(&sizes[1]); |
| 524 | cpu_to_be32s(&sizes[2]); |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 525 | rc = nbd_negotiate_send_info(client, NBD_INFO_BLOCK_SIZE, |
Eric Blake | 0c1d50b | 2017-07-07 15:30:48 -0500 | [diff] [blame] | 526 | sizeof(sizes), sizes, errp); |
| 527 | if (rc < 0) { |
| 528 | return rc; |
| 529 | } |
| 530 | |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 531 | /* Send NBD_INFO_EXPORT always */ |
| 532 | trace_nbd_negotiate_new_style_size_flags(exp->size, |
| 533 | exp->nbdflags | myflags); |
| 534 | stq_be_p(buf, exp->size); |
| 535 | stw_be_p(buf + 8, exp->nbdflags | myflags); |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 536 | rc = nbd_negotiate_send_info(client, NBD_INFO_EXPORT, |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 537 | sizeof(buf), buf, errp); |
| 538 | if (rc < 0) { |
| 539 | return rc; |
| 540 | } |
| 541 | |
Eric Blake | 0c1d50b | 2017-07-07 15:30:48 -0500 | [diff] [blame] | 542 | /* If the client is just asking for NBD_OPT_INFO, but forgot to |
| 543 | * request block sizes, return an error. |
| 544 | * TODO: consult blk_bs(blk)->request_align, and only error if it |
| 545 | * is not 1? */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 546 | if (client->opt == NBD_OPT_INFO && !blocksize) { |
| 547 | return nbd_negotiate_send_rep_err(client, |
| 548 | NBD_REP_ERR_BLOCK_SIZE_REQD, |
Eric Blake | 0c1d50b | 2017-07-07 15:30:48 -0500 | [diff] [blame] | 549 | errp, |
| 550 | "request NBD_INFO_BLOCK_SIZE to " |
| 551 | "use this export"); |
| 552 | } |
| 553 | |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 554 | /* Final reply */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 555 | rc = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 556 | if (rc < 0) { |
| 557 | return rc; |
| 558 | } |
| 559 | |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 560 | if (client->opt == NBD_OPT_GO) { |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 561 | client->exp = exp; |
| 562 | QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); |
| 563 | nbd_export_get(client->exp); |
| 564 | rc = 1; |
| 565 | } |
| 566 | return rc; |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 567 | } |
| 568 | |
| 569 | |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 570 | /* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the |
| 571 | * new channel for all further (now-encrypted) communication. */ |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 572 | static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 573 | Error **errp) |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 574 | { |
| 575 | QIOChannel *ioc; |
| 576 | QIOChannelTLS *tioc; |
| 577 | struct NBDTLSHandshakeData data = { 0 }; |
| 578 | |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 579 | assert(client->opt == NBD_OPT_STARTTLS); |
| 580 | |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 581 | trace_nbd_negotiate_handle_starttls(); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 582 | ioc = client->ioc; |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 583 | |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 584 | if (nbd_negotiate_send_rep(client, NBD_REP_ACK, errp) < 0) { |
Eric Blake | 63d5ef8 | 2016-05-11 16:39:36 -0600 | [diff] [blame] | 585 | return NULL; |
| 586 | } |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 587 | |
| 588 | tioc = qio_channel_tls_new_server(ioc, |
| 589 | client->tlscreds, |
| 590 | client->tlsaclname, |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 591 | errp); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 592 | if (!tioc) { |
| 593 | return NULL; |
| 594 | } |
| 595 | |
Daniel P. Berrange | 0d73f72 | 2016-09-30 11:57:14 +0100 | [diff] [blame] | 596 | qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-server-tls"); |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 597 | trace_nbd_negotiate_handle_starttls_handshake(); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 598 | data.loop = g_main_loop_new(g_main_context_default(), FALSE); |
| 599 | qio_channel_tls_handshake(tioc, |
| 600 | nbd_tls_handshake, |
| 601 | &data, |
| 602 | NULL); |
| 603 | |
| 604 | if (!data.complete) { |
| 605 | g_main_loop_run(data.loop); |
| 606 | } |
| 607 | g_main_loop_unref(data.loop); |
| 608 | if (data.error) { |
| 609 | object_unref(OBJECT(tioc)); |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 610 | error_propagate(errp, data.error); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 611 | return NULL; |
| 612 | } |
| 613 | |
| 614 | return QIO_CHANNEL(tioc); |
| 615 | } |
| 616 | |
Vladimir Sementsov-Ogievskiy | 1e120ff | 2017-07-07 18:29:09 +0300 | [diff] [blame] | 617 | /* nbd_negotiate_options |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 618 | * Process all NBD_OPT_* client option commands, during fixed newstyle |
| 619 | * negotiation. |
Vladimir Sementsov-Ogievskiy | 1e120ff | 2017-07-07 18:29:09 +0300 | [diff] [blame] | 620 | * Return: |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 621 | * -errno on error, errp is set |
| 622 | * 0 on successful negotiation, errp is not set |
| 623 | * 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect, |
| 624 | * errp is not set |
Vladimir Sementsov-Ogievskiy | 1e120ff | 2017-07-07 18:29:09 +0300 | [diff] [blame] | 625 | */ |
Eric Blake | 23e099c | 2017-07-07 15:30:45 -0500 | [diff] [blame] | 626 | static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, |
| 627 | Error **errp) |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 628 | { |
Max Reitz | 9c122ad | 2015-02-25 13:08:31 -0500 | [diff] [blame] | 629 | uint32_t flags; |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 630 | bool fixedNewstyle = false; |
Eric Blake | 23e099c | 2017-07-07 15:30:45 -0500 | [diff] [blame] | 631 | bool no_zeroes = false; |
Max Reitz | 9c122ad | 2015-02-25 13:08:31 -0500 | [diff] [blame] | 632 | |
| 633 | /* Client sends: |
| 634 | [ 0 .. 3] client flags |
| 635 | |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 636 | Then we loop until NBD_OPT_EXPORT_NAME or NBD_OPT_GO: |
Max Reitz | 9c122ad | 2015-02-25 13:08:31 -0500 | [diff] [blame] | 637 | [ 0 .. 7] NBD_OPTS_MAGIC |
| 638 | [ 8 .. 11] NBD option |
| 639 | [12 .. 15] Data length |
| 640 | ... Rest of request |
| 641 | |
| 642 | [ 0 .. 7] NBD_OPTS_MAGIC |
| 643 | [ 8 .. 11] Second NBD option |
| 644 | [12 .. 15] Data length |
| 645 | ... Rest of request |
| 646 | */ |
| 647 | |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 648 | if (nbd_read(client->ioc, &flags, sizeof(flags), errp) < 0) { |
| 649 | error_prepend(errp, "read failed: "); |
Max Reitz | 9c122ad | 2015-02-25 13:08:31 -0500 | [diff] [blame] | 650 | return -EIO; |
| 651 | } |
Max Reitz | 9c122ad | 2015-02-25 13:08:31 -0500 | [diff] [blame] | 652 | be32_to_cpus(&flags); |
Eric Blake | 621c4f4 | 2017-07-07 15:30:44 -0500 | [diff] [blame] | 653 | trace_nbd_negotiate_options_flags(flags); |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 654 | if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 655 | fixedNewstyle = true; |
| 656 | flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE; |
| 657 | } |
Eric Blake | c203c59 | 2016-10-14 13:33:14 -0500 | [diff] [blame] | 658 | if (flags & NBD_FLAG_C_NO_ZEROES) { |
Eric Blake | 23e099c | 2017-07-07 15:30:45 -0500 | [diff] [blame] | 659 | no_zeroes = true; |
Eric Blake | c203c59 | 2016-10-14 13:33:14 -0500 | [diff] [blame] | 660 | flags &= ~NBD_FLAG_C_NO_ZEROES; |
| 661 | } |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 662 | if (flags != 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 663 | error_setg(errp, "Unknown client flags 0x%" PRIx32 " received", flags); |
Eric Blake | 621c4f4 | 2017-07-07 15:30:44 -0500 | [diff] [blame] | 664 | return -EINVAL; |
Max Reitz | 9c122ad | 2015-02-25 13:08:31 -0500 | [diff] [blame] | 665 | } |
| 666 | |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 667 | while (1) { |
Max Reitz | 9c122ad | 2015-02-25 13:08:31 -0500 | [diff] [blame] | 668 | int ret; |
Vladimir Sementsov-Ogievskiy | 7f9039c | 2017-07-07 18:29:16 +0300 | [diff] [blame] | 669 | uint32_t option, length; |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 670 | uint64_t magic; |
| 671 | |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 672 | if (nbd_read(client->ioc, &magic, sizeof(magic), errp) < 0) { |
| 673 | error_prepend(errp, "read failed: "); |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 674 | return -EINVAL; |
| 675 | } |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 676 | magic = be64_to_cpu(magic); |
| 677 | trace_nbd_negotiate_options_check_magic(magic); |
| 678 | if (magic != NBD_OPTS_MAGIC) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 679 | error_setg(errp, "Bad magic received"); |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 680 | return -EINVAL; |
| 681 | } |
| 682 | |
Vladimir Sementsov-Ogievskiy | 7f9039c | 2017-07-07 18:29:16 +0300 | [diff] [blame] | 683 | if (nbd_read(client->ioc, &option, |
| 684 | sizeof(option), errp) < 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 685 | error_prepend(errp, "read failed: "); |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 686 | return -EINVAL; |
| 687 | } |
Vladimir Sementsov-Ogievskiy | 7f9039c | 2017-07-07 18:29:16 +0300 | [diff] [blame] | 688 | option = be32_to_cpu(option); |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 689 | client->opt = option; |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 690 | |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 691 | if (nbd_read(client->ioc, &length, sizeof(length), errp) < 0) { |
| 692 | error_prepend(errp, "read failed: "); |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 693 | return -EINVAL; |
| 694 | } |
| 695 | length = be32_to_cpu(length); |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 696 | assert(!client->optlen); |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 697 | client->optlen = length; |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 698 | |
Eric Blake | fdad35e | 2017-11-22 16:25:16 -0600 | [diff] [blame] | 699 | if (length > NBD_MAX_BUFFER_SIZE) { |
| 700 | error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)", |
| 701 | length, NBD_MAX_BUFFER_SIZE); |
| 702 | return -EINVAL; |
| 703 | } |
| 704 | |
Eric Blake | 3736cc5 | 2017-07-07 15:30:43 -0500 | [diff] [blame] | 705 | trace_nbd_negotiate_options_check_option(option, |
| 706 | nbd_opt_lookup(option)); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 707 | if (client->tlscreds && |
| 708 | client->ioc == (QIOChannel *)client->sioc) { |
| 709 | QIOChannel *tioc; |
| 710 | if (!fixedNewstyle) { |
Vladimir Sementsov-Ogievskiy | 7f9039c | 2017-07-07 18:29:16 +0300 | [diff] [blame] | 711 | error_setg(errp, "Unsupported option 0x%" PRIx32, option); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 712 | return -EINVAL; |
| 713 | } |
Vladimir Sementsov-Ogievskiy | 7f9039c | 2017-07-07 18:29:16 +0300 | [diff] [blame] | 714 | switch (option) { |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 715 | case NBD_OPT_STARTTLS: |
Eric Blake | e68c35c | 2017-10-27 12:40:31 +0200 | [diff] [blame] | 716 | if (length) { |
| 717 | /* Unconditionally drop the connection if the client |
| 718 | * can't start a TLS negotiation correctly */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 719 | return nbd_reject_length(client, true, errp); |
Eric Blake | e68c35c | 2017-10-27 12:40:31 +0200 | [diff] [blame] | 720 | } |
| 721 | tioc = nbd_negotiate_handle_starttls(client, errp); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 722 | if (!tioc) { |
| 723 | return -EIO; |
| 724 | } |
Eric Blake | 8cbee49 | 2017-10-27 12:40:30 +0200 | [diff] [blame] | 725 | ret = 0; |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 726 | object_unref(OBJECT(client->ioc)); |
| 727 | client->ioc = QIO_CHANNEL(tioc); |
| 728 | break; |
| 729 | |
Eric Blake | d1129a8 | 2016-04-14 16:02:23 -0600 | [diff] [blame] | 730 | case NBD_OPT_EXPORT_NAME: |
| 731 | /* No way to return an error to client, so drop connection */ |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 732 | error_setg(errp, "Option 0x%x not permitted before TLS", |
Vladimir Sementsov-Ogievskiy | 7f9039c | 2017-07-07 18:29:16 +0300 | [diff] [blame] | 733 | option); |
Eric Blake | d1129a8 | 2016-04-14 16:02:23 -0600 | [diff] [blame] | 734 | return -EINVAL; |
| 735 | |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 736 | default: |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 737 | ret = nbd_opt_drop(client, NBD_REP_ERR_TLS_REQD, errp, |
| 738 | "Option 0x%" PRIx32 |
| 739 | "not permitted before TLS", option); |
Eric Blake | 37ec36f | 2017-07-07 15:30:42 -0500 | [diff] [blame] | 740 | /* Let the client keep trying, unless they asked to |
| 741 | * quit. In this mode, we've already sent an error, so |
| 742 | * we can't ack the abort. */ |
Vladimir Sementsov-Ogievskiy | 7f9039c | 2017-07-07 18:29:16 +0300 | [diff] [blame] | 743 | if (option == NBD_OPT_ABORT) { |
Vladimir Sementsov-Ogievskiy | 1e120ff | 2017-07-07 18:29:09 +0300 | [diff] [blame] | 744 | return 1; |
Eric Blake | b6f5d3b | 2016-10-14 13:33:16 -0500 | [diff] [blame] | 745 | } |
Eric Blake | d1129a8 | 2016-04-14 16:02:23 -0600 | [diff] [blame] | 746 | break; |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 747 | } |
| 748 | } else if (fixedNewstyle) { |
Vladimir Sementsov-Ogievskiy | 7f9039c | 2017-07-07 18:29:16 +0300 | [diff] [blame] | 749 | switch (option) { |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 750 | case NBD_OPT_LIST: |
Eric Blake | e68c35c | 2017-10-27 12:40:31 +0200 | [diff] [blame] | 751 | if (length) { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 752 | ret = nbd_reject_length(client, false, errp); |
Eric Blake | e68c35c | 2017-10-27 12:40:31 +0200 | [diff] [blame] | 753 | } else { |
| 754 | ret = nbd_negotiate_handle_list(client, errp); |
| 755 | } |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 756 | break; |
| 757 | |
| 758 | case NBD_OPT_ABORT: |
Eric Blake | b6f5d3b | 2016-10-14 13:33:16 -0500 | [diff] [blame] | 759 | /* NBD spec says we must try to reply before |
| 760 | * disconnecting, but that we must also tolerate |
| 761 | * guests that don't wait for our reply. */ |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 762 | nbd_negotiate_send_rep(client, NBD_REP_ACK, NULL); |
Vladimir Sementsov-Ogievskiy | 1e120ff | 2017-07-07 18:29:09 +0300 | [diff] [blame] | 763 | return 1; |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 764 | |
| 765 | case NBD_OPT_EXPORT_NAME: |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 766 | return nbd_negotiate_handle_export_name(client, |
Eric Blake | 23e099c | 2017-07-07 15:30:45 -0500 | [diff] [blame] | 767 | myflags, no_zeroes, |
| 768 | errp); |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 769 | |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 770 | case NBD_OPT_INFO: |
| 771 | case NBD_OPT_GO: |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 772 | ret = nbd_negotiate_handle_info(client, myflags, errp); |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 773 | if (ret == 1) { |
| 774 | assert(option == NBD_OPT_GO); |
| 775 | return 0; |
| 776 | } |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 777 | break; |
| 778 | |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 779 | case NBD_OPT_STARTTLS: |
Eric Blake | e68c35c | 2017-10-27 12:40:31 +0200 | [diff] [blame] | 780 | if (length) { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 781 | ret = nbd_reject_length(client, false, errp); |
Eric Blake | e68c35c | 2017-10-27 12:40:31 +0200 | [diff] [blame] | 782 | } else if (client->tlscreds) { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 783 | ret = nbd_negotiate_send_rep_err(client, |
| 784 | NBD_REP_ERR_INVALID, errp, |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 785 | "TLS already enabled"); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 786 | } else { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 787 | ret = nbd_negotiate_send_rep_err(client, |
| 788 | NBD_REP_ERR_POLICY, errp, |
Eric Blake | 3668328 | 2016-10-14 13:33:09 -0500 | [diff] [blame] | 789 | "TLS not configured"); |
Eric Blake | 63d5ef8 | 2016-05-11 16:39:36 -0600 | [diff] [blame] | 790 | } |
Eric Blake | d1129a8 | 2016-04-14 16:02:23 -0600 | [diff] [blame] | 791 | break; |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 792 | |
| 793 | case NBD_OPT_STRUCTURED_REPLY: |
| 794 | if (length) { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 795 | ret = nbd_reject_length(client, false, errp); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 796 | } else if (client->structured_reply) { |
| 797 | ret = nbd_negotiate_send_rep_err( |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 798 | client, NBD_REP_ERR_INVALID, errp, |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 799 | "structured reply already negotiated"); |
| 800 | } else { |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 801 | ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 802 | client->structured_reply = true; |
| 803 | myflags |= NBD_FLAG_SEND_DF; |
| 804 | } |
| 805 | break; |
| 806 | |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 807 | default: |
Eric Blake | 894e028 | 2018-01-10 17:08:24 -0600 | [diff] [blame] | 808 | ret = nbd_opt_drop(client, NBD_REP_ERR_UNSUP, errp, |
| 809 | "Unsupported option 0x%" PRIx32 " (%s)", |
| 810 | option, nbd_opt_lookup(option)); |
Eric Blake | 156f6a1 | 2016-04-06 16:48:38 -0600 | [diff] [blame] | 811 | break; |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 812 | } |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 813 | } else { |
| 814 | /* |
| 815 | * If broken new-style we should drop the connection |
| 816 | * for anything except NBD_OPT_EXPORT_NAME |
| 817 | */ |
Vladimir Sementsov-Ogievskiy | 7f9039c | 2017-07-07 18:29:16 +0300 | [diff] [blame] | 818 | switch (option) { |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 819 | case NBD_OPT_EXPORT_NAME: |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 820 | return nbd_negotiate_handle_export_name(client, |
Eric Blake | 23e099c | 2017-07-07 15:30:45 -0500 | [diff] [blame] | 821 | myflags, no_zeroes, |
| 822 | errp); |
Hani Benhabiles | 32d7d2e | 2014-06-07 01:32:32 +0100 | [diff] [blame] | 823 | |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 824 | default: |
Eric Blake | 3736cc5 | 2017-07-07 15:30:43 -0500 | [diff] [blame] | 825 | error_setg(errp, "Unsupported option 0x%" PRIx32 " (%s)", |
| 826 | option, nbd_opt_lookup(option)); |
Daniel P. Berrange | 26afa86 | 2016-02-10 18:41:06 +0000 | [diff] [blame] | 827 | return -EINVAL; |
| 828 | } |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 829 | } |
Eric Blake | 8cbee49 | 2017-10-27 12:40:30 +0200 | [diff] [blame] | 830 | if (ret < 0) { |
| 831 | return ret; |
| 832 | } |
Hani Benhabiles | f5076b5 | 2014-06-07 01:32:31 +0100 | [diff] [blame] | 833 | } |
| 834 | } |
| 835 | |
Vladimir Sementsov-Ogievskiy | 1e120ff | 2017-07-07 18:29:09 +0300 | [diff] [blame] | 836 | /* nbd_negotiate |
| 837 | * Return: |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 838 | * -errno on error, errp is set |
| 839 | * 0 on successful negotiation, errp is not set |
| 840 | * 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect, |
| 841 | * errp is not set |
Vladimir Sementsov-Ogievskiy | 1e120ff | 2017-07-07 18:29:09 +0300 | [diff] [blame] | 842 | */ |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 843 | static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 844 | { |
Eric Blake | 5f66d06 | 2017-07-17 14:26:35 -0500 | [diff] [blame] | 845 | char buf[NBD_OLDSTYLE_NEGOTIATE_SIZE] = ""; |
Vladimir Sementsov-Ogievskiy | 2e5c9ad | 2017-06-02 18:01:49 +0300 | [diff] [blame] | 846 | int ret; |
Eric Blake | 7423f41 | 2016-07-21 13:34:46 -0600 | [diff] [blame] | 847 | const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM | |
Eric Blake | 1f4d6d1 | 2016-10-14 13:33:17 -0500 | [diff] [blame] | 848 | NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA | |
| 849 | NBD_FLAG_SEND_WRITE_ZEROES); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 850 | bool oldStyle; |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 851 | |
Eric Blake | 5f66d06 | 2017-07-17 14:26:35 -0500 | [diff] [blame] | 852 | /* Old style negotiation header, no room for options |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 853 | [ 0 .. 7] passwd ("NBDMAGIC") |
| 854 | [ 8 .. 15] magic (NBD_CLIENT_MAGIC) |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 855 | [16 .. 23] size |
Eric Blake | 5f66d06 | 2017-07-17 14:26:35 -0500 | [diff] [blame] | 856 | [24 .. 27] export flags (zero-extended) |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 857 | [28 .. 151] reserved (0) |
| 858 | |
Eric Blake | 5f66d06 | 2017-07-17 14:26:35 -0500 | [diff] [blame] | 859 | New style negotiation header, client can send options |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 860 | [ 0 .. 7] passwd ("NBDMAGIC") |
| 861 | [ 8 .. 15] magic (NBD_OPTS_MAGIC) |
| 862 | [16 .. 17] server flags (0) |
Eric Blake | f37708f | 2017-07-07 15:30:46 -0500 | [diff] [blame] | 863 | ....options sent, ending in NBD_OPT_EXPORT_NAME or NBD_OPT_GO.... |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 864 | */ |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 865 | |
Daniel P. Berrange | 1c778ef | 2016-02-10 18:41:04 +0000 | [diff] [blame] | 866 | qio_channel_set_blocking(client->ioc, false, NULL); |
Paolo Bonzini | 185b433 | 2012-03-05 08:56:10 +0100 | [diff] [blame] | 867 | |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 868 | trace_nbd_negotiate_begin(); |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 869 | memcpy(buf, "NBDMAGIC", 8); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 870 | |
| 871 | oldStyle = client->exp != NULL && !client->tlscreds; |
| 872 | if (oldStyle) { |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 873 | trace_nbd_negotiate_old_style(client->exp->size, |
| 874 | client->exp->nbdflags | myflags); |
John Snow | 667ad26 | 2016-02-04 11:27:55 +0100 | [diff] [blame] | 875 | stq_be_p(buf + 8, NBD_CLIENT_MAGIC); |
| 876 | stq_be_p(buf + 16, client->exp->size); |
Eric Blake | 5f66d06 | 2017-07-17 14:26:35 -0500 | [diff] [blame] | 877 | stl_be_p(buf + 24, client->exp->nbdflags | myflags); |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 878 | |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 879 | if (nbd_write(client->ioc, buf, sizeof(buf), errp) < 0) { |
| 880 | error_prepend(errp, "write failed: "); |
Vladimir Sementsov-Ogievskiy | d9faeed | 2017-06-02 18:01:48 +0300 | [diff] [blame] | 881 | return -EINVAL; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 882 | } |
| 883 | } else { |
Vladimir Sementsov-Ogievskiy | 76ff081 | 2017-07-07 18:29:10 +0300 | [diff] [blame] | 884 | stq_be_p(buf + 8, NBD_OPTS_MAGIC); |
| 885 | stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES); |
| 886 | |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 887 | if (nbd_write(client->ioc, buf, 18, errp) < 0) { |
| 888 | error_prepend(errp, "write failed: "); |
Vladimir Sementsov-Ogievskiy | d9faeed | 2017-06-02 18:01:48 +0300 | [diff] [blame] | 889 | return -EINVAL; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 890 | } |
Eric Blake | 23e099c | 2017-07-07 15:30:45 -0500 | [diff] [blame] | 891 | ret = nbd_negotiate_options(client, myflags, errp); |
Vladimir Sementsov-Ogievskiy | 2e5c9ad | 2017-06-02 18:01:49 +0300 | [diff] [blame] | 892 | if (ret != 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 893 | if (ret < 0) { |
| 894 | error_prepend(errp, "option negotiation failed: "); |
| 895 | } |
Vladimir Sementsov-Ogievskiy | 2e5c9ad | 2017-06-02 18:01:49 +0300 | [diff] [blame] | 896 | return ret; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 897 | } |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 898 | } |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 899 | |
Vladimir Sementsov-Ogievskiy | 0cfae92 | 2018-01-10 17:08:21 -0600 | [diff] [blame] | 900 | assert(!client->optlen); |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 901 | trace_nbd_negotiate_success(); |
Vladimir Sementsov-Ogievskiy | d9faeed | 2017-06-02 18:01:48 +0300 | [diff] [blame] | 902 | |
| 903 | return 0; |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 904 | } |
| 905 | |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 906 | static int nbd_receive_request(QIOChannel *ioc, NBDRequest *request, |
| 907 | Error **errp) |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 908 | { |
Paolo Bonzini | fa26c26 | 2012-08-22 15:13:30 +0200 | [diff] [blame] | 909 | uint8_t buf[NBD_REQUEST_SIZE]; |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 910 | uint32_t magic; |
Vladimir Sementsov-Ogievskiy | a0dc63a | 2017-06-02 18:01:42 +0300 | [diff] [blame] | 911 | int ret; |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 912 | |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 913 | ret = nbd_read(ioc, buf, sizeof(buf), errp); |
Paolo Bonzini | 185b433 | 2012-03-05 08:56:10 +0100 | [diff] [blame] | 914 | if (ret < 0) { |
| 915 | return ret; |
| 916 | } |
| 917 | |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 918 | /* Request |
| 919 | [ 0 .. 3] magic (NBD_REQUEST_MAGIC) |
Eric Blake | b626b51 | 2016-10-14 13:33:04 -0500 | [diff] [blame] | 920 | [ 4 .. 5] flags (NBD_CMD_FLAG_FUA, ...) |
| 921 | [ 6 .. 7] type (NBD_CMD_READ, ...) |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 922 | [ 8 .. 15] handle |
| 923 | [16 .. 23] from |
| 924 | [24 .. 27] len |
| 925 | */ |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 926 | |
Peter Maydell | 773dce3 | 2016-06-10 16:00:36 +0100 | [diff] [blame] | 927 | magic = ldl_be_p(buf); |
Eric Blake | b626b51 | 2016-10-14 13:33:04 -0500 | [diff] [blame] | 928 | request->flags = lduw_be_p(buf + 4); |
| 929 | request->type = lduw_be_p(buf + 6); |
Peter Maydell | 773dce3 | 2016-06-10 16:00:36 +0100 | [diff] [blame] | 930 | request->handle = ldq_be_p(buf + 8); |
| 931 | request->from = ldq_be_p(buf + 16); |
| 932 | request->len = ldl_be_p(buf + 24); |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 933 | |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 934 | trace_nbd_receive_request(magic, request->flags, request->type, |
| 935 | request->from, request->len); |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 936 | |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 937 | if (magic != NBD_REQUEST_MAGIC) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 938 | error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", magic); |
Paolo Bonzini | 185b433 | 2012-03-05 08:56:10 +0100 | [diff] [blame] | 939 | return -EINVAL; |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 940 | } |
| 941 | return 0; |
ths | 7581825 | 2008-07-03 13:41:03 +0000 | [diff] [blame] | 942 | } |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 943 | |
Paolo Bonzini | 41996e3 | 2011-09-19 15:25:40 +0200 | [diff] [blame] | 944 | #define MAX_NBD_REQUESTS 16 |
| 945 | |
Paolo Bonzini | ce33967 | 2012-09-18 13:17:52 +0200 | [diff] [blame] | 946 | void nbd_client_get(NBDClient *client) |
Paolo Bonzini | 1743b51 | 2011-09-19 14:33:23 +0200 | [diff] [blame] | 947 | { |
| 948 | client->refcount++; |
| 949 | } |
| 950 | |
Paolo Bonzini | ce33967 | 2012-09-18 13:17:52 +0200 | [diff] [blame] | 951 | void nbd_client_put(NBDClient *client) |
Paolo Bonzini | 1743b51 | 2011-09-19 14:33:23 +0200 | [diff] [blame] | 952 | { |
| 953 | if (--client->refcount == 0) { |
Paolo Bonzini | ff2b68a | 2012-08-22 18:45:12 +0200 | [diff] [blame] | 954 | /* The last reference should be dropped by client->close, |
Max Reitz | f53a829 | 2015-02-06 16:06:16 -0500 | [diff] [blame] | 955 | * which is called by client_close. |
Paolo Bonzini | ff2b68a | 2012-08-22 18:45:12 +0200 | [diff] [blame] | 956 | */ |
| 957 | assert(client->closing); |
| 958 | |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 959 | qio_channel_detach_aio_context(client->ioc); |
Daniel P. Berrange | 1c778ef | 2016-02-10 18:41:04 +0000 | [diff] [blame] | 960 | object_unref(OBJECT(client->sioc)); |
| 961 | object_unref(OBJECT(client->ioc)); |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 962 | if (client->tlscreds) { |
| 963 | object_unref(OBJECT(client->tlscreds)); |
| 964 | } |
| 965 | g_free(client->tlsaclname); |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 966 | if (client->exp) { |
| 967 | QTAILQ_REMOVE(&client->exp->clients, client, next); |
| 968 | nbd_export_put(client->exp); |
| 969 | } |
Paolo Bonzini | 1743b51 | 2011-09-19 14:33:23 +0200 | [diff] [blame] | 970 | g_free(client); |
| 971 | } |
| 972 | } |
| 973 | |
Eric Blake | 0c9390d | 2017-06-08 17:26:17 -0500 | [diff] [blame] | 974 | static void client_close(NBDClient *client, bool negotiated) |
Paolo Bonzini | 1743b51 | 2011-09-19 14:33:23 +0200 | [diff] [blame] | 975 | { |
Paolo Bonzini | ff2b68a | 2012-08-22 18:45:12 +0200 | [diff] [blame] | 976 | if (client->closing) { |
| 977 | return; |
| 978 | } |
| 979 | |
| 980 | client->closing = true; |
| 981 | |
| 982 | /* Force requests to finish. They will drop their own references, |
| 983 | * then we'll close the socket and free the NBDClient. |
| 984 | */ |
Daniel P. Berrange | 1c778ef | 2016-02-10 18:41:04 +0000 | [diff] [blame] | 985 | qio_channel_shutdown(client->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, |
| 986 | NULL); |
Paolo Bonzini | ff2b68a | 2012-08-22 18:45:12 +0200 | [diff] [blame] | 987 | |
| 988 | /* Also tell the client, so that they release their reference. */ |
Eric Blake | 0c9390d | 2017-06-08 17:26:17 -0500 | [diff] [blame] | 989 | if (client->close_fn) { |
| 990 | client->close_fn(client, negotiated); |
Paolo Bonzini | 1743b51 | 2011-09-19 14:33:23 +0200 | [diff] [blame] | 991 | } |
Paolo Bonzini | 1743b51 | 2011-09-19 14:33:23 +0200 | [diff] [blame] | 992 | } |
| 993 | |
Eric Blake | 315f78a | 2016-10-14 13:33:05 -0500 | [diff] [blame] | 994 | static NBDRequestData *nbd_request_get(NBDClient *client) |
Paolo Bonzini | d9a7380 | 2011-09-19 14:18:33 +0200 | [diff] [blame] | 995 | { |
Eric Blake | 315f78a | 2016-10-14 13:33:05 -0500 | [diff] [blame] | 996 | NBDRequestData *req; |
Paolo Bonzini | 72deddc | 2011-10-07 16:47:56 +0200 | [diff] [blame] | 997 | |
Paolo Bonzini | 41996e3 | 2011-09-19 15:25:40 +0200 | [diff] [blame] | 998 | assert(client->nb_requests <= MAX_NBD_REQUESTS - 1); |
| 999 | client->nb_requests++; |
| 1000 | |
Eric Blake | 315f78a | 2016-10-14 13:33:05 -0500 | [diff] [blame] | 1001 | req = g_new0(NBDRequestData, 1); |
Paolo Bonzini | 72deddc | 2011-10-07 16:47:56 +0200 | [diff] [blame] | 1002 | nbd_client_get(client); |
| 1003 | req->client = client; |
Paolo Bonzini | d9a7380 | 2011-09-19 14:18:33 +0200 | [diff] [blame] | 1004 | return req; |
| 1005 | } |
| 1006 | |
Eric Blake | 315f78a | 2016-10-14 13:33:05 -0500 | [diff] [blame] | 1007 | static void nbd_request_put(NBDRequestData *req) |
Paolo Bonzini | d9a7380 | 2011-09-19 14:18:33 +0200 | [diff] [blame] | 1008 | { |
Paolo Bonzini | 72deddc | 2011-10-07 16:47:56 +0200 | [diff] [blame] | 1009 | NBDClient *client = req->client; |
Stefan Hajnoczi | e1adb27 | 2013-05-02 14:23:07 +0200 | [diff] [blame] | 1010 | |
Stefan Hajnoczi | 2d82148 | 2013-05-02 14:23:08 +0200 | [diff] [blame] | 1011 | if (req->data) { |
| 1012 | qemu_vfree(req->data); |
| 1013 | } |
Paolo Bonzini | 1729404 | 2015-10-01 12:59:08 +0200 | [diff] [blame] | 1014 | g_free(req); |
Stefan Hajnoczi | e1adb27 | 2013-05-02 14:23:07 +0200 | [diff] [blame] | 1015 | |
Max Reitz | 958c717 | 2014-06-20 21:57:32 +0200 | [diff] [blame] | 1016 | client->nb_requests--; |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1017 | nbd_client_receive_next_request(client); |
| 1018 | |
Paolo Bonzini | 72deddc | 2011-10-07 16:47:56 +0200 | [diff] [blame] | 1019 | nbd_client_put(client); |
Paolo Bonzini | d9a7380 | 2011-09-19 14:18:33 +0200 | [diff] [blame] | 1020 | } |
| 1021 | |
Max Reitz | aadf99a | 2014-11-18 12:21:18 +0100 | [diff] [blame] | 1022 | static void blk_aio_attached(AioContext *ctx, void *opaque) |
Max Reitz | f214928 | 2014-06-20 21:57:34 +0200 | [diff] [blame] | 1023 | { |
| 1024 | NBDExport *exp = opaque; |
| 1025 | NBDClient *client; |
| 1026 | |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 1027 | trace_nbd_blk_aio_attached(exp->name, ctx); |
Max Reitz | f214928 | 2014-06-20 21:57:34 +0200 | [diff] [blame] | 1028 | |
| 1029 | exp->ctx = ctx; |
| 1030 | |
| 1031 | QTAILQ_FOREACH(client, &exp->clients, next) { |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1032 | qio_channel_attach_aio_context(client->ioc, ctx); |
| 1033 | if (client->recv_coroutine) { |
| 1034 | aio_co_schedule(ctx, client->recv_coroutine); |
| 1035 | } |
| 1036 | if (client->send_coroutine) { |
| 1037 | aio_co_schedule(ctx, client->send_coroutine); |
| 1038 | } |
Max Reitz | f214928 | 2014-06-20 21:57:34 +0200 | [diff] [blame] | 1039 | } |
| 1040 | } |
| 1041 | |
Max Reitz | aadf99a | 2014-11-18 12:21:18 +0100 | [diff] [blame] | 1042 | static void blk_aio_detach(void *opaque) |
Max Reitz | f214928 | 2014-06-20 21:57:34 +0200 | [diff] [blame] | 1043 | { |
| 1044 | NBDExport *exp = opaque; |
| 1045 | NBDClient *client; |
| 1046 | |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 1047 | trace_nbd_blk_aio_detach(exp->name, exp->ctx); |
Max Reitz | f214928 | 2014-06-20 21:57:34 +0200 | [diff] [blame] | 1048 | |
| 1049 | QTAILQ_FOREACH(client, &exp->clients, next) { |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1050 | qio_channel_detach_aio_context(client->ioc); |
Max Reitz | f214928 | 2014-06-20 21:57:34 +0200 | [diff] [blame] | 1051 | } |
| 1052 | |
| 1053 | exp->ctx = NULL; |
| 1054 | } |
| 1055 | |
Max Reitz | 741cc43 | 2016-01-29 16:36:06 +0100 | [diff] [blame] | 1056 | static void nbd_eject_notifier(Notifier *n, void *data) |
| 1057 | { |
| 1058 | NBDExport *exp = container_of(n, NBDExport, eject_notifier); |
| 1059 | nbd_export_close(exp); |
| 1060 | } |
| 1061 | |
Kevin Wolf | cd7fca9 | 2016-07-06 11:22:39 +0200 | [diff] [blame] | 1062 | NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, |
Eric Blake | 7423f41 | 2016-07-21 13:34:46 -0600 | [diff] [blame] | 1063 | uint16_t nbdflags, void (*close)(NBDExport *), |
Kevin Wolf | cd7fca9 | 2016-07-06 11:22:39 +0200 | [diff] [blame] | 1064 | bool writethrough, BlockBackend *on_eject_blk, |
Max Reitz | 98f44bb | 2015-02-25 13:08:21 -0500 | [diff] [blame] | 1065 | Error **errp) |
Paolo Bonzini | af49bbb | 2011-09-19 14:03:37 +0200 | [diff] [blame] | 1066 | { |
Kevin Wolf | 3dff24f | 2017-08-15 21:07:38 +0800 | [diff] [blame] | 1067 | AioContext *ctx; |
Kevin Wolf | cd7fca9 | 2016-07-06 11:22:39 +0200 | [diff] [blame] | 1068 | BlockBackend *blk; |
Marc-André Lureau | e8d3eb7 | 2017-10-06 20:49:16 -0300 | [diff] [blame] | 1069 | NBDExport *exp = g_new0(NBDExport, 1); |
Kevin Wolf | 8a7ce4f | 2017-02-09 15:43:38 +0100 | [diff] [blame] | 1070 | uint64_t perm; |
Kevin Wolf | d708642 | 2017-01-13 19:02:32 +0100 | [diff] [blame] | 1071 | int ret; |
Kevin Wolf | cd7fca9 | 2016-07-06 11:22:39 +0200 | [diff] [blame] | 1072 | |
Kevin Wolf | 3dff24f | 2017-08-15 21:07:38 +0800 | [diff] [blame] | 1073 | /* |
| 1074 | * NBD exports are used for non-shared storage migration. Make sure |
| 1075 | * that BDRV_O_INACTIVE is cleared and the image is ready for write |
| 1076 | * access since the export could be available before migration handover. |
| 1077 | */ |
| 1078 | ctx = bdrv_get_aio_context(bs); |
| 1079 | aio_context_acquire(ctx); |
| 1080 | bdrv_invalidate_cache(bs, NULL); |
| 1081 | aio_context_release(ctx); |
| 1082 | |
Kevin Wolf | 8a7ce4f | 2017-02-09 15:43:38 +0100 | [diff] [blame] | 1083 | /* Don't allow resize while the NBD server is running, otherwise we don't |
| 1084 | * care what happens with the node. */ |
| 1085 | perm = BLK_PERM_CONSISTENT_READ; |
| 1086 | if ((nbdflags & NBD_FLAG_READ_ONLY) == 0) { |
| 1087 | perm |= BLK_PERM_WRITE; |
| 1088 | } |
| 1089 | blk = blk_new(perm, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | |
| 1090 | BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD); |
Kevin Wolf | d708642 | 2017-01-13 19:02:32 +0100 | [diff] [blame] | 1091 | ret = blk_insert_bs(blk, bs, errp); |
| 1092 | if (ret < 0) { |
| 1093 | goto fail; |
| 1094 | } |
Kevin Wolf | cd7fca9 | 2016-07-06 11:22:39 +0200 | [diff] [blame] | 1095 | blk_set_enable_write_cache(blk, !writethrough); |
| 1096 | |
Paolo Bonzini | 2c8d9f0 | 2012-09-18 13:26:25 +0200 | [diff] [blame] | 1097 | exp->refcount = 1; |
Paolo Bonzini | 4b9441f | 2012-09-18 13:58:25 +0200 | [diff] [blame] | 1098 | QTAILQ_INIT(&exp->clients); |
Max Reitz | aadf99a | 2014-11-18 12:21:18 +0100 | [diff] [blame] | 1099 | exp->blk = blk; |
Paolo Bonzini | af49bbb | 2011-09-19 14:03:37 +0200 | [diff] [blame] | 1100 | exp->dev_offset = dev_offset; |
| 1101 | exp->nbdflags = nbdflags; |
Max Reitz | 98f44bb | 2015-02-25 13:08:21 -0500 | [diff] [blame] | 1102 | exp->size = size < 0 ? blk_getlength(blk) : size; |
| 1103 | if (exp->size < 0) { |
| 1104 | error_setg_errno(errp, -exp->size, |
| 1105 | "Failed to determine the NBD export's length"); |
| 1106 | goto fail; |
| 1107 | } |
| 1108 | exp->size -= exp->size % BDRV_SECTOR_SIZE; |
| 1109 | |
Paolo Bonzini | 0ddf08d | 2012-09-18 13:59:03 +0200 | [diff] [blame] | 1110 | exp->close = close; |
Max Reitz | aadf99a | 2014-11-18 12:21:18 +0100 | [diff] [blame] | 1111 | exp->ctx = blk_get_aio_context(blk); |
Max Reitz | aadf99a | 2014-11-18 12:21:18 +0100 | [diff] [blame] | 1112 | blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp); |
Max Reitz | 741cc43 | 2016-01-29 16:36:06 +0100 | [diff] [blame] | 1113 | |
Kevin Wolf | cd7fca9 | 2016-07-06 11:22:39 +0200 | [diff] [blame] | 1114 | if (on_eject_blk) { |
| 1115 | blk_ref(on_eject_blk); |
| 1116 | exp->eject_notifier_blk = on_eject_blk; |
| 1117 | exp->eject_notifier.notify = nbd_eject_notifier; |
| 1118 | blk_add_remove_bs_notifier(on_eject_blk, &exp->eject_notifier); |
| 1119 | } |
Paolo Bonzini | af49bbb | 2011-09-19 14:03:37 +0200 | [diff] [blame] | 1120 | return exp; |
Max Reitz | 98f44bb | 2015-02-25 13:08:21 -0500 | [diff] [blame] | 1121 | |
| 1122 | fail: |
Kevin Wolf | cd7fca9 | 2016-07-06 11:22:39 +0200 | [diff] [blame] | 1123 | blk_unref(blk); |
Max Reitz | 98f44bb | 2015-02-25 13:08:21 -0500 | [diff] [blame] | 1124 | g_free(exp); |
| 1125 | return NULL; |
Paolo Bonzini | af49bbb | 2011-09-19 14:03:37 +0200 | [diff] [blame] | 1126 | } |
| 1127 | |
Paolo Bonzini | ee0a19e | 2012-08-22 15:59:23 +0200 | [diff] [blame] | 1128 | NBDExport *nbd_export_find(const char *name) |
| 1129 | { |
| 1130 | NBDExport *exp; |
| 1131 | QTAILQ_FOREACH(exp, &exports, next) { |
| 1132 | if (strcmp(name, exp->name) == 0) { |
| 1133 | return exp; |
| 1134 | } |
| 1135 | } |
| 1136 | |
| 1137 | return NULL; |
| 1138 | } |
| 1139 | |
| 1140 | void nbd_export_set_name(NBDExport *exp, const char *name) |
| 1141 | { |
| 1142 | if (exp->name == name) { |
| 1143 | return; |
| 1144 | } |
| 1145 | |
| 1146 | nbd_export_get(exp); |
| 1147 | if (exp->name != NULL) { |
| 1148 | g_free(exp->name); |
| 1149 | exp->name = NULL; |
| 1150 | QTAILQ_REMOVE(&exports, exp, next); |
| 1151 | nbd_export_put(exp); |
| 1152 | } |
| 1153 | if (name != NULL) { |
| 1154 | nbd_export_get(exp); |
| 1155 | exp->name = g_strdup(name); |
| 1156 | QTAILQ_INSERT_TAIL(&exports, exp, next); |
| 1157 | } |
| 1158 | nbd_export_put(exp); |
| 1159 | } |
| 1160 | |
Eric Blake | b1a75b3 | 2016-10-14 13:33:03 -0500 | [diff] [blame] | 1161 | void nbd_export_set_description(NBDExport *exp, const char *description) |
| 1162 | { |
| 1163 | g_free(exp->description); |
| 1164 | exp->description = g_strdup(description); |
| 1165 | } |
| 1166 | |
Paolo Bonzini | af49bbb | 2011-09-19 14:03:37 +0200 | [diff] [blame] | 1167 | void nbd_export_close(NBDExport *exp) |
| 1168 | { |
Paolo Bonzini | 4b9441f | 2012-09-18 13:58:25 +0200 | [diff] [blame] | 1169 | NBDClient *client, *next; |
Paolo Bonzini | 2c8d9f0 | 2012-09-18 13:26:25 +0200 | [diff] [blame] | 1170 | |
Paolo Bonzini | 4b9441f | 2012-09-18 13:58:25 +0200 | [diff] [blame] | 1171 | nbd_export_get(exp); |
| 1172 | QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) { |
Eric Blake | 0c9390d | 2017-06-08 17:26:17 -0500 | [diff] [blame] | 1173 | client_close(client, true); |
Paolo Bonzini | 4b9441f | 2012-09-18 13:58:25 +0200 | [diff] [blame] | 1174 | } |
Paolo Bonzini | 125afda | 2012-09-18 14:31:44 +0200 | [diff] [blame] | 1175 | nbd_export_set_name(exp, NULL); |
Eric Blake | b1a75b3 | 2016-10-14 13:33:03 -0500 | [diff] [blame] | 1176 | nbd_export_set_description(exp, NULL); |
Paolo Bonzini | 4b9441f | 2012-09-18 13:58:25 +0200 | [diff] [blame] | 1177 | nbd_export_put(exp); |
Paolo Bonzini | 2c8d9f0 | 2012-09-18 13:26:25 +0200 | [diff] [blame] | 1178 | } |
| 1179 | |
| 1180 | void nbd_export_get(NBDExport *exp) |
| 1181 | { |
| 1182 | assert(exp->refcount > 0); |
| 1183 | exp->refcount++; |
| 1184 | } |
| 1185 | |
| 1186 | void nbd_export_put(NBDExport *exp) |
| 1187 | { |
| 1188 | assert(exp->refcount > 0); |
| 1189 | if (exp->refcount == 1) { |
| 1190 | nbd_export_close(exp); |
Paolo Bonzini | d9a7380 | 2011-09-19 14:18:33 +0200 | [diff] [blame] | 1191 | } |
| 1192 | |
Vladimir Sementsov-Ogievskiy | 9156245 | 2017-12-07 18:50:57 +0300 | [diff] [blame] | 1193 | /* nbd_export_close() may theoretically reduce refcount to 0. It may happen |
| 1194 | * if someone calls nbd_export_put() on named export not through |
| 1195 | * nbd_export_set_name() when refcount is 1. So, let's assert that |
| 1196 | * it is > 0. |
| 1197 | */ |
| 1198 | assert(exp->refcount > 0); |
Paolo Bonzini | 2c8d9f0 | 2012-09-18 13:26:25 +0200 | [diff] [blame] | 1199 | if (--exp->refcount == 0) { |
Paolo Bonzini | ee0a19e | 2012-08-22 15:59:23 +0200 | [diff] [blame] | 1200 | assert(exp->name == NULL); |
Eric Blake | b1a75b3 | 2016-10-14 13:33:03 -0500 | [diff] [blame] | 1201 | assert(exp->description == NULL); |
Paolo Bonzini | ee0a19e | 2012-08-22 15:59:23 +0200 | [diff] [blame] | 1202 | |
Paolo Bonzini | 0ddf08d | 2012-09-18 13:59:03 +0200 | [diff] [blame] | 1203 | if (exp->close) { |
| 1204 | exp->close(exp); |
| 1205 | } |
| 1206 | |
Wen Congyang | d626834 | 2015-09-16 16:35:46 +0800 | [diff] [blame] | 1207 | if (exp->blk) { |
Kevin Wolf | cd7fca9 | 2016-07-06 11:22:39 +0200 | [diff] [blame] | 1208 | if (exp->eject_notifier_blk) { |
| 1209 | notifier_remove(&exp->eject_notifier); |
| 1210 | blk_unref(exp->eject_notifier_blk); |
| 1211 | } |
Wen Congyang | d626834 | 2015-09-16 16:35:46 +0800 | [diff] [blame] | 1212 | blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, |
| 1213 | blk_aio_detach, exp); |
| 1214 | blk_unref(exp->blk); |
| 1215 | exp->blk = NULL; |
| 1216 | } |
| 1217 | |
Paolo Bonzini | 2c8d9f0 | 2012-09-18 13:26:25 +0200 | [diff] [blame] | 1218 | g_free(exp); |
| 1219 | } |
Paolo Bonzini | af49bbb | 2011-09-19 14:03:37 +0200 | [diff] [blame] | 1220 | } |
| 1221 | |
Max Reitz | e140177 | 2014-11-18 12:21:17 +0100 | [diff] [blame] | 1222 | BlockBackend *nbd_export_get_blockdev(NBDExport *exp) |
Paolo Bonzini | 125afda | 2012-09-18 14:31:44 +0200 | [diff] [blame] | 1223 | { |
Max Reitz | aadf99a | 2014-11-18 12:21:18 +0100 | [diff] [blame] | 1224 | return exp->blk; |
Paolo Bonzini | 125afda | 2012-09-18 14:31:44 +0200 | [diff] [blame] | 1225 | } |
| 1226 | |
Paolo Bonzini | ee0a19e | 2012-08-22 15:59:23 +0200 | [diff] [blame] | 1227 | void nbd_export_close_all(void) |
| 1228 | { |
| 1229 | NBDExport *exp, *next; |
| 1230 | |
| 1231 | QTAILQ_FOREACH_SAFE(exp, &exports, next, next) { |
| 1232 | nbd_export_close(exp); |
Paolo Bonzini | ee0a19e | 2012-08-22 15:59:23 +0200 | [diff] [blame] | 1233 | } |
| 1234 | } |
| 1235 | |
Vladimir Sementsov-Ogievskiy | de79bfc | 2017-10-12 17:29:00 -0500 | [diff] [blame] | 1236 | static int coroutine_fn nbd_co_send_iov(NBDClient *client, struct iovec *iov, |
| 1237 | unsigned niov, Error **errp) |
| 1238 | { |
| 1239 | int ret; |
| 1240 | |
| 1241 | g_assert(qemu_in_coroutine()); |
| 1242 | qemu_co_mutex_lock(&client->send_lock); |
| 1243 | client->send_coroutine = qemu_coroutine_self(); |
| 1244 | |
| 1245 | ret = qio_channel_writev_all(client->ioc, iov, niov, errp) < 0 ? -EIO : 0; |
| 1246 | |
| 1247 | client->send_coroutine = NULL; |
| 1248 | qemu_co_mutex_unlock(&client->send_lock); |
| 1249 | |
| 1250 | return ret; |
| 1251 | } |
| 1252 | |
Vladimir Sementsov-Ogievskiy | caad538 | 2017-10-12 12:53:10 +0300 | [diff] [blame] | 1253 | static inline void set_be_simple_reply(NBDSimpleReply *reply, uint64_t error, |
| 1254 | uint64_t handle) |
| 1255 | { |
| 1256 | stl_be_p(&reply->magic, NBD_SIMPLE_REPLY_MAGIC); |
| 1257 | stl_be_p(&reply->error, error); |
| 1258 | stq_be_p(&reply->handle, handle); |
| 1259 | } |
| 1260 | |
Vladimir Sementsov-Ogievskiy | 978df1b | 2017-10-12 12:53:12 +0300 | [diff] [blame] | 1261 | static int nbd_co_send_simple_reply(NBDClient *client, |
Vladimir Sementsov-Ogievskiy | 14cea41 | 2017-10-12 17:05:06 -0500 | [diff] [blame] | 1262 | uint64_t handle, |
| 1263 | uint32_t error, |
Vladimir Sementsov-Ogievskiy | 978df1b | 2017-10-12 12:53:12 +0300 | [diff] [blame] | 1264 | void *data, |
| 1265 | size_t len, |
| 1266 | Error **errp) |
Paolo Bonzini | 2204559 | 2011-09-19 14:25:30 +0200 | [diff] [blame] | 1267 | { |
Vladimir Sementsov-Ogievskiy | de79bfc | 2017-10-12 17:29:00 -0500 | [diff] [blame] | 1268 | NBDSimpleReply reply; |
Vladimir Sementsov-Ogievskiy | 14cea41 | 2017-10-12 17:05:06 -0500 | [diff] [blame] | 1269 | int nbd_err = system_errno_to_nbd_errno(error); |
Vladimir Sementsov-Ogievskiy | de79bfc | 2017-10-12 17:29:00 -0500 | [diff] [blame] | 1270 | struct iovec iov[] = { |
| 1271 | {.iov_base = &reply, .iov_len = sizeof(reply)}, |
| 1272 | {.iov_base = data, .iov_len = len} |
| 1273 | }; |
Vladimir Sementsov-Ogievskiy | 6fb2b97 | 2017-07-07 18:29:17 +0300 | [diff] [blame] | 1274 | |
Eric Blake | e7a78d0 | 2017-10-27 12:40:26 +0200 | [diff] [blame] | 1275 | trace_nbd_co_send_simple_reply(handle, nbd_err, nbd_err_lookup(nbd_err), |
| 1276 | len); |
Vladimir Sementsov-Ogievskiy | de79bfc | 2017-10-12 17:29:00 -0500 | [diff] [blame] | 1277 | set_be_simple_reply(&reply, nbd_err, handle); |
Vladimir Sementsov-Ogievskiy | 6fb2b97 | 2017-07-07 18:29:17 +0300 | [diff] [blame] | 1278 | |
Vladimir Sementsov-Ogievskiy | de79bfc | 2017-10-12 17:29:00 -0500 | [diff] [blame] | 1279 | return nbd_co_send_iov(client, iov, len ? 2 : 1, errp); |
Paolo Bonzini | 2204559 | 2011-09-19 14:25:30 +0200 | [diff] [blame] | 1280 | } |
| 1281 | |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1282 | static inline void set_be_chunk(NBDStructuredReplyChunk *chunk, uint16_t flags, |
| 1283 | uint16_t type, uint64_t handle, uint32_t length) |
| 1284 | { |
| 1285 | stl_be_p(&chunk->magic, NBD_STRUCTURED_REPLY_MAGIC); |
| 1286 | stw_be_p(&chunk->flags, flags); |
| 1287 | stw_be_p(&chunk->type, type); |
| 1288 | stq_be_p(&chunk->handle, handle); |
| 1289 | stl_be_p(&chunk->length, length); |
| 1290 | } |
| 1291 | |
Eric Blake | ef8c887 | 2017-11-08 15:57:03 -0600 | [diff] [blame] | 1292 | static int coroutine_fn nbd_co_send_structured_done(NBDClient *client, |
| 1293 | uint64_t handle, |
| 1294 | Error **errp) |
| 1295 | { |
| 1296 | NBDStructuredReplyChunk chunk; |
| 1297 | struct iovec iov[] = { |
| 1298 | {.iov_base = &chunk, .iov_len = sizeof(chunk)}, |
| 1299 | }; |
| 1300 | |
| 1301 | trace_nbd_co_send_structured_done(handle); |
| 1302 | set_be_chunk(&chunk, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_NONE, handle, 0); |
| 1303 | |
| 1304 | return nbd_co_send_iov(client, iov, 1, errp); |
| 1305 | } |
| 1306 | |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1307 | static int coroutine_fn nbd_co_send_structured_read(NBDClient *client, |
| 1308 | uint64_t handle, |
| 1309 | uint64_t offset, |
| 1310 | void *data, |
| 1311 | size_t size, |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1312 | bool final, |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1313 | Error **errp) |
| 1314 | { |
Eric Blake | efdc0c1 | 2017-11-08 15:57:00 -0600 | [diff] [blame] | 1315 | NBDStructuredReadData chunk; |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1316 | struct iovec iov[] = { |
| 1317 | {.iov_base = &chunk, .iov_len = sizeof(chunk)}, |
| 1318 | {.iov_base = data, .iov_len = size} |
| 1319 | }; |
| 1320 | |
Eric Blake | ef8c887 | 2017-11-08 15:57:03 -0600 | [diff] [blame] | 1321 | assert(size); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1322 | trace_nbd_co_send_structured_read(handle, offset, data, size); |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1323 | set_be_chunk(&chunk.h, final ? NBD_REPLY_FLAG_DONE : 0, |
| 1324 | NBD_REPLY_TYPE_OFFSET_DATA, handle, |
| 1325 | sizeof(chunk) - sizeof(chunk.h) + size); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1326 | stq_be_p(&chunk.offset, offset); |
| 1327 | |
| 1328 | return nbd_co_send_iov(client, iov, 2, errp); |
| 1329 | } |
| 1330 | |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1331 | static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, |
| 1332 | uint64_t handle, |
| 1333 | uint64_t offset, |
| 1334 | uint8_t *data, |
| 1335 | size_t size, |
| 1336 | Error **errp) |
| 1337 | { |
| 1338 | int ret = 0; |
| 1339 | NBDExport *exp = client->exp; |
| 1340 | size_t progress = 0; |
| 1341 | |
| 1342 | while (progress < size) { |
| 1343 | int64_t pnum; |
| 1344 | int status = bdrv_block_status_above(blk_bs(exp->blk), NULL, |
| 1345 | offset + progress, |
| 1346 | size - progress, &pnum, NULL, |
| 1347 | NULL); |
Eric Blake | e2de325 | 2017-11-06 21:09:12 -0600 | [diff] [blame] | 1348 | bool final; |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1349 | |
| 1350 | if (status < 0) { |
| 1351 | error_setg_errno(errp, -status, "unable to check for holes"); |
| 1352 | return status; |
| 1353 | } |
| 1354 | assert(pnum && pnum <= size - progress); |
Eric Blake | e2de325 | 2017-11-06 21:09:12 -0600 | [diff] [blame] | 1355 | final = progress + pnum == size; |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1356 | if (status & BDRV_BLOCK_ZERO) { |
| 1357 | NBDStructuredReadHole chunk; |
| 1358 | struct iovec iov[] = { |
| 1359 | {.iov_base = &chunk, .iov_len = sizeof(chunk)}, |
| 1360 | }; |
| 1361 | |
| 1362 | trace_nbd_co_send_structured_read_hole(handle, offset + progress, |
| 1363 | pnum); |
Eric Blake | e2de325 | 2017-11-06 21:09:12 -0600 | [diff] [blame] | 1364 | set_be_chunk(&chunk.h, final ? NBD_REPLY_FLAG_DONE : 0, |
| 1365 | NBD_REPLY_TYPE_OFFSET_HOLE, |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1366 | handle, sizeof(chunk) - sizeof(chunk.h)); |
| 1367 | stq_be_p(&chunk.offset, offset + progress); |
| 1368 | stl_be_p(&chunk.length, pnum); |
| 1369 | ret = nbd_co_send_iov(client, iov, 1, errp); |
| 1370 | } else { |
| 1371 | ret = blk_pread(exp->blk, offset + progress + exp->dev_offset, |
| 1372 | data + progress, pnum); |
| 1373 | if (ret < 0) { |
| 1374 | error_setg_errno(errp, -ret, "reading from file failed"); |
| 1375 | break; |
| 1376 | } |
| 1377 | ret = nbd_co_send_structured_read(client, handle, offset + progress, |
Eric Blake | e2de325 | 2017-11-06 21:09:12 -0600 | [diff] [blame] | 1378 | data + progress, pnum, final, |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1379 | errp); |
| 1380 | } |
| 1381 | |
| 1382 | if (ret < 0) { |
| 1383 | break; |
| 1384 | } |
| 1385 | progress += pnum; |
| 1386 | } |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1387 | return ret; |
| 1388 | } |
| 1389 | |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1390 | static int coroutine_fn nbd_co_send_structured_error(NBDClient *client, |
| 1391 | uint64_t handle, |
| 1392 | uint32_t error, |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1393 | const char *msg, |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1394 | Error **errp) |
| 1395 | { |
| 1396 | NBDStructuredError chunk; |
| 1397 | int nbd_err = system_errno_to_nbd_errno(error); |
| 1398 | struct iovec iov[] = { |
| 1399 | {.iov_base = &chunk, .iov_len = sizeof(chunk)}, |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1400 | {.iov_base = (char *)msg, .iov_len = msg ? strlen(msg) : 0}, |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1401 | }; |
| 1402 | |
| 1403 | assert(nbd_err); |
| 1404 | trace_nbd_co_send_structured_error(handle, nbd_err, |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1405 | nbd_err_lookup(nbd_err), msg ? msg : ""); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1406 | set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_ERROR, handle, |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1407 | sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1408 | stl_be_p(&chunk.error, nbd_err); |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1409 | stw_be_p(&chunk.message_length, iov[1].iov_len); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1410 | |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1411 | return nbd_co_send_iov(client, iov, 1 + !!iov[1].iov_len, errp); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1412 | } |
| 1413 | |
Vladimir Sementsov-Ogievskiy | 2a6e128 | 2017-06-02 18:01:44 +0300 | [diff] [blame] | 1414 | /* nbd_co_receive_request |
| 1415 | * Collect a client request. Return 0 if request looks valid, -EIO to drop |
| 1416 | * connection right away, and any other negative value to report an error to |
| 1417 | * the client (although the caller may still need to disconnect after reporting |
| 1418 | * the error). |
| 1419 | */ |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1420 | static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, |
| 1421 | Error **errp) |
Paolo Bonzini | a030b34 | 2011-09-19 15:07:54 +0200 | [diff] [blame] | 1422 | { |
Paolo Bonzini | 72deddc | 2011-10-07 16:47:56 +0200 | [diff] [blame] | 1423 | NBDClient *client = req->client; |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1424 | int valid_flags; |
Paolo Bonzini | a030b34 | 2011-09-19 15:07:54 +0200 | [diff] [blame] | 1425 | |
Daniel P. Berrange | 1c778ef | 2016-02-10 18:41:04 +0000 | [diff] [blame] | 1426 | g_assert(qemu_in_coroutine()); |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1427 | assert(client->recv_coroutine == qemu_coroutine_self()); |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1428 | if (nbd_receive_request(client->ioc, request, errp) < 0) { |
Vladimir Sementsov-Ogievskiy | ee898b8 | 2017-06-02 18:01:45 +0300 | [diff] [blame] | 1429 | return -EIO; |
Paolo Bonzini | a030b34 | 2011-09-19 15:07:54 +0200 | [diff] [blame] | 1430 | } |
| 1431 | |
Eric Blake | 3736cc5 | 2017-07-07 15:30:43 -0500 | [diff] [blame] | 1432 | trace_nbd_co_receive_request_decode_type(request->handle, request->type, |
| 1433 | nbd_cmd_lookup(request->type)); |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 1434 | |
Eric Blake | b626b51 | 2016-10-14 13:33:04 -0500 | [diff] [blame] | 1435 | if (request->type != NBD_CMD_WRITE) { |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 1436 | /* No payload, we are ready to read the next request. */ |
| 1437 | req->complete = true; |
| 1438 | } |
| 1439 | |
Eric Blake | b626b51 | 2016-10-14 13:33:04 -0500 | [diff] [blame] | 1440 | if (request->type == NBD_CMD_DISC) { |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 1441 | /* Special case: we're going to disconnect without a reply, |
| 1442 | * whether or not flags, from, or len are bogus */ |
Vladimir Sementsov-Ogievskiy | ee898b8 | 2017-06-02 18:01:45 +0300 | [diff] [blame] | 1443 | return -EIO; |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 1444 | } |
| 1445 | |
Eric Blake | b626b51 | 2016-10-14 13:33:04 -0500 | [diff] [blame] | 1446 | if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE) { |
Paolo Bonzini | eb38c3b | 2016-01-07 14:32:42 +0100 | [diff] [blame] | 1447 | if (request->len > NBD_MAX_BUFFER_SIZE) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1448 | error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)", |
| 1449 | request->len, NBD_MAX_BUFFER_SIZE); |
Vladimir Sementsov-Ogievskiy | ee898b8 | 2017-06-02 18:01:45 +0300 | [diff] [blame] | 1450 | return -EINVAL; |
Paolo Bonzini | eb38c3b | 2016-01-07 14:32:42 +0100 | [diff] [blame] | 1451 | } |
| 1452 | |
Paolo Bonzini | f1c1752 | 2016-01-07 14:34:13 +0100 | [diff] [blame] | 1453 | req->data = blk_try_blockalign(client->exp->blk, request->len); |
| 1454 | if (req->data == NULL) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1455 | error_setg(errp, "No memory"); |
Vladimir Sementsov-Ogievskiy | ee898b8 | 2017-06-02 18:01:45 +0300 | [diff] [blame] | 1456 | return -ENOMEM; |
Paolo Bonzini | f1c1752 | 2016-01-07 14:34:13 +0100 | [diff] [blame] | 1457 | } |
Stefan Hajnoczi | 2d82148 | 2013-05-02 14:23:08 +0200 | [diff] [blame] | 1458 | } |
Eric Blake | b626b51 | 2016-10-14 13:33:04 -0500 | [diff] [blame] | 1459 | if (request->type == NBD_CMD_WRITE) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1460 | if (nbd_read(client->ioc, req->data, request->len, errp) < 0) { |
| 1461 | error_prepend(errp, "reading from socket failed: "); |
Vladimir Sementsov-Ogievskiy | ee898b8 | 2017-06-02 18:01:45 +0300 | [diff] [blame] | 1462 | return -EIO; |
Paolo Bonzini | a030b34 | 2011-09-19 15:07:54 +0200 | [diff] [blame] | 1463 | } |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 1464 | req->complete = true; |
Vladimir Sementsov-Ogievskiy | 6fb2b97 | 2017-07-07 18:29:17 +0300 | [diff] [blame] | 1465 | |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 1466 | trace_nbd_co_receive_request_payload_received(request->handle, |
| 1467 | request->len); |
Paolo Bonzini | a030b34 | 2011-09-19 15:07:54 +0200 | [diff] [blame] | 1468 | } |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 1469 | |
Eric Blake | fed5f8f | 2017-11-15 15:35:56 -0600 | [diff] [blame] | 1470 | /* Sanity checks. */ |
| 1471 | if (client->exp->nbdflags & NBD_FLAG_READ_ONLY && |
| 1472 | (request->type == NBD_CMD_WRITE || |
| 1473 | request->type == NBD_CMD_WRITE_ZEROES || |
| 1474 | request->type == NBD_CMD_TRIM)) { |
| 1475 | error_setg(errp, "Export is read-only"); |
| 1476 | return -EROFS; |
| 1477 | } |
| 1478 | if (request->from > client->exp->size || |
| 1479 | request->from + request->len > client->exp->size) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1480 | error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32 |
| 1481 | ", Size: %" PRIu64, request->from, request->len, |
| 1482 | (uint64_t)client->exp->size); |
Eric Blake | fed5f8f | 2017-11-15 15:35:56 -0600 | [diff] [blame] | 1483 | return (request->type == NBD_CMD_WRITE || |
| 1484 | request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL; |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 1485 | } |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1486 | valid_flags = NBD_CMD_FLAG_FUA; |
| 1487 | if (request->type == NBD_CMD_READ && client->structured_reply) { |
| 1488 | valid_flags |= NBD_CMD_FLAG_DF; |
| 1489 | } else if (request->type == NBD_CMD_WRITE_ZEROES) { |
| 1490 | valid_flags |= NBD_CMD_FLAG_NO_HOLE; |
Eric Blake | ab7c548 | 2016-05-11 16:39:38 -0600 | [diff] [blame] | 1491 | } |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1492 | if (request->flags & ~valid_flags) { |
| 1493 | error_setg(errp, "unsupported flags for command %s (got 0x%x)", |
| 1494 | nbd_cmd_lookup(request->type), request->flags); |
Vladimir Sementsov-Ogievskiy | ee898b8 | 2017-06-02 18:01:45 +0300 | [diff] [blame] | 1495 | return -EINVAL; |
Eric Blake | 1f4d6d1 | 2016-10-14 13:33:17 -0500 | [diff] [blame] | 1496 | } |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 1497 | |
Vladimir Sementsov-Ogievskiy | ee898b8 | 2017-06-02 18:01:45 +0300 | [diff] [blame] | 1498 | return 0; |
Paolo Bonzini | a030b34 | 2011-09-19 15:07:54 +0200 | [diff] [blame] | 1499 | } |
| 1500 | |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1501 | /* Owns a reference to the NBDClient passed as opaque. */ |
| 1502 | static coroutine_fn void nbd_trip(void *opaque) |
ths | 7581825 | 2008-07-03 13:41:03 +0000 | [diff] [blame] | 1503 | { |
Paolo Bonzini | 262db38 | 2011-09-19 15:19:27 +0200 | [diff] [blame] | 1504 | NBDClient *client = opaque; |
Paolo Bonzini | 1743b51 | 2011-09-19 14:33:23 +0200 | [diff] [blame] | 1505 | NBDExport *exp = client->exp; |
Eric Blake | 315f78a | 2016-10-14 13:33:05 -0500 | [diff] [blame] | 1506 | NBDRequestData *req; |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1507 | NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */ |
Vladimir Sementsov-Ogievskiy | a0dc63a | 2017-06-02 18:01:42 +0300 | [diff] [blame] | 1508 | int ret; |
Eric Blake | a0c3036 | 2016-05-11 16:39:34 -0600 | [diff] [blame] | 1509 | int flags; |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1510 | int reply_data_len = 0; |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1511 | Error *local_err = NULL; |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1512 | char *msg = NULL; |
ths | 7581825 | 2008-07-03 13:41:03 +0000 | [diff] [blame] | 1513 | |
Vladimir Sementsov-Ogievskiy | 9588463 | 2017-07-07 18:29:18 +0300 | [diff] [blame] | 1514 | trace_nbd_trip(); |
Paolo Bonzini | ff2b68a | 2012-08-22 18:45:12 +0200 | [diff] [blame] | 1515 | if (client->closing) { |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1516 | nbd_client_put(client); |
Paolo Bonzini | ff2b68a | 2012-08-22 18:45:12 +0200 | [diff] [blame] | 1517 | return; |
| 1518 | } |
ths | 7581825 | 2008-07-03 13:41:03 +0000 | [diff] [blame] | 1519 | |
Paolo Bonzini | ff2b68a | 2012-08-22 18:45:12 +0200 | [diff] [blame] | 1520 | req = nbd_request_get(client); |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1521 | ret = nbd_co_receive_request(req, &request, &local_err); |
Vladimir Sementsov-Ogievskiy | ee898b8 | 2017-06-02 18:01:45 +0300 | [diff] [blame] | 1522 | client->recv_coroutine = NULL; |
| 1523 | nbd_client_receive_next_request(client); |
Paolo Bonzini | a030b34 | 2011-09-19 15:07:54 +0200 | [diff] [blame] | 1524 | if (ret == -EIO) { |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1525 | goto disconnect; |
Paolo Bonzini | a030b34 | 2011-09-19 15:07:54 +0200 | [diff] [blame] | 1526 | } |
ths | 7581825 | 2008-07-03 13:41:03 +0000 | [diff] [blame] | 1527 | |
Paolo Bonzini | a030b34 | 2011-09-19 15:07:54 +0200 | [diff] [blame] | 1528 | if (ret < 0) { |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1529 | goto reply; |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 1530 | } |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 1531 | |
Wen Congyang | d626834 | 2015-09-16 16:35:46 +0800 | [diff] [blame] | 1532 | if (client->closing) { |
| 1533 | /* |
| 1534 | * The client may be closed when we are blocked in |
| 1535 | * nbd_co_receive_request() |
| 1536 | */ |
| 1537 | goto done; |
| 1538 | } |
| 1539 | |
Eric Blake | b626b51 | 2016-10-14 13:33:04 -0500 | [diff] [blame] | 1540 | switch (request.type) { |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 1541 | case NBD_CMD_READ: |
Eric Blake | b626b51 | 2016-10-14 13:33:04 -0500 | [diff] [blame] | 1542 | /* XXX: NBD Protocol only documents use of FUA with WRITE */ |
| 1543 | if (request.flags & NBD_CMD_FLAG_FUA) { |
Max Reitz | aadf99a | 2014-11-18 12:21:18 +0100 | [diff] [blame] | 1544 | ret = blk_co_flush(exp->blk); |
Paolo Bonzini | e25ceb7 | 2012-04-19 11:59:11 +0200 | [diff] [blame] | 1545 | if (ret < 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1546 | error_setg_errno(&local_err, -ret, "flush failed"); |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1547 | break; |
Paolo Bonzini | e25ceb7 | 2012-04-19 11:59:11 +0200 | [diff] [blame] | 1548 | } |
| 1549 | } |
| 1550 | |
Eric Blake | e2de325 | 2017-11-06 21:09:12 -0600 | [diff] [blame] | 1551 | if (client->structured_reply && !(request.flags & NBD_CMD_FLAG_DF) && |
| 1552 | request.len) { |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1553 | ret = nbd_co_send_sparse_read(req->client, request.handle, |
| 1554 | request.from, req->data, request.len, |
| 1555 | &local_err); |
| 1556 | if (ret < 0) { |
| 1557 | goto reply; |
| 1558 | } |
| 1559 | goto done; |
| 1560 | } |
| 1561 | |
Eric Blake | df7b97f | 2016-04-21 08:42:30 -0600 | [diff] [blame] | 1562 | ret = blk_pread(exp->blk, request.from + exp->dev_offset, |
| 1563 | req->data, request.len); |
Paolo Bonzini | adcf630 | 2011-09-13 17:27:45 +0200 | [diff] [blame] | 1564 | if (ret < 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1565 | error_setg_errno(&local_err, -ret, "reading from file failed"); |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1566 | break; |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 1567 | } |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 1568 | |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1569 | reply_data_len = request.len; |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1570 | |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 1571 | break; |
| 1572 | case NBD_CMD_WRITE: |
Eric Blake | a0c3036 | 2016-05-11 16:39:34 -0600 | [diff] [blame] | 1573 | flags = 0; |
Eric Blake | b626b51 | 2016-10-14 13:33:04 -0500 | [diff] [blame] | 1574 | if (request.flags & NBD_CMD_FLAG_FUA) { |
Eric Blake | a0c3036 | 2016-05-11 16:39:34 -0600 | [diff] [blame] | 1575 | flags |= BDRV_REQ_FUA; |
| 1576 | } |
Eric Blake | df7b97f | 2016-04-21 08:42:30 -0600 | [diff] [blame] | 1577 | ret = blk_pwrite(exp->blk, request.from + exp->dev_offset, |
Eric Blake | a0c3036 | 2016-05-11 16:39:34 -0600 | [diff] [blame] | 1578 | req->data, request.len, flags); |
Paolo Bonzini | fae6941 | 2011-09-19 16:04:36 +0200 | [diff] [blame] | 1579 | if (ret < 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1580 | error_setg_errno(&local_err, -ret, "writing to file failed"); |
Paolo Bonzini | fae6941 | 2011-09-19 16:04:36 +0200 | [diff] [blame] | 1581 | } |
| 1582 | |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 1583 | break; |
Eric Blake | 1f4d6d1 | 2016-10-14 13:33:17 -0500 | [diff] [blame] | 1584 | case NBD_CMD_WRITE_ZEROES: |
Eric Blake | 1f4d6d1 | 2016-10-14 13:33:17 -0500 | [diff] [blame] | 1585 | flags = 0; |
| 1586 | if (request.flags & NBD_CMD_FLAG_FUA) { |
| 1587 | flags |= BDRV_REQ_FUA; |
| 1588 | } |
| 1589 | if (!(request.flags & NBD_CMD_FLAG_NO_HOLE)) { |
| 1590 | flags |= BDRV_REQ_MAY_UNMAP; |
| 1591 | } |
| 1592 | ret = blk_pwrite_zeroes(exp->blk, request.from + exp->dev_offset, |
| 1593 | request.len, flags); |
| 1594 | if (ret < 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1595 | error_setg_errno(&local_err, -ret, "writing to file failed"); |
Eric Blake | 1f4d6d1 | 2016-10-14 13:33:17 -0500 | [diff] [blame] | 1596 | } |
| 1597 | |
Eric Blake | 1f4d6d1 | 2016-10-14 13:33:17 -0500 | [diff] [blame] | 1598 | break; |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 1599 | case NBD_CMD_DISC: |
Eric Blake | 29b6c3b | 2016-05-11 16:39:37 -0600 | [diff] [blame] | 1600 | /* unreachable, thanks to special case in nbd_co_receive_request() */ |
| 1601 | abort(); |
| 1602 | |
Paolo Bonzini | 1486d04 | 2011-10-21 13:17:14 +0200 | [diff] [blame] | 1603 | case NBD_CMD_FLUSH: |
Max Reitz | aadf99a | 2014-11-18 12:21:18 +0100 | [diff] [blame] | 1604 | ret = blk_co_flush(exp->blk); |
Paolo Bonzini | 1486d04 | 2011-10-21 13:17:14 +0200 | [diff] [blame] | 1605 | if (ret < 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1606 | error_setg_errno(&local_err, -ret, "flush failed"); |
Paolo Bonzini | 1486d04 | 2011-10-21 13:17:14 +0200 | [diff] [blame] | 1607 | } |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1608 | |
Paolo Bonzini | 1486d04 | 2011-10-21 13:17:14 +0200 | [diff] [blame] | 1609 | break; |
Paolo Bonzini | 7a70663 | 2011-10-21 13:17:14 +0200 | [diff] [blame] | 1610 | case NBD_CMD_TRIM: |
Eric Blake | 1c6c4bb | 2016-07-15 17:22:54 -0600 | [diff] [blame] | 1611 | ret = blk_co_pdiscard(exp->blk, request.from + exp->dev_offset, |
| 1612 | request.len); |
| 1613 | if (ret < 0) { |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1614 | error_setg_errno(&local_err, -ret, "discard failed"); |
Paolo Bonzini | 7a70663 | 2011-10-21 13:17:14 +0200 | [diff] [blame] | 1615 | } |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1616 | |
Paolo Bonzini | 7a70663 | 2011-10-21 13:17:14 +0200 | [diff] [blame] | 1617 | break; |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 1618 | default: |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1619 | error_setg(&local_err, "invalid request type (%" PRIu32 ") received", |
| 1620 | request.type); |
Vladimir Sementsov-Ogievskiy | 14cea41 | 2017-10-12 17:05:06 -0500 | [diff] [blame] | 1621 | ret = -EINVAL; |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1622 | } |
| 1623 | |
| 1624 | reply: |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1625 | if (local_err) { |
Vladimir Sementsov-Ogievskiy | 14cea41 | 2017-10-12 17:05:06 -0500 | [diff] [blame] | 1626 | /* If we get here, local_err was not a fatal error, and should be sent |
| 1627 | * to the client. */ |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1628 | assert(ret < 0); |
| 1629 | msg = g_strdup(error_get_pretty(local_err)); |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1630 | error_report_err(local_err); |
| 1631 | local_err = NULL; |
| 1632 | } |
| 1633 | |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1634 | if (client->structured_reply && |
| 1635 | (ret < 0 || request.type == NBD_CMD_READ)) { |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1636 | if (ret < 0) { |
| 1637 | ret = nbd_co_send_structured_error(req->client, request.handle, |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1638 | -ret, msg, &local_err); |
Eric Blake | ef8c887 | 2017-11-08 15:57:03 -0600 | [diff] [blame] | 1639 | } else if (reply_data_len) { |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1640 | ret = nbd_co_send_structured_read(req->client, request.handle, |
| 1641 | request.from, req->data, |
Eric Blake | 418638d | 2017-11-06 21:09:11 -0600 | [diff] [blame] | 1642 | reply_data_len, true, |
| 1643 | &local_err); |
Eric Blake | ef8c887 | 2017-11-08 15:57:03 -0600 | [diff] [blame] | 1644 | } else { |
| 1645 | ret = nbd_co_send_structured_done(req->client, request.handle, |
| 1646 | &local_err); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1647 | } |
| 1648 | } else { |
| 1649 | ret = nbd_co_send_simple_reply(req->client, request.handle, |
| 1650 | ret < 0 ? -ret : 0, |
| 1651 | req->data, reply_data_len, &local_err); |
| 1652 | } |
Eric Blake | a57f6de | 2017-10-27 12:40:33 +0200 | [diff] [blame] | 1653 | g_free(msg); |
Vladimir Sementsov-Ogievskiy | 5c54e7f | 2017-10-27 12:40:32 +0200 | [diff] [blame] | 1654 | if (ret < 0) { |
Vladimir Sementsov-Ogievskiy | c7b9728 | 2017-07-07 18:29:12 +0300 | [diff] [blame] | 1655 | error_prepend(&local_err, "Failed to send reply: "); |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1656 | goto disconnect; |
| 1657 | } |
| 1658 | |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1659 | /* We must disconnect after NBD_CMD_WRITE if we did not |
| 1660 | * read the payload. |
| 1661 | */ |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1662 | if (!req->complete) { |
| 1663 | error_setg(&local_err, "Request handling failed in intermediate state"); |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1664 | goto disconnect; |
Nick Thomas | b2e3d87 | 2011-02-22 15:44:51 +0000 | [diff] [blame] | 1665 | } |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 1666 | |
Paolo Bonzini | 7fe7b68 | 2012-03-05 09:10:35 +0100 | [diff] [blame] | 1667 | done: |
Paolo Bonzini | 262db38 | 2011-09-19 15:19:27 +0200 | [diff] [blame] | 1668 | nbd_request_put(req); |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1669 | nbd_client_put(client); |
Paolo Bonzini | 262db38 | 2011-09-19 15:19:27 +0200 | [diff] [blame] | 1670 | return; |
| 1671 | |
Vladimir Sementsov-Ogievskiy | 8c372a0 | 2017-06-02 18:01:50 +0300 | [diff] [blame] | 1672 | disconnect: |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1673 | if (local_err) { |
| 1674 | error_reportf_err(local_err, "Disconnect client, due to: "); |
| 1675 | } |
Paolo Bonzini | 72deddc | 2011-10-07 16:47:56 +0200 | [diff] [blame] | 1676 | nbd_request_put(req); |
Eric Blake | 0c9390d | 2017-06-08 17:26:17 -0500 | [diff] [blame] | 1677 | client_close(client, true); |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1678 | nbd_client_put(client); |
bellard | 7a5ca86 | 2008-05-27 21:13:40 +0000 | [diff] [blame] | 1679 | } |
Paolo Bonzini | af49bbb | 2011-09-19 14:03:37 +0200 | [diff] [blame] | 1680 | |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1681 | static void nbd_client_receive_next_request(NBDClient *client) |
Paolo Bonzini | af49bbb | 2011-09-19 14:03:37 +0200 | [diff] [blame] | 1682 | { |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1683 | if (!client->recv_coroutine && client->nb_requests < MAX_NBD_REQUESTS) { |
| 1684 | nbd_client_get(client); |
| 1685 | client->recv_coroutine = qemu_coroutine_create(nbd_trip, client); |
| 1686 | aio_co_schedule(client->exp->ctx, client->recv_coroutine); |
Max Reitz | 958c717 | 2014-06-20 21:57:32 +0200 | [diff] [blame] | 1687 | } |
| 1688 | } |
| 1689 | |
Fam Zheng | 1a6245a | 2016-01-14 16:41:03 +0800 | [diff] [blame] | 1690 | static coroutine_fn void nbd_co_client_start(void *opaque) |
Paolo Bonzini | 1743b51 | 2011-09-19 14:33:23 +0200 | [diff] [blame] | 1691 | { |
Vladimir Sementsov-Ogievskiy | c84087f | 2017-06-02 18:01:46 +0300 | [diff] [blame] | 1692 | NBDClient *client = opaque; |
Fam Zheng | 1a6245a | 2016-01-14 16:41:03 +0800 | [diff] [blame] | 1693 | NBDExport *exp = client->exp; |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1694 | Error *local_err = NULL; |
Fam Zheng | 1a6245a | 2016-01-14 16:41:03 +0800 | [diff] [blame] | 1695 | |
| 1696 | if (exp) { |
| 1697 | nbd_export_get(exp); |
Eric Blake | df8ad9f | 2017-05-26 22:04:21 -0500 | [diff] [blame] | 1698 | QTAILQ_INSERT_TAIL(&exp->clients, client, next); |
Fam Zheng | 1a6245a | 2016-01-14 16:41:03 +0800 | [diff] [blame] | 1699 | } |
Paolo Bonzini | 262db38 | 2011-09-19 15:19:27 +0200 | [diff] [blame] | 1700 | qemu_co_mutex_init(&client->send_lock); |
Paolo Bonzini | 2c8d9f0 | 2012-09-18 13:26:25 +0200 | [diff] [blame] | 1701 | |
Vladimir Sementsov-Ogievskiy | 2fd2c84 | 2017-07-07 18:29:11 +0300 | [diff] [blame] | 1702 | if (nbd_negotiate(client, &local_err)) { |
| 1703 | if (local_err) { |
| 1704 | error_report_err(local_err); |
| 1705 | } |
Eric Blake | 0c9390d | 2017-06-08 17:26:17 -0500 | [diff] [blame] | 1706 | client_close(client, false); |
Vladimir Sementsov-Ogievskiy | c84087f | 2017-06-02 18:01:46 +0300 | [diff] [blame] | 1707 | return; |
Paolo Bonzini | 6b8c01e | 2012-08-23 14:57:11 +0200 | [diff] [blame] | 1708 | } |
Paolo Bonzini | ff82911 | 2017-02-13 14:52:24 +0100 | [diff] [blame] | 1709 | |
| 1710 | nbd_client_receive_next_request(client); |
Fam Zheng | 1a6245a | 2016-01-14 16:41:03 +0800 | [diff] [blame] | 1711 | } |
| 1712 | |
Eric Blake | 0c9390d | 2017-06-08 17:26:17 -0500 | [diff] [blame] | 1713 | /* |
| 1714 | * Create a new client listener on the given export @exp, using the |
| 1715 | * given channel @sioc. Begin servicing it in a coroutine. When the |
| 1716 | * connection closes, call @close_fn with an indication of whether the |
| 1717 | * client completed negotiation. |
| 1718 | */ |
Daniel P. Berrange | 1c778ef | 2016-02-10 18:41:04 +0000 | [diff] [blame] | 1719 | void nbd_client_new(NBDExport *exp, |
| 1720 | QIOChannelSocket *sioc, |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 1721 | QCryptoTLSCreds *tlscreds, |
| 1722 | const char *tlsaclname, |
Eric Blake | 0c9390d | 2017-06-08 17:26:17 -0500 | [diff] [blame] | 1723 | void (*close_fn)(NBDClient *, bool)) |
Fam Zheng | 1a6245a | 2016-01-14 16:41:03 +0800 | [diff] [blame] | 1724 | { |
| 1725 | NBDClient *client; |
Vladimir Sementsov-Ogievskiy | c84087f | 2017-06-02 18:01:46 +0300 | [diff] [blame] | 1726 | Coroutine *co; |
Fam Zheng | 1a6245a | 2016-01-14 16:41:03 +0800 | [diff] [blame] | 1727 | |
Marc-André Lureau | e8d3eb7 | 2017-10-06 20:49:16 -0300 | [diff] [blame] | 1728 | client = g_new0(NBDClient, 1); |
Fam Zheng | 1a6245a | 2016-01-14 16:41:03 +0800 | [diff] [blame] | 1729 | client->refcount = 1; |
| 1730 | client->exp = exp; |
Daniel P. Berrange | f95910f | 2016-02-10 18:41:11 +0000 | [diff] [blame] | 1731 | client->tlscreds = tlscreds; |
| 1732 | if (tlscreds) { |
| 1733 | object_ref(OBJECT(client->tlscreds)); |
| 1734 | } |
| 1735 | client->tlsaclname = g_strdup(tlsaclname); |
Daniel P. Berrange | 1c778ef | 2016-02-10 18:41:04 +0000 | [diff] [blame] | 1736 | client->sioc = sioc; |
| 1737 | object_ref(OBJECT(client->sioc)); |
| 1738 | client->ioc = QIO_CHANNEL(sioc); |
| 1739 | object_ref(OBJECT(client->ioc)); |
Eric Blake | 0c9390d | 2017-06-08 17:26:17 -0500 | [diff] [blame] | 1740 | client->close_fn = close_fn; |
Fam Zheng | 1a6245a | 2016-01-14 16:41:03 +0800 | [diff] [blame] | 1741 | |
Vladimir Sementsov-Ogievskiy | c84087f | 2017-06-02 18:01:46 +0300 | [diff] [blame] | 1742 | co = qemu_coroutine_create(nbd_co_client_start, client); |
| 1743 | qemu_coroutine_enter(co); |
Paolo Bonzini | af49bbb | 2011-09-19 14:03:37 +0200 | [diff] [blame] | 1744 | } |