root / nbd.c @ a0a3167a
History | View | Annotate | Download (16.8 kB)
1 | 75818250 | ths | /*
|
---|---|---|---|
2 | 7a5ca864 | bellard | * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
|
3 | 7a5ca864 | bellard | *
|
4 | 7a5ca864 | bellard | * Network Block Device
|
5 | 7a5ca864 | bellard | *
|
6 | 7a5ca864 | bellard | * This program is free software; you can redistribute it and/or modify
|
7 | 7a5ca864 | bellard | * it under the terms of the GNU General Public License as published by
|
8 | 7a5ca864 | bellard | * the Free Software Foundation; under version 2 of the License.
|
9 | 7a5ca864 | bellard | *
|
10 | 7a5ca864 | bellard | * This program is distributed in the hope that it will be useful,
|
11 | 7a5ca864 | bellard | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 | 7a5ca864 | bellard | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13 | 7a5ca864 | bellard | * GNU General Public License for more details.
|
14 | 7a5ca864 | bellard | *
|
15 | 7a5ca864 | bellard | * You should have received a copy of the GNU General Public License
|
16 | 8167ee88 | Blue Swirl | * along with this program; if not, see <http://www.gnu.org/licenses/>.
|
17 | 75818250 | ths | */
|
18 | 7a5ca864 | bellard | |
19 | 7a5ca864 | bellard | #include "nbd.h" |
20 | 7a5ca864 | bellard | |
21 | 7a5ca864 | bellard | #include <errno.h> |
22 | 7a5ca864 | bellard | #include <string.h> |
23 | 03ff3ca3 | aliguori | #ifndef _WIN32
|
24 | 7a5ca864 | bellard | #include <sys/ioctl.h> |
25 | 03ff3ca3 | aliguori | #endif
|
26 | 5dc2eec9 | Andreas Färber | #if defined(__sun__) || defined(__HAIKU__)
|
27 | 7e00eb9b | aliguori | #include <sys/ioccom.h> |
28 | 7e00eb9b | aliguori | #endif
|
29 | 7a5ca864 | bellard | #include <ctype.h> |
30 | 7a5ca864 | bellard | #include <inttypes.h> |
31 | 75818250 | ths | |
32 | 03ff3ca3 | aliguori | #include "qemu_socket.h" |
33 | 03ff3ca3 | aliguori | |
34 | 03ff3ca3 | aliguori | //#define DEBUG_NBD
|
35 | 03ff3ca3 | aliguori | |
36 | 03ff3ca3 | aliguori | #ifdef DEBUG_NBD
|
37 | 75818250 | ths | #define TRACE(msg, ...) do { \ |
38 | 03ff3ca3 | aliguori | LOG(msg, ## __VA_ARGS__); \ |
39 | 75818250 | ths | } while(0) |
40 | 03ff3ca3 | aliguori | #else
|
41 | 03ff3ca3 | aliguori | #define TRACE(msg, ...) \
|
42 | 03ff3ca3 | aliguori | do { } while (0) |
43 | 03ff3ca3 | aliguori | #endif
|
44 | 7a5ca864 | bellard | |
45 | 7a5ca864 | bellard | #define LOG(msg, ...) do { \ |
46 | 7a5ca864 | bellard | fprintf(stderr, "%s:%s():L%d: " msg "\n", \ |
47 | 7a5ca864 | bellard | __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \ |
48 | 7a5ca864 | bellard | } while(0) |
49 | 7a5ca864 | bellard | |
50 | 7a5ca864 | bellard | /* This is all part of the "official" NBD API */
|
51 | 7a5ca864 | bellard | |
52 | b2e3d87f | Nick Thomas | #define NBD_REPLY_SIZE (4 + 4 + 8) |
53 | 7a5ca864 | bellard | #define NBD_REQUEST_MAGIC 0x25609513 |
54 | 7a5ca864 | bellard | #define NBD_REPLY_MAGIC 0x67446698 |
55 | 7a5ca864 | bellard | |
56 | 7a5ca864 | bellard | #define NBD_SET_SOCK _IO(0xab, 0) |
57 | 7a5ca864 | bellard | #define NBD_SET_BLKSIZE _IO(0xab, 1) |
58 | 7a5ca864 | bellard | #define NBD_SET_SIZE _IO(0xab, 2) |
59 | 7a5ca864 | bellard | #define NBD_DO_IT _IO(0xab, 3) |
60 | 7a5ca864 | bellard | #define NBD_CLEAR_SOCK _IO(0xab, 4) |
61 | 7a5ca864 | bellard | #define NBD_CLEAR_QUE _IO(0xab, 5) |
62 | b2e3d87f | Nick Thomas | #define NBD_PRINT_DEBUG _IO(0xab, 6) |
63 | b2e3d87f | Nick Thomas | #define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) |
64 | 7a5ca864 | bellard | #define NBD_DISCONNECT _IO(0xab, 8) |
65 | 7a5ca864 | bellard | |
66 | b2e3d87f | Nick Thomas | #define NBD_OPT_EXPORT_NAME (1 << 0) |
67 | 1d45f8b5 | Laurent Vivier | |
68 | 7a5ca864 | bellard | /* That's all folks */
|
69 | 7a5ca864 | bellard | |
70 | 75818250 | ths | #define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true) |
71 | 75818250 | ths | #define write_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, false) |
72 | 7a5ca864 | bellard | |
73 | 75818250 | ths | size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) |
74 | 7a5ca864 | bellard | { |
75 | 7a5ca864 | bellard | size_t offset = 0;
|
76 | 7a5ca864 | bellard | |
77 | 7a5ca864 | bellard | while (offset < size) {
|
78 | 7a5ca864 | bellard | ssize_t len; |
79 | 7a5ca864 | bellard | |
80 | 7a5ca864 | bellard | if (do_read) {
|
81 | 03ff3ca3 | aliguori | len = recv(fd, buffer + offset, size - offset, 0);
|
82 | 7a5ca864 | bellard | } else {
|
83 | 03ff3ca3 | aliguori | len = send(fd, buffer + offset, size - offset, 0);
|
84 | 7a5ca864 | bellard | } |
85 | 7a5ca864 | bellard | |
86 | 03ff3ca3 | aliguori | if (len == -1) |
87 | 03ff3ca3 | aliguori | errno = socket_error(); |
88 | 03ff3ca3 | aliguori | |
89 | 7a5ca864 | bellard | /* recoverable error */
|
90 | 75818250 | ths | if (len == -1 && (errno == EAGAIN || errno == EINTR)) { |
91 | 7a5ca864 | bellard | continue;
|
92 | 7a5ca864 | bellard | } |
93 | 7a5ca864 | bellard | |
94 | 7a5ca864 | bellard | /* eof */
|
95 | 7a5ca864 | bellard | if (len == 0) { |
96 | 7a5ca864 | bellard | break;
|
97 | 7a5ca864 | bellard | } |
98 | 7a5ca864 | bellard | |
99 | 7a5ca864 | bellard | /* unrecoverable error */
|
100 | 7a5ca864 | bellard | if (len == -1) { |
101 | 7a5ca864 | bellard | return 0; |
102 | 7a5ca864 | bellard | } |
103 | 7a5ca864 | bellard | |
104 | 7a5ca864 | bellard | offset += len; |
105 | 7a5ca864 | bellard | } |
106 | 7a5ca864 | bellard | |
107 | 7a5ca864 | bellard | return offset;
|
108 | 7a5ca864 | bellard | } |
109 | 7a5ca864 | bellard | |
110 | c12504ce | Nick Thomas | static void combine_addr(char *buf, size_t len, const char* address, |
111 | c12504ce | Nick Thomas | uint16_t port) |
112 | 7a5ca864 | bellard | { |
113 | c12504ce | Nick Thomas | /* If the address-part contains a colon, it's an IPv6 IP so needs [] */
|
114 | c12504ce | Nick Thomas | if (strstr(address, ":")) { |
115 | c12504ce | Nick Thomas | snprintf(buf, len, "[%s]:%u", address, port);
|
116 | c12504ce | Nick Thomas | } else {
|
117 | c12504ce | Nick Thomas | snprintf(buf, len, "%s:%u", address, port);
|
118 | 7a5ca864 | bellard | } |
119 | 7a5ca864 | bellard | } |
120 | 7a5ca864 | bellard | |
121 | c12504ce | Nick Thomas | int tcp_socket_outgoing(const char *address, uint16_t port) |
122 | 7a5ca864 | bellard | { |
123 | c12504ce | Nick Thomas | char address_and_port[128]; |
124 | c12504ce | Nick Thomas | combine_addr(address_and_port, 128, address, port);
|
125 | c12504ce | Nick Thomas | return tcp_socket_outgoing_spec(address_and_port);
|
126 | 7a5ca864 | bellard | } |
127 | 7a5ca864 | bellard | |
128 | c12504ce | Nick Thomas | int tcp_socket_outgoing_spec(const char *address_and_port) |
129 | cd831bd7 | ths | { |
130 | c12504ce | Nick Thomas | return inet_connect(address_and_port, SOCK_STREAM);
|
131 | cd831bd7 | ths | } |
132 | cd831bd7 | ths | |
133 | c12504ce | Nick Thomas | int tcp_socket_incoming(const char *address, uint16_t port) |
134 | cd831bd7 | ths | { |
135 | c12504ce | Nick Thomas | char address_and_port[128]; |
136 | c12504ce | Nick Thomas | combine_addr(address_and_port, 128, address, port);
|
137 | c12504ce | Nick Thomas | return tcp_socket_incoming_spec(address_and_port);
|
138 | c12504ce | Nick Thomas | } |
139 | cd831bd7 | ths | |
140 | c12504ce | Nick Thomas | int tcp_socket_incoming_spec(const char *address_and_port) |
141 | c12504ce | Nick Thomas | { |
142 | c12504ce | Nick Thomas | char *ostr = NULL; |
143 | c12504ce | Nick Thomas | int olen = 0; |
144 | c12504ce | Nick Thomas | return inet_listen(address_and_port, ostr, olen, SOCK_STREAM, 0); |
145 | 03ff3ca3 | aliguori | } |
146 | c12504ce | Nick Thomas | |
147 | 03ff3ca3 | aliguori | int unix_socket_incoming(const char *path) |
148 | 03ff3ca3 | aliguori | { |
149 | c12504ce | Nick Thomas | char *ostr = NULL; |
150 | c12504ce | Nick Thomas | int olen = 0; |
151 | c12504ce | Nick Thomas | |
152 | c12504ce | Nick Thomas | return unix_listen(path, ostr, olen);
|
153 | cd831bd7 | ths | } |
154 | cd831bd7 | ths | |
155 | 03ff3ca3 | aliguori | int unix_socket_outgoing(const char *path) |
156 | 03ff3ca3 | aliguori | { |
157 | c12504ce | Nick Thomas | return unix_connect(path);
|
158 | 03ff3ca3 | aliguori | } |
159 | cd831bd7 | ths | |
160 | 7a5ca864 | bellard | /* Basic flow
|
161 | 7a5ca864 | bellard | |
162 | 7a5ca864 | bellard | Server Client
|
163 | 7a5ca864 | bellard | |
164 | 7a5ca864 | bellard | Negotiate
|
165 | 7a5ca864 | bellard | Request
|
166 | 7a5ca864 | bellard | Response
|
167 | 7a5ca864 | bellard | Request
|
168 | 7a5ca864 | bellard | Response
|
169 | 7a5ca864 | bellard | ...
|
170 | 7a5ca864 | bellard | ...
|
171 | 7a5ca864 | bellard | Request (type == 2)
|
172 | 7a5ca864 | bellard | */
|
173 | 7a5ca864 | bellard | |
174 | 27982661 | aliguori | int nbd_negotiate(int csock, off_t size) |
175 | 7a5ca864 | bellard | { |
176 | b2e3d87f | Nick Thomas | char buf[8 + 8 + 8 + 128]; |
177 | b2e3d87f | Nick Thomas | |
178 | b2e3d87f | Nick Thomas | /* Negotiate
|
179 | b2e3d87f | Nick Thomas | [ 0 .. 7] passwd ("NBDMAGIC")
|
180 | b2e3d87f | Nick Thomas | [ 8 .. 15] magic (0x00420281861253)
|
181 | b2e3d87f | Nick Thomas | [16 .. 23] size
|
182 | b2e3d87f | Nick Thomas | [24 .. 151] reserved (0)
|
183 | b2e3d87f | Nick Thomas | */
|
184 | b2e3d87f | Nick Thomas | |
185 | b2e3d87f | Nick Thomas | TRACE("Beginning negotiation.");
|
186 | b2e3d87f | Nick Thomas | memcpy(buf, "NBDMAGIC", 8); |
187 | b2e3d87f | Nick Thomas | cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL); |
188 | b2e3d87f | Nick Thomas | cpu_to_be64w((uint64_t*)(buf + 16), size);
|
189 | b2e3d87f | Nick Thomas | memset(buf + 24, 0, 128); |
190 | b2e3d87f | Nick Thomas | |
191 | b2e3d87f | Nick Thomas | if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { |
192 | b2e3d87f | Nick Thomas | LOG("write failed");
|
193 | b2e3d87f | Nick Thomas | errno = EINVAL; |
194 | b2e3d87f | Nick Thomas | return -1; |
195 | b2e3d87f | Nick Thomas | } |
196 | b2e3d87f | Nick Thomas | |
197 | b2e3d87f | Nick Thomas | TRACE("Negotation succeeded.");
|
198 | b2e3d87f | Nick Thomas | |
199 | b2e3d87f | Nick Thomas | return 0; |
200 | 7a5ca864 | bellard | } |
201 | 7a5ca864 | bellard | |
202 | 1d45f8b5 | Laurent Vivier | int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, |
203 | 1d45f8b5 | Laurent Vivier | off_t *size, size_t *blocksize) |
204 | 7a5ca864 | bellard | { |
205 | b2e3d87f | Nick Thomas | char buf[256]; |
206 | b2e3d87f | Nick Thomas | uint64_t magic, s; |
207 | b2e3d87f | Nick Thomas | uint16_t tmp; |
208 | b2e3d87f | Nick Thomas | |
209 | b2e3d87f | Nick Thomas | TRACE("Receiving negotation.");
|
210 | b2e3d87f | Nick Thomas | |
211 | b2e3d87f | Nick Thomas | if (read_sync(csock, buf, 8) != 8) { |
212 | b2e3d87f | Nick Thomas | LOG("read failed");
|
213 | b2e3d87f | Nick Thomas | errno = EINVAL; |
214 | b2e3d87f | Nick Thomas | return -1; |
215 | b2e3d87f | Nick Thomas | } |
216 | b2e3d87f | Nick Thomas | |
217 | b2e3d87f | Nick Thomas | buf[8] = '\0'; |
218 | b2e3d87f | Nick Thomas | if (strlen(buf) == 0) { |
219 | b2e3d87f | Nick Thomas | LOG("server connection closed");
|
220 | b2e3d87f | Nick Thomas | errno = EINVAL; |
221 | b2e3d87f | Nick Thomas | return -1; |
222 | b2e3d87f | Nick Thomas | } |
223 | b2e3d87f | Nick Thomas | |
224 | b2e3d87f | Nick Thomas | TRACE("Magic is %c%c%c%c%c%c%c%c",
|
225 | b2e3d87f | Nick Thomas | qemu_isprint(buf[0]) ? buf[0] : '.', |
226 | b2e3d87f | Nick Thomas | qemu_isprint(buf[1]) ? buf[1] : '.', |
227 | b2e3d87f | Nick Thomas | qemu_isprint(buf[2]) ? buf[2] : '.', |
228 | b2e3d87f | Nick Thomas | qemu_isprint(buf[3]) ? buf[3] : '.', |
229 | b2e3d87f | Nick Thomas | qemu_isprint(buf[4]) ? buf[4] : '.', |
230 | b2e3d87f | Nick Thomas | qemu_isprint(buf[5]) ? buf[5] : '.', |
231 | b2e3d87f | Nick Thomas | qemu_isprint(buf[6]) ? buf[6] : '.', |
232 | b2e3d87f | Nick Thomas | qemu_isprint(buf[7]) ? buf[7] : '.'); |
233 | b2e3d87f | Nick Thomas | |
234 | b2e3d87f | Nick Thomas | if (memcmp(buf, "NBDMAGIC", 8) != 0) { |
235 | b2e3d87f | Nick Thomas | LOG("Invalid magic received");
|
236 | b2e3d87f | Nick Thomas | errno = EINVAL; |
237 | b2e3d87f | Nick Thomas | return -1; |
238 | b2e3d87f | Nick Thomas | } |
239 | b2e3d87f | Nick Thomas | |
240 | b2e3d87f | Nick Thomas | if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { |
241 | b2e3d87f | Nick Thomas | LOG("read failed");
|
242 | b2e3d87f | Nick Thomas | errno = EINVAL; |
243 | b2e3d87f | Nick Thomas | return -1; |
244 | b2e3d87f | Nick Thomas | } |
245 | b2e3d87f | Nick Thomas | magic = be64_to_cpu(magic); |
246 | b2e3d87f | Nick Thomas | TRACE("Magic is 0x%" PRIx64, magic);
|
247 | b2e3d87f | Nick Thomas | |
248 | b2e3d87f | Nick Thomas | if (name) {
|
249 | b2e3d87f | Nick Thomas | uint32_t reserved = 0;
|
250 | b2e3d87f | Nick Thomas | uint32_t opt; |
251 | b2e3d87f | Nick Thomas | uint32_t namesize; |
252 | b2e3d87f | Nick Thomas | |
253 | b2e3d87f | Nick Thomas | TRACE("Checking magic (opts_magic)");
|
254 | b2e3d87f | Nick Thomas | if (magic != 0x49484156454F5054LL) { |
255 | b2e3d87f | Nick Thomas | LOG("Bad magic received");
|
256 | b2e3d87f | Nick Thomas | errno = EINVAL; |
257 | b2e3d87f | Nick Thomas | return -1; |
258 | b2e3d87f | Nick Thomas | } |
259 | b2e3d87f | Nick Thomas | if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { |
260 | b2e3d87f | Nick Thomas | LOG("flags read failed");
|
261 | b2e3d87f | Nick Thomas | errno = EINVAL; |
262 | b2e3d87f | Nick Thomas | return -1; |
263 | b2e3d87f | Nick Thomas | } |
264 | b2e3d87f | Nick Thomas | *flags = be16_to_cpu(tmp) << 16;
|
265 | b2e3d87f | Nick Thomas | /* reserved for future use */
|
266 | b2e3d87f | Nick Thomas | if (write_sync(csock, &reserved, sizeof(reserved)) != |
267 | b2e3d87f | Nick Thomas | sizeof(reserved)) {
|
268 | b2e3d87f | Nick Thomas | LOG("write failed (reserved)");
|
269 | b2e3d87f | Nick Thomas | errno = EINVAL; |
270 | b2e3d87f | Nick Thomas | return -1; |
271 | b2e3d87f | Nick Thomas | } |
272 | b2e3d87f | Nick Thomas | /* write the export name */
|
273 | b2e3d87f | Nick Thomas | magic = cpu_to_be64(magic); |
274 | b2e3d87f | Nick Thomas | if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { |
275 | b2e3d87f | Nick Thomas | LOG("write failed (magic)");
|
276 | b2e3d87f | Nick Thomas | errno = EINVAL; |
277 | b2e3d87f | Nick Thomas | return -1; |
278 | b2e3d87f | Nick Thomas | } |
279 | b2e3d87f | Nick Thomas | opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); |
280 | b2e3d87f | Nick Thomas | if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { |
281 | b2e3d87f | Nick Thomas | LOG("write failed (opt)");
|
282 | b2e3d87f | Nick Thomas | errno = EINVAL; |
283 | b2e3d87f | Nick Thomas | return -1; |
284 | b2e3d87f | Nick Thomas | } |
285 | b2e3d87f | Nick Thomas | namesize = cpu_to_be32(strlen(name)); |
286 | b2e3d87f | Nick Thomas | if (write_sync(csock, &namesize, sizeof(namesize)) != |
287 | b2e3d87f | Nick Thomas | sizeof(namesize)) {
|
288 | b2e3d87f | Nick Thomas | LOG("write failed (namesize)");
|
289 | b2e3d87f | Nick Thomas | errno = EINVAL; |
290 | b2e3d87f | Nick Thomas | return -1; |
291 | b2e3d87f | Nick Thomas | } |
292 | b2e3d87f | Nick Thomas | if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) { |
293 | b2e3d87f | Nick Thomas | LOG("write failed (name)");
|
294 | b2e3d87f | Nick Thomas | errno = EINVAL; |
295 | b2e3d87f | Nick Thomas | return -1; |
296 | b2e3d87f | Nick Thomas | } |
297 | b2e3d87f | Nick Thomas | } else {
|
298 | b2e3d87f | Nick Thomas | TRACE("Checking magic (cli_magic)");
|
299 | b2e3d87f | Nick Thomas | |
300 | b2e3d87f | Nick Thomas | if (magic != 0x00420281861253LL) { |
301 | b2e3d87f | Nick Thomas | LOG("Bad magic received");
|
302 | b2e3d87f | Nick Thomas | errno = EINVAL; |
303 | b2e3d87f | Nick Thomas | return -1; |
304 | b2e3d87f | Nick Thomas | } |
305 | b2e3d87f | Nick Thomas | } |
306 | b2e3d87f | Nick Thomas | |
307 | b2e3d87f | Nick Thomas | if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) { |
308 | b2e3d87f | Nick Thomas | LOG("read failed");
|
309 | b2e3d87f | Nick Thomas | errno = EINVAL; |
310 | b2e3d87f | Nick Thomas | return -1; |
311 | b2e3d87f | Nick Thomas | } |
312 | b2e3d87f | Nick Thomas | *size = be64_to_cpu(s); |
313 | b2e3d87f | Nick Thomas | *blocksize = 1024;
|
314 | b2e3d87f | Nick Thomas | TRACE("Size is %" PRIu64, *size);
|
315 | b2e3d87f | Nick Thomas | |
316 | b2e3d87f | Nick Thomas | if (!name) {
|
317 | b2e3d87f | Nick Thomas | if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) { |
318 | b2e3d87f | Nick Thomas | LOG("read failed (flags)");
|
319 | b2e3d87f | Nick Thomas | errno = EINVAL; |
320 | b2e3d87f | Nick Thomas | return -1; |
321 | b2e3d87f | Nick Thomas | } |
322 | b2e3d87f | Nick Thomas | *flags = be32_to_cpup(flags); |
323 | b2e3d87f | Nick Thomas | } else {
|
324 | b2e3d87f | Nick Thomas | if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { |
325 | b2e3d87f | Nick Thomas | LOG("read failed (tmp)");
|
326 | b2e3d87f | Nick Thomas | errno = EINVAL; |
327 | b2e3d87f | Nick Thomas | return -1; |
328 | b2e3d87f | Nick Thomas | } |
329 | b2e3d87f | Nick Thomas | *flags |= be32_to_cpu(tmp); |
330 | b2e3d87f | Nick Thomas | } |
331 | b2e3d87f | Nick Thomas | if (read_sync(csock, &buf, 124) != 124) { |
332 | b2e3d87f | Nick Thomas | LOG("read failed (buf)");
|
333 | b2e3d87f | Nick Thomas | errno = EINVAL; |
334 | b2e3d87f | Nick Thomas | return -1; |
335 | b2e3d87f | Nick Thomas | } |
336 | cd831bd7 | ths | return 0; |
337 | cd831bd7 | ths | } |
338 | 7a5ca864 | bellard | |
339 | 03ff3ca3 | aliguori | #ifndef _WIN32
|
340 | cd831bd7 | ths | int nbd_init(int fd, int csock, off_t size, size_t blocksize) |
341 | cd831bd7 | ths | { |
342 | b2e3d87f | Nick Thomas | TRACE("Setting block size to %lu", (unsigned long)blocksize); |
343 | 7a5ca864 | bellard | |
344 | b2e3d87f | Nick Thomas | if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) { |
345 | b2e3d87f | Nick Thomas | int serrno = errno;
|
346 | b2e3d87f | Nick Thomas | LOG("Failed setting NBD block size");
|
347 | b2e3d87f | Nick Thomas | errno = serrno; |
348 | b2e3d87f | Nick Thomas | return -1; |
349 | b2e3d87f | Nick Thomas | } |
350 | 7a5ca864 | bellard | |
351 | 0bfcd599 | Blue Swirl | TRACE("Setting size to %zd block(s)", (size_t)(size / blocksize));
|
352 | 7a5ca864 | bellard | |
353 | b2e3d87f | Nick Thomas | if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) { |
354 | b2e3d87f | Nick Thomas | int serrno = errno;
|
355 | b2e3d87f | Nick Thomas | LOG("Failed setting size (in blocks)");
|
356 | b2e3d87f | Nick Thomas | errno = serrno; |
357 | b2e3d87f | Nick Thomas | return -1; |
358 | b2e3d87f | Nick Thomas | } |
359 | 7a5ca864 | bellard | |
360 | b2e3d87f | Nick Thomas | TRACE("Clearing NBD socket");
|
361 | 7a5ca864 | bellard | |
362 | b2e3d87f | Nick Thomas | if (ioctl(fd, NBD_CLEAR_SOCK) == -1) { |
363 | b2e3d87f | Nick Thomas | int serrno = errno;
|
364 | b2e3d87f | Nick Thomas | LOG("Failed clearing NBD socket");
|
365 | b2e3d87f | Nick Thomas | errno = serrno; |
366 | b2e3d87f | Nick Thomas | return -1; |
367 | b2e3d87f | Nick Thomas | } |
368 | 7a5ca864 | bellard | |
369 | b2e3d87f | Nick Thomas | TRACE("Setting NBD socket");
|
370 | 7a5ca864 | bellard | |
371 | b2e3d87f | Nick Thomas | if (ioctl(fd, NBD_SET_SOCK, csock) == -1) { |
372 | b2e3d87f | Nick Thomas | int serrno = errno;
|
373 | b2e3d87f | Nick Thomas | LOG("Failed to set NBD socket");
|
374 | b2e3d87f | Nick Thomas | errno = serrno; |
375 | b2e3d87f | Nick Thomas | return -1; |
376 | b2e3d87f | Nick Thomas | } |
377 | 7a5ca864 | bellard | |
378 | b2e3d87f | Nick Thomas | TRACE("Negotiation ended");
|
379 | 7a5ca864 | bellard | |
380 | b2e3d87f | Nick Thomas | return 0; |
381 | 7a5ca864 | bellard | } |
382 | 7a5ca864 | bellard | |
383 | 7a5ca864 | bellard | int nbd_disconnect(int fd) |
384 | 7a5ca864 | bellard | { |
385 | b2e3d87f | Nick Thomas | ioctl(fd, NBD_CLEAR_QUE); |
386 | b2e3d87f | Nick Thomas | ioctl(fd, NBD_DISCONNECT); |
387 | b2e3d87f | Nick Thomas | ioctl(fd, NBD_CLEAR_SOCK); |
388 | b2e3d87f | Nick Thomas | return 0; |
389 | 7a5ca864 | bellard | } |
390 | 7a5ca864 | bellard | |
391 | 0a4eb864 | Jes Sorensen | int nbd_client(int fd) |
392 | 7a5ca864 | bellard | { |
393 | b2e3d87f | Nick Thomas | int ret;
|
394 | b2e3d87f | Nick Thomas | int serrno;
|
395 | 7a5ca864 | bellard | |
396 | b2e3d87f | Nick Thomas | TRACE("Doing NBD loop");
|
397 | 7a5ca864 | bellard | |
398 | b2e3d87f | Nick Thomas | ret = ioctl(fd, NBD_DO_IT); |
399 | b2e3d87f | Nick Thomas | serrno = errno; |
400 | 7a5ca864 | bellard | |
401 | b2e3d87f | Nick Thomas | TRACE("NBD loop returned %d: %s", ret, strerror(serrno));
|
402 | 7a5ca864 | bellard | |
403 | b2e3d87f | Nick Thomas | TRACE("Clearing NBD queue");
|
404 | b2e3d87f | Nick Thomas | ioctl(fd, NBD_CLEAR_QUE); |
405 | 7a5ca864 | bellard | |
406 | b2e3d87f | Nick Thomas | TRACE("Clearing NBD socket");
|
407 | b2e3d87f | Nick Thomas | ioctl(fd, NBD_CLEAR_SOCK); |
408 | 7a5ca864 | bellard | |
409 | b2e3d87f | Nick Thomas | errno = serrno; |
410 | b2e3d87f | Nick Thomas | return ret;
|
411 | 7a5ca864 | bellard | } |
412 | 03ff3ca3 | aliguori | #else
|
413 | 03ff3ca3 | aliguori | int nbd_init(int fd, int csock, off_t size, size_t blocksize) |
414 | 03ff3ca3 | aliguori | { |
415 | 03ff3ca3 | aliguori | errno = ENOTSUP; |
416 | 03ff3ca3 | aliguori | return -1; |
417 | 03ff3ca3 | aliguori | } |
418 | 03ff3ca3 | aliguori | |
419 | 03ff3ca3 | aliguori | int nbd_disconnect(int fd) |
420 | 03ff3ca3 | aliguori | { |
421 | 03ff3ca3 | aliguori | errno = ENOTSUP; |
422 | 03ff3ca3 | aliguori | return -1; |
423 | 03ff3ca3 | aliguori | } |
424 | 03ff3ca3 | aliguori | |
425 | 0a4eb864 | Jes Sorensen | int nbd_client(int fd) |
426 | 03ff3ca3 | aliguori | { |
427 | 03ff3ca3 | aliguori | errno = ENOTSUP; |
428 | 03ff3ca3 | aliguori | return -1; |
429 | 03ff3ca3 | aliguori | } |
430 | 03ff3ca3 | aliguori | #endif
|
431 | 7a5ca864 | bellard | |
432 | 75818250 | ths | int nbd_send_request(int csock, struct nbd_request *request) |
433 | 7a5ca864 | bellard | { |
434 | b2e3d87f | Nick Thomas | uint8_t buf[4 + 4 + 8 + 8 + 4]; |
435 | b2e3d87f | Nick Thomas | |
436 | b2e3d87f | Nick Thomas | cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); |
437 | b2e3d87f | Nick Thomas | cpu_to_be32w((uint32_t*)(buf + 4), request->type);
|
438 | b2e3d87f | Nick Thomas | cpu_to_be64w((uint64_t*)(buf + 8), request->handle);
|
439 | b2e3d87f | Nick Thomas | cpu_to_be64w((uint64_t*)(buf + 16), request->from);
|
440 | b2e3d87f | Nick Thomas | cpu_to_be32w((uint32_t*)(buf + 24), request->len);
|
441 | 75818250 | ths | |
442 | b2e3d87f | Nick Thomas | TRACE("Sending request to client: "
|
443 | b2e3d87f | Nick Thomas | "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}", |
444 | b2e3d87f | Nick Thomas | request->from, request->len, request->handle, request->type); |
445 | b2e3d87f | Nick Thomas | |
446 | b2e3d87f | Nick Thomas | if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { |
447 | b2e3d87f | Nick Thomas | LOG("writing to socket failed");
|
448 | b2e3d87f | Nick Thomas | errno = EINVAL; |
449 | b2e3d87f | Nick Thomas | return -1; |
450 | b2e3d87f | Nick Thomas | } |
451 | b2e3d87f | Nick Thomas | return 0; |
452 | b2e3d87f | Nick Thomas | } |
453 | 75818250 | ths | |
454 | 75818250 | ths | static int nbd_receive_request(int csock, struct nbd_request *request) |
455 | 75818250 | ths | { |
456 | b2e3d87f | Nick Thomas | uint8_t buf[4 + 4 + 8 + 8 + 4]; |
457 | b2e3d87f | Nick Thomas | uint32_t magic; |
458 | b2e3d87f | Nick Thomas | |
459 | b2e3d87f | Nick Thomas | if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { |
460 | b2e3d87f | Nick Thomas | LOG("read failed");
|
461 | b2e3d87f | Nick Thomas | errno = EINVAL; |
462 | b2e3d87f | Nick Thomas | return -1; |
463 | b2e3d87f | Nick Thomas | } |
464 | b2e3d87f | Nick Thomas | |
465 | b2e3d87f | Nick Thomas | /* Request
|
466 | b2e3d87f | Nick Thomas | [ 0 .. 3] magic (NBD_REQUEST_MAGIC)
|
467 | b2e3d87f | Nick Thomas | [ 4 .. 7] type (0 == READ, 1 == WRITE)
|
468 | b2e3d87f | Nick Thomas | [ 8 .. 15] handle
|
469 | b2e3d87f | Nick Thomas | [16 .. 23] from
|
470 | b2e3d87f | Nick Thomas | [24 .. 27] len
|
471 | b2e3d87f | Nick Thomas | */
|
472 | b2e3d87f | Nick Thomas | |
473 | b2e3d87f | Nick Thomas | magic = be32_to_cpup((uint32_t*)buf); |
474 | b2e3d87f | Nick Thomas | request->type = be32_to_cpup((uint32_t*)(buf + 4));
|
475 | b2e3d87f | Nick Thomas | request->handle = be64_to_cpup((uint64_t*)(buf + 8));
|
476 | b2e3d87f | Nick Thomas | request->from = be64_to_cpup((uint64_t*)(buf + 16));
|
477 | b2e3d87f | Nick Thomas | request->len = be32_to_cpup((uint32_t*)(buf + 24));
|
478 | b2e3d87f | Nick Thomas | |
479 | b2e3d87f | Nick Thomas | TRACE("Got request: "
|
480 | b2e3d87f | Nick Thomas | "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }", |
481 | b2e3d87f | Nick Thomas | magic, request->type, request->from, request->len); |
482 | b2e3d87f | Nick Thomas | |
483 | b2e3d87f | Nick Thomas | if (magic != NBD_REQUEST_MAGIC) {
|
484 | b2e3d87f | Nick Thomas | LOG("invalid magic (got 0x%x)", magic);
|
485 | b2e3d87f | Nick Thomas | errno = EINVAL; |
486 | b2e3d87f | Nick Thomas | return -1; |
487 | b2e3d87f | Nick Thomas | } |
488 | b2e3d87f | Nick Thomas | return 0; |
489 | 75818250 | ths | } |
490 | 75818250 | ths | |
491 | 75818250 | ths | int nbd_receive_reply(int csock, struct nbd_reply *reply) |
492 | 75818250 | ths | { |
493 | b2e3d87f | Nick Thomas | uint8_t buf[NBD_REPLY_SIZE]; |
494 | b2e3d87f | Nick Thomas | uint32_t magic; |
495 | b2e3d87f | Nick Thomas | |
496 | b2e3d87f | Nick Thomas | memset(buf, 0xAA, sizeof(buf)); |
497 | b2e3d87f | Nick Thomas | |
498 | b2e3d87f | Nick Thomas | if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { |
499 | b2e3d87f | Nick Thomas | LOG("read failed");
|
500 | b2e3d87f | Nick Thomas | errno = EINVAL; |
501 | b2e3d87f | Nick Thomas | return -1; |
502 | b2e3d87f | Nick Thomas | } |
503 | b2e3d87f | Nick Thomas | |
504 | b2e3d87f | Nick Thomas | /* Reply
|
505 | b2e3d87f | Nick Thomas | [ 0 .. 3] magic (NBD_REPLY_MAGIC)
|
506 | b2e3d87f | Nick Thomas | [ 4 .. 7] error (0 == no error)
|
507 | b2e3d87f | Nick Thomas | [ 7 .. 15] handle
|
508 | b2e3d87f | Nick Thomas | */
|
509 | b2e3d87f | Nick Thomas | |
510 | b2e3d87f | Nick Thomas | magic = be32_to_cpup((uint32_t*)buf); |
511 | b2e3d87f | Nick Thomas | reply->error = be32_to_cpup((uint32_t*)(buf + 4));
|
512 | b2e3d87f | Nick Thomas | reply->handle = be64_to_cpup((uint64_t*)(buf + 8));
|
513 | b2e3d87f | Nick Thomas | |
514 | b2e3d87f | Nick Thomas | TRACE("Got reply: "
|
515 | b2e3d87f | Nick Thomas | "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }", |
516 | b2e3d87f | Nick Thomas | magic, reply->error, reply->handle); |
517 | b2e3d87f | Nick Thomas | |
518 | b2e3d87f | Nick Thomas | if (magic != NBD_REPLY_MAGIC) {
|
519 | b2e3d87f | Nick Thomas | LOG("invalid magic (got 0x%x)", magic);
|
520 | b2e3d87f | Nick Thomas | errno = EINVAL; |
521 | b2e3d87f | Nick Thomas | return -1; |
522 | b2e3d87f | Nick Thomas | } |
523 | b2e3d87f | Nick Thomas | return 0; |
524 | 75818250 | ths | } |
525 | 75818250 | ths | |
526 | 75818250 | ths | static int nbd_send_reply(int csock, struct nbd_reply *reply) |
527 | 75818250 | ths | { |
528 | b2e3d87f | Nick Thomas | uint8_t buf[4 + 4 + 8]; |
529 | b2e3d87f | Nick Thomas | |
530 | b2e3d87f | Nick Thomas | /* Reply
|
531 | b2e3d87f | Nick Thomas | [ 0 .. 3] magic (NBD_REPLY_MAGIC)
|
532 | b2e3d87f | Nick Thomas | [ 4 .. 7] error (0 == no error)
|
533 | b2e3d87f | Nick Thomas | [ 7 .. 15] handle
|
534 | b2e3d87f | Nick Thomas | */
|
535 | b2e3d87f | Nick Thomas | cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC); |
536 | b2e3d87f | Nick Thomas | cpu_to_be32w((uint32_t*)(buf + 4), reply->error);
|
537 | b2e3d87f | Nick Thomas | cpu_to_be64w((uint64_t*)(buf + 8), reply->handle);
|
538 | b2e3d87f | Nick Thomas | |
539 | b2e3d87f | Nick Thomas | TRACE("Sending response to client");
|
540 | b2e3d87f | Nick Thomas | |
541 | b2e3d87f | Nick Thomas | if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { |
542 | b2e3d87f | Nick Thomas | LOG("writing to socket failed");
|
543 | b2e3d87f | Nick Thomas | errno = EINVAL; |
544 | b2e3d87f | Nick Thomas | return -1; |
545 | b2e3d87f | Nick Thomas | } |
546 | b2e3d87f | Nick Thomas | return 0; |
547 | 75818250 | ths | } |
548 | 7a5ca864 | bellard | |
549 | 75818250 | ths | int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, |
550 | 75818250 | ths | off_t *offset, bool readonly, uint8_t *data, int data_size) |
551 | 75818250 | ths | { |
552 | b2e3d87f | Nick Thomas | struct nbd_request request;
|
553 | b2e3d87f | Nick Thomas | struct nbd_reply reply;
|
554 | b2e3d87f | Nick Thomas | |
555 | b2e3d87f | Nick Thomas | TRACE("Reading request.");
|
556 | b2e3d87f | Nick Thomas | |
557 | b2e3d87f | Nick Thomas | if (nbd_receive_request(csock, &request) == -1) |
558 | b2e3d87f | Nick Thomas | return -1; |
559 | b2e3d87f | Nick Thomas | |
560 | b2e3d87f | Nick Thomas | if (request.len + NBD_REPLY_SIZE > data_size) {
|
561 | b2e3d87f | Nick Thomas | LOG("len (%u) is larger than max len (%u)",
|
562 | b2e3d87f | Nick Thomas | request.len + NBD_REPLY_SIZE, data_size); |
563 | b2e3d87f | Nick Thomas | errno = EINVAL; |
564 | b2e3d87f | Nick Thomas | return -1; |
565 | b2e3d87f | Nick Thomas | } |
566 | b2e3d87f | Nick Thomas | |
567 | b2e3d87f | Nick Thomas | if ((request.from + request.len) < request.from) {
|
568 | b2e3d87f | Nick Thomas | LOG("integer overflow detected! "
|
569 | b2e3d87f | Nick Thomas | "you're probably being attacked");
|
570 | b2e3d87f | Nick Thomas | errno = EINVAL; |
571 | b2e3d87f | Nick Thomas | return -1; |
572 | b2e3d87f | Nick Thomas | } |
573 | b2e3d87f | Nick Thomas | |
574 | b2e3d87f | Nick Thomas | if ((request.from + request.len) > size) {
|
575 | b2e3d87f | Nick Thomas | LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 |
576 | b2e3d87f | Nick Thomas | ", Offset: %" PRIu64 "\n", |
577 | b9e82a59 | blueswir1 | request.from, request.len, (uint64_t)size, dev_offset); |
578 | b2e3d87f | Nick Thomas | LOG("requested operation past EOF--bad client?");
|
579 | b2e3d87f | Nick Thomas | errno = EINVAL; |
580 | b2e3d87f | Nick Thomas | return -1; |
581 | b2e3d87f | Nick Thomas | } |
582 | b2e3d87f | Nick Thomas | |
583 | b2e3d87f | Nick Thomas | TRACE("Decoding type");
|
584 | b2e3d87f | Nick Thomas | |
585 | b2e3d87f | Nick Thomas | reply.handle = request.handle; |
586 | b2e3d87f | Nick Thomas | reply.error = 0;
|
587 | b2e3d87f | Nick Thomas | |
588 | b2e3d87f | Nick Thomas | switch (request.type) {
|
589 | b2e3d87f | Nick Thomas | case NBD_CMD_READ:
|
590 | b2e3d87f | Nick Thomas | TRACE("Request type is READ");
|
591 | b2e3d87f | Nick Thomas | |
592 | b2e3d87f | Nick Thomas | if (bdrv_read(bs, (request.from + dev_offset) / 512, |
593 | b2e3d87f | Nick Thomas | data + NBD_REPLY_SIZE, |
594 | b2e3d87f | Nick Thomas | request.len / 512) == -1) { |
595 | b2e3d87f | Nick Thomas | LOG("reading from file failed");
|
596 | b2e3d87f | Nick Thomas | errno = EINVAL; |
597 | b2e3d87f | Nick Thomas | return -1; |
598 | b2e3d87f | Nick Thomas | } |
599 | b2e3d87f | Nick Thomas | *offset += request.len; |
600 | b2e3d87f | Nick Thomas | |
601 | b2e3d87f | Nick Thomas | TRACE("Read %u byte(s)", request.len);
|
602 | b2e3d87f | Nick Thomas | |
603 | b2e3d87f | Nick Thomas | /* Reply
|
604 | b2e3d87f | Nick Thomas | [ 0 .. 3] magic (NBD_REPLY_MAGIC)
|
605 | b2e3d87f | Nick Thomas | [ 4 .. 7] error (0 == no error)
|
606 | b2e3d87f | Nick Thomas | [ 7 .. 15] handle
|
607 | b2e3d87f | Nick Thomas | */
|
608 | b2e3d87f | Nick Thomas | |
609 | b2e3d87f | Nick Thomas | cpu_to_be32w((uint32_t*)data, NBD_REPLY_MAGIC); |
610 | b2e3d87f | Nick Thomas | cpu_to_be32w((uint32_t*)(data + 4), reply.error);
|
611 | b2e3d87f | Nick Thomas | cpu_to_be64w((uint64_t*)(data + 8), reply.handle);
|
612 | b2e3d87f | Nick Thomas | |
613 | b2e3d87f | Nick Thomas | TRACE("Sending data to client");
|
614 | b2e3d87f | Nick Thomas | |
615 | b2e3d87f | Nick Thomas | if (write_sync(csock, data,
|
616 | b2e3d87f | Nick Thomas | request.len + NBD_REPLY_SIZE) != |
617 | b2e3d87f | Nick Thomas | request.len + NBD_REPLY_SIZE) { |
618 | b2e3d87f | Nick Thomas | LOG("writing to socket failed");
|
619 | b2e3d87f | Nick Thomas | errno = EINVAL; |
620 | b2e3d87f | Nick Thomas | return -1; |
621 | b2e3d87f | Nick Thomas | } |
622 | b2e3d87f | Nick Thomas | break;
|
623 | b2e3d87f | Nick Thomas | case NBD_CMD_WRITE:
|
624 | b2e3d87f | Nick Thomas | TRACE("Request type is WRITE");
|
625 | b2e3d87f | Nick Thomas | |
626 | b2e3d87f | Nick Thomas | TRACE("Reading %u byte(s)", request.len);
|
627 | b2e3d87f | Nick Thomas | |
628 | b2e3d87f | Nick Thomas | if (read_sync(csock, data, request.len) != request.len) {
|
629 | b2e3d87f | Nick Thomas | LOG("reading from socket failed");
|
630 | b2e3d87f | Nick Thomas | errno = EINVAL; |
631 | b2e3d87f | Nick Thomas | return -1; |
632 | b2e3d87f | Nick Thomas | } |
633 | b2e3d87f | Nick Thomas | |
634 | b2e3d87f | Nick Thomas | if (readonly) {
|
635 | b2e3d87f | Nick Thomas | TRACE("Server is read-only, return error");
|
636 | b2e3d87f | Nick Thomas | reply.error = 1;
|
637 | b2e3d87f | Nick Thomas | } else {
|
638 | b2e3d87f | Nick Thomas | TRACE("Writing to device");
|
639 | b2e3d87f | Nick Thomas | |
640 | b2e3d87f | Nick Thomas | if (bdrv_write(bs, (request.from + dev_offset) / 512, |
641 | b2e3d87f | Nick Thomas | data, request.len / 512) == -1) { |
642 | b2e3d87f | Nick Thomas | LOG("writing to file failed");
|
643 | b2e3d87f | Nick Thomas | errno = EINVAL; |
644 | b2e3d87f | Nick Thomas | return -1; |
645 | b2e3d87f | Nick Thomas | } |
646 | b2e3d87f | Nick Thomas | |
647 | b2e3d87f | Nick Thomas | *offset += request.len; |
648 | b2e3d87f | Nick Thomas | } |
649 | b2e3d87f | Nick Thomas | |
650 | b2e3d87f | Nick Thomas | if (nbd_send_reply(csock, &reply) == -1) |
651 | b2e3d87f | Nick Thomas | return -1; |
652 | b2e3d87f | Nick Thomas | break;
|
653 | b2e3d87f | Nick Thomas | case NBD_CMD_DISC:
|
654 | b2e3d87f | Nick Thomas | TRACE("Request type is DISCONNECT");
|
655 | b2e3d87f | Nick Thomas | errno = 0;
|
656 | b2e3d87f | Nick Thomas | return 1; |
657 | b2e3d87f | Nick Thomas | default:
|
658 | b2e3d87f | Nick Thomas | LOG("invalid request type (%u) received", request.type);
|
659 | b2e3d87f | Nick Thomas | errno = EINVAL; |
660 | b2e3d87f | Nick Thomas | return -1; |
661 | b2e3d87f | Nick Thomas | } |
662 | b2e3d87f | Nick Thomas | |
663 | b2e3d87f | Nick Thomas | TRACE("Request/Reply complete");
|
664 | b2e3d87f | Nick Thomas | |
665 | b2e3d87f | Nick Thomas | return 0; |
666 | 7a5ca864 | bellard | } |