xref: /openbmc/qemu/block/nbd.c (revision 776574d6)
1 /*
2  * QEMU Block driver for  NBD
3  *
4  * Copyright (C) 2008 Bull S.A.S.
5  *     Author: Laurent Vivier <Laurent.Vivier@bull.net>
6  *
7  * Some parts:
8  *    Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  */
28 
29 #include "qemu-common.h"
30 #include "nbd.h"
31 #include "block_int.h"
32 #include "module.h"
33 #include "qemu_socket.h"
34 
35 #include <sys/types.h>
36 #include <unistd.h>
37 
38 #define EN_OPTSTR ":exportname="
39 
40 /* #define DEBUG_NBD */
41 
42 #if defined(DEBUG_NBD)
43 #define logout(fmt, ...) \
44                 fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
45 #else
46 #define logout(fmt, ...) ((void)0)
47 #endif
48 
49 typedef struct BDRVNBDState {
50     int sock;
51     uint32_t nbdflags;
52     off_t size;
53     size_t blocksize;
54     char *export_name; /* An NBD server may export several devices */
55 
56     /* If it begins with  '/', this is a UNIX domain socket. Otherwise,
57      * it's a string of the form <hostname|ip4|\[ip6\]>:port
58      */
59     char *host_spec;
60 } BDRVNBDState;
61 
62 static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
63 {
64     char *file;
65     char *export_name;
66     const char *host_spec;
67     const char *unixpath;
68     int err = -EINVAL;
69 
70     file = g_strdup(filename);
71 
72     export_name = strstr(file, EN_OPTSTR);
73     if (export_name) {
74         if (export_name[strlen(EN_OPTSTR)] == 0) {
75             goto out;
76         }
77         export_name[0] = 0; /* truncate 'file' */
78         export_name += strlen(EN_OPTSTR);
79         s->export_name = g_strdup(export_name);
80     }
81 
82     /* extract the host_spec - fail if it's not nbd:... */
83     if (!strstart(file, "nbd:", &host_spec)) {
84         goto out;
85     }
86 
87     /* are we a UNIX or TCP socket? */
88     if (strstart(host_spec, "unix:", &unixpath)) {
89         if (unixpath[0] != '/') { /* We demand  an absolute path*/
90             goto out;
91         }
92         s->host_spec = g_strdup(unixpath);
93     } else {
94         s->host_spec = g_strdup(host_spec);
95     }
96 
97     err = 0;
98 
99 out:
100     g_free(file);
101     if (err != 0) {
102         g_free(s->export_name);
103         g_free(s->host_spec);
104     }
105     return err;
106 }
107 
108 static int nbd_establish_connection(BlockDriverState *bs)
109 {
110     BDRVNBDState *s = bs->opaque;
111     int sock;
112     int ret;
113     off_t size;
114     size_t blocksize;
115 
116     if (s->host_spec[0] == '/') {
117         sock = unix_socket_outgoing(s->host_spec);
118     } else {
119         sock = tcp_socket_outgoing_spec(s->host_spec);
120     }
121 
122     /* Failed to establish connection */
123     if (sock == -1) {
124         logout("Failed to establish connection to NBD server\n");
125         return -errno;
126     }
127 
128     /* NBD handshake */
129     ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size,
130                                 &blocksize);
131     if (ret == -1) {
132         logout("Failed to negotiate with the NBD server\n");
133         closesocket(sock);
134         return -errno;
135     }
136 
137     /* Now that we're connected, set the socket to be non-blocking */
138     socket_set_nonblock(sock);
139 
140     s->sock = sock;
141     s->size = size;
142     s->blocksize = blocksize;
143 
144     logout("Established connection with NBD server\n");
145     return 0;
146 }
147 
148 static void nbd_teardown_connection(BlockDriverState *bs)
149 {
150     BDRVNBDState *s = bs->opaque;
151     struct nbd_request request;
152 
153     request.type = NBD_CMD_DISC;
154     request.handle = (uint64_t)(intptr_t)bs;
155     request.from = 0;
156     request.len = 0;
157     nbd_send_request(s->sock, &request);
158 
159     closesocket(s->sock);
160 }
161 
162 static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
163 {
164     BDRVNBDState *s = bs->opaque;
165     int result;
166 
167     /* Pop the config into our state object. Exit if invalid. */
168     result = nbd_config(s, filename, flags);
169     if (result != 0) {
170         return result;
171     }
172 
173     /* establish TCP connection, return error if it fails
174      * TODO: Configurable retry-until-timeout behaviour.
175      */
176     result = nbd_establish_connection(bs);
177 
178     return result;
179 }
180 
181 static int nbd_read(BlockDriverState *bs, int64_t sector_num,
182                     uint8_t *buf, int nb_sectors)
183 {
184     BDRVNBDState *s = bs->opaque;
185     struct nbd_request request;
186     struct nbd_reply reply;
187 
188     request.type = NBD_CMD_READ;
189     request.handle = (uint64_t)(intptr_t)bs;
190     request.from = sector_num * 512;;
191     request.len = nb_sectors * 512;
192 
193     if (nbd_send_request(s->sock, &request) == -1)
194         return -errno;
195 
196     if (nbd_receive_reply(s->sock, &reply) == -1)
197         return -errno;
198 
199     if (reply.error !=0)
200         return -reply.error;
201 
202     if (reply.handle != request.handle)
203         return -EIO;
204 
205     if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
206         return -EIO;
207 
208     return 0;
209 }
210 
211 static int nbd_write(BlockDriverState *bs, int64_t sector_num,
212                      const uint8_t *buf, int nb_sectors)
213 {
214     BDRVNBDState *s = bs->opaque;
215     struct nbd_request request;
216     struct nbd_reply reply;
217 
218     request.type = NBD_CMD_WRITE;
219     request.handle = (uint64_t)(intptr_t)bs;
220     request.from = sector_num * 512;;
221     request.len = nb_sectors * 512;
222 
223     if (nbd_send_request(s->sock, &request) == -1)
224         return -errno;
225 
226     if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
227         return -EIO;
228 
229     if (nbd_receive_reply(s->sock, &reply) == -1)
230         return -errno;
231 
232     if (reply.error !=0)
233         return -reply.error;
234 
235     if (reply.handle != request.handle)
236         return -EIO;
237 
238     return 0;
239 }
240 
241 static void nbd_close(BlockDriverState *bs)
242 {
243     BDRVNBDState *s = bs->opaque;
244     g_free(s->export_name);
245     g_free(s->host_spec);
246 
247     nbd_teardown_connection(bs);
248 }
249 
250 static int64_t nbd_getlength(BlockDriverState *bs)
251 {
252     BDRVNBDState *s = bs->opaque;
253 
254     return s->size;
255 }
256 
257 static BlockDriver bdrv_nbd = {
258     .format_name	= "nbd",
259     .instance_size	= sizeof(BDRVNBDState),
260     .bdrv_file_open	= nbd_open,
261     .bdrv_read		= nbd_read,
262     .bdrv_write		= nbd_write,
263     .bdrv_close		= nbd_close,
264     .bdrv_getlength	= nbd_getlength,
265     .protocol_name	= "nbd",
266 };
267 
268 static void bdrv_nbd_init(void)
269 {
270     bdrv_register(&bdrv_nbd);
271 }
272 
273 block_init(bdrv_nbd_init);
274