xref: /openbmc/qemu/block/nbd.c (revision d2d979c6)
1019d6b8fSAnthony Liguori /*
2019d6b8fSAnthony Liguori  * QEMU Block driver for  NBD
3019d6b8fSAnthony Liguori  *
4019d6b8fSAnthony Liguori  * Copyright (C) 2008 Bull S.A.S.
5019d6b8fSAnthony Liguori  *     Author: Laurent Vivier <Laurent.Vivier@bull.net>
6019d6b8fSAnthony Liguori  *
7019d6b8fSAnthony Liguori  * Some parts:
8019d6b8fSAnthony Liguori  *    Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
9019d6b8fSAnthony Liguori  *
10019d6b8fSAnthony Liguori  * Permission is hereby granted, free of charge, to any person obtaining a copy
11019d6b8fSAnthony Liguori  * of this software and associated documentation files (the "Software"), to deal
12019d6b8fSAnthony Liguori  * in the Software without restriction, including without limitation the rights
13019d6b8fSAnthony Liguori  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14019d6b8fSAnthony Liguori  * copies of the Software, and to permit persons to whom the Software is
15019d6b8fSAnthony Liguori  * furnished to do so, subject to the following conditions:
16019d6b8fSAnthony Liguori  *
17019d6b8fSAnthony Liguori  * The above copyright notice and this permission notice shall be included in
18019d6b8fSAnthony Liguori  * all copies or substantial portions of the Software.
19019d6b8fSAnthony Liguori  *
20019d6b8fSAnthony Liguori  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21019d6b8fSAnthony Liguori  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22019d6b8fSAnthony Liguori  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23019d6b8fSAnthony Liguori  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24019d6b8fSAnthony Liguori  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25019d6b8fSAnthony Liguori  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26019d6b8fSAnthony Liguori  * THE SOFTWARE.
27019d6b8fSAnthony Liguori  */
28019d6b8fSAnthony Liguori 
29019d6b8fSAnthony Liguori #include "qemu-common.h"
30019d6b8fSAnthony Liguori #include "nbd.h"
31019d6b8fSAnthony Liguori #include "module.h"
3233897dc7SNick Thomas #include "qemu_socket.h"
33019d6b8fSAnthony Liguori 
34019d6b8fSAnthony Liguori #include <sys/types.h>
35019d6b8fSAnthony Liguori #include <unistd.h>
36019d6b8fSAnthony Liguori 
371d45f8b5SLaurent Vivier #define EN_OPTSTR ":exportname="
381d45f8b5SLaurent Vivier 
3933897dc7SNick Thomas /* #define DEBUG_NBD */
4033897dc7SNick Thomas 
4133897dc7SNick Thomas #if defined(DEBUG_NBD)
4233897dc7SNick Thomas #define logout(fmt, ...) \
4333897dc7SNick Thomas                 fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
4433897dc7SNick Thomas #else
4533897dc7SNick Thomas #define logout(fmt, ...) ((void)0)
4633897dc7SNick Thomas #endif
4733897dc7SNick Thomas 
48019d6b8fSAnthony Liguori typedef struct BDRVNBDState {
49019d6b8fSAnthony Liguori     int sock;
50019d6b8fSAnthony Liguori     off_t size;
51019d6b8fSAnthony Liguori     size_t blocksize;
5233897dc7SNick Thomas     char *export_name; /* An NBD server may export several devices */
5333897dc7SNick Thomas 
5433897dc7SNick Thomas     /* If it begins with  '/', this is a UNIX domain socket. Otherwise,
5533897dc7SNick Thomas      * it's a string of the form <hostname|ip4|\[ip6\]>:port
5633897dc7SNick Thomas      */
5733897dc7SNick Thomas     char *host_spec;
58019d6b8fSAnthony Liguori } BDRVNBDState;
59019d6b8fSAnthony Liguori 
6033897dc7SNick Thomas static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
61019d6b8fSAnthony Liguori {
621d45f8b5SLaurent Vivier     char *file;
6333897dc7SNick Thomas     char *export_name;
6433897dc7SNick Thomas     const char *host_spec;
65019d6b8fSAnthony Liguori     const char *unixpath;
661d45f8b5SLaurent Vivier     int err = -EINVAL;
67019d6b8fSAnthony Liguori 
681d45f8b5SLaurent Vivier     file = qemu_strdup(filename);
691d45f8b5SLaurent Vivier 
7033897dc7SNick Thomas     export_name = strstr(file, EN_OPTSTR);
7133897dc7SNick Thomas     if (export_name) {
7233897dc7SNick Thomas         if (export_name[strlen(EN_OPTSTR)] == 0) {
731d45f8b5SLaurent Vivier             goto out;
741d45f8b5SLaurent Vivier         }
7533897dc7SNick Thomas         export_name[0] = 0; /* truncate 'file' */
7633897dc7SNick Thomas         export_name += strlen(EN_OPTSTR);
7733897dc7SNick Thomas         s->export_name = qemu_strdup(export_name);
781d45f8b5SLaurent Vivier     }
791d45f8b5SLaurent Vivier 
8033897dc7SNick Thomas     /* extract the host_spec - fail if it's not nbd:... */
8133897dc7SNick Thomas     if (!strstart(file, "nbd:", &host_spec)) {
821d45f8b5SLaurent Vivier         goto out;
831d45f8b5SLaurent Vivier     }
84019d6b8fSAnthony Liguori 
8533897dc7SNick Thomas     /* are we a UNIX or TCP socket? */
8633897dc7SNick Thomas     if (strstart(host_spec, "unix:", &unixpath)) {
8733897dc7SNick Thomas         if (unixpath[0] != '/') { /* We demand  an absolute path*/
881d45f8b5SLaurent Vivier             goto out;
891d45f8b5SLaurent Vivier         }
9033897dc7SNick Thomas         s->host_spec = qemu_strdup(unixpath);
91019d6b8fSAnthony Liguori     } else {
9233897dc7SNick Thomas         s->host_spec = qemu_strdup(host_spec);
931d45f8b5SLaurent Vivier     }
941d45f8b5SLaurent Vivier 
951d45f8b5SLaurent Vivier     err = 0;
96019d6b8fSAnthony Liguori 
971d45f8b5SLaurent Vivier out:
981d45f8b5SLaurent Vivier     qemu_free(file);
9933897dc7SNick Thomas     if (err != 0) {
10033897dc7SNick Thomas         qemu_free(s->export_name);
10133897dc7SNick Thomas         qemu_free(s->host_spec);
10233897dc7SNick Thomas     }
1031d45f8b5SLaurent Vivier     return err;
104019d6b8fSAnthony Liguori }
105019d6b8fSAnthony Liguori 
10633897dc7SNick Thomas static int nbd_establish_connection(BlockDriverState *bs)
10733897dc7SNick Thomas {
10833897dc7SNick Thomas     BDRVNBDState *s = bs->opaque;
10933897dc7SNick Thomas     int sock;
11033897dc7SNick Thomas     int ret;
11133897dc7SNick Thomas     off_t size;
11233897dc7SNick Thomas     size_t blocksize;
11333897dc7SNick Thomas     uint32_t nbdflags;
11433897dc7SNick Thomas 
11533897dc7SNick Thomas     if (s->host_spec[0] == '/') {
11633897dc7SNick Thomas         sock = unix_socket_outgoing(s->host_spec);
11733897dc7SNick Thomas     } else {
11833897dc7SNick Thomas         sock = tcp_socket_outgoing_spec(s->host_spec);
11933897dc7SNick Thomas     }
12033897dc7SNick Thomas 
12133897dc7SNick Thomas     /* Failed to establish connection */
12233897dc7SNick Thomas     if (sock == -1) {
12333897dc7SNick Thomas         logout("Failed to establish connection to NBD server\n");
12433897dc7SNick Thomas         return -errno;
12533897dc7SNick Thomas     }
12633897dc7SNick Thomas 
12733897dc7SNick Thomas     /* NBD handshake */
12833897dc7SNick Thomas     ret = nbd_receive_negotiate(sock, s->export_name, &nbdflags, &size,
12933897dc7SNick Thomas                                 &blocksize);
13033897dc7SNick Thomas     if (ret == -1) {
13133897dc7SNick Thomas         logout("Failed to negotiate with the NBD server\n");
13233897dc7SNick Thomas         closesocket(sock);
13333897dc7SNick Thomas         return -errno;
13433897dc7SNick Thomas     }
13533897dc7SNick Thomas 
13633897dc7SNick Thomas     /* Now that we're connected, set the socket to be non-blocking */
13733897dc7SNick Thomas     socket_set_nonblock(sock);
13833897dc7SNick Thomas 
13933897dc7SNick Thomas     s->sock = sock;
14033897dc7SNick Thomas     s->size = size;
14133897dc7SNick Thomas     s->blocksize = blocksize;
14233897dc7SNick Thomas 
14333897dc7SNick Thomas     logout("Established connection with NBD server\n");
14433897dc7SNick Thomas     return 0;
14533897dc7SNick Thomas }
14633897dc7SNick Thomas 
14733897dc7SNick Thomas static void nbd_teardown_connection(BlockDriverState *bs)
14833897dc7SNick Thomas {
14933897dc7SNick Thomas     BDRVNBDState *s = bs->opaque;
15033897dc7SNick Thomas     struct nbd_request request;
15133897dc7SNick Thomas 
15233897dc7SNick Thomas     request.type = NBD_CMD_DISC;
15333897dc7SNick Thomas     request.handle = (uint64_t)(intptr_t)bs;
15433897dc7SNick Thomas     request.from = 0;
15533897dc7SNick Thomas     request.len = 0;
15633897dc7SNick Thomas     nbd_send_request(s->sock, &request);
15733897dc7SNick Thomas 
15833897dc7SNick Thomas     closesocket(s->sock);
15933897dc7SNick Thomas }
16033897dc7SNick Thomas 
16133897dc7SNick Thomas static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
16233897dc7SNick Thomas {
16333897dc7SNick Thomas     BDRVNBDState *s = bs->opaque;
16433897dc7SNick Thomas     int result;
16533897dc7SNick Thomas 
16633897dc7SNick Thomas     /* Pop the config into our state object. Exit if invalid. */
16733897dc7SNick Thomas     result = nbd_config(s, filename, flags);
16833897dc7SNick Thomas     if (result != 0) {
16933897dc7SNick Thomas         return result;
17033897dc7SNick Thomas     }
17133897dc7SNick Thomas 
17233897dc7SNick Thomas     /* establish TCP connection, return error if it fails
17333897dc7SNick Thomas      * TODO: Configurable retry-until-timeout behaviour.
17433897dc7SNick Thomas      */
17533897dc7SNick Thomas     result = nbd_establish_connection(bs);
17633897dc7SNick Thomas 
17733897dc7SNick Thomas     return result;
17833897dc7SNick Thomas }
17933897dc7SNick Thomas 
180019d6b8fSAnthony Liguori static int nbd_read(BlockDriverState *bs, int64_t sector_num,
181019d6b8fSAnthony Liguori                     uint8_t *buf, int nb_sectors)
182019d6b8fSAnthony Liguori {
183019d6b8fSAnthony Liguori     BDRVNBDState *s = bs->opaque;
184019d6b8fSAnthony Liguori     struct nbd_request request;
185019d6b8fSAnthony Liguori     struct nbd_reply reply;
186019d6b8fSAnthony Liguori 
187019d6b8fSAnthony Liguori     request.type = NBD_CMD_READ;
188019d6b8fSAnthony Liguori     request.handle = (uint64_t)(intptr_t)bs;
189019d6b8fSAnthony Liguori     request.from = sector_num * 512;;
190019d6b8fSAnthony Liguori     request.len = nb_sectors * 512;
191019d6b8fSAnthony Liguori 
192019d6b8fSAnthony Liguori     if (nbd_send_request(s->sock, &request) == -1)
193019d6b8fSAnthony Liguori         return -errno;
194019d6b8fSAnthony Liguori 
195019d6b8fSAnthony Liguori     if (nbd_receive_reply(s->sock, &reply) == -1)
196019d6b8fSAnthony Liguori         return -errno;
197019d6b8fSAnthony Liguori 
198019d6b8fSAnthony Liguori     if (reply.error !=0)
199019d6b8fSAnthony Liguori         return -reply.error;
200019d6b8fSAnthony Liguori 
201019d6b8fSAnthony Liguori     if (reply.handle != request.handle)
202019d6b8fSAnthony Liguori         return -EIO;
203019d6b8fSAnthony Liguori 
204019d6b8fSAnthony Liguori     if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
205019d6b8fSAnthony Liguori         return -EIO;
206019d6b8fSAnthony Liguori 
207019d6b8fSAnthony Liguori     return 0;
208019d6b8fSAnthony Liguori }
209019d6b8fSAnthony Liguori 
210019d6b8fSAnthony Liguori static int nbd_write(BlockDriverState *bs, int64_t sector_num,
211019d6b8fSAnthony Liguori                      const uint8_t *buf, int nb_sectors)
212019d6b8fSAnthony Liguori {
213019d6b8fSAnthony Liguori     BDRVNBDState *s = bs->opaque;
214019d6b8fSAnthony Liguori     struct nbd_request request;
215019d6b8fSAnthony Liguori     struct nbd_reply reply;
216019d6b8fSAnthony Liguori 
217019d6b8fSAnthony Liguori     request.type = NBD_CMD_WRITE;
218019d6b8fSAnthony Liguori     request.handle = (uint64_t)(intptr_t)bs;
219019d6b8fSAnthony Liguori     request.from = sector_num * 512;;
220019d6b8fSAnthony Liguori     request.len = nb_sectors * 512;
221019d6b8fSAnthony Liguori 
222019d6b8fSAnthony Liguori     if (nbd_send_request(s->sock, &request) == -1)
223019d6b8fSAnthony Liguori         return -errno;
224019d6b8fSAnthony Liguori 
225019d6b8fSAnthony Liguori     if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
226019d6b8fSAnthony Liguori         return -EIO;
227019d6b8fSAnthony Liguori 
228019d6b8fSAnthony Liguori     if (nbd_receive_reply(s->sock, &reply) == -1)
229019d6b8fSAnthony Liguori         return -errno;
230019d6b8fSAnthony Liguori 
231019d6b8fSAnthony Liguori     if (reply.error !=0)
232019d6b8fSAnthony Liguori         return -reply.error;
233019d6b8fSAnthony Liguori 
234019d6b8fSAnthony Liguori     if (reply.handle != request.handle)
235019d6b8fSAnthony Liguori         return -EIO;
236019d6b8fSAnthony Liguori 
237019d6b8fSAnthony Liguori     return 0;
238019d6b8fSAnthony Liguori }
239019d6b8fSAnthony Liguori 
240019d6b8fSAnthony Liguori static void nbd_close(BlockDriverState *bs)
241019d6b8fSAnthony Liguori {
242*d2d979c6SNick Thomas     BDRVNBDState *s = bs->opaque;
243*d2d979c6SNick Thomas     qemu_free(s->export_name);
244*d2d979c6SNick Thomas     qemu_free(s->host_spec);
245*d2d979c6SNick Thomas 
24633897dc7SNick Thomas     nbd_teardown_connection(bs);
247019d6b8fSAnthony Liguori }
248019d6b8fSAnthony Liguori 
249019d6b8fSAnthony Liguori static int64_t nbd_getlength(BlockDriverState *bs)
250019d6b8fSAnthony Liguori {
251019d6b8fSAnthony Liguori     BDRVNBDState *s = bs->opaque;
252019d6b8fSAnthony Liguori 
253019d6b8fSAnthony Liguori     return s->size;
254019d6b8fSAnthony Liguori }
255019d6b8fSAnthony Liguori 
256019d6b8fSAnthony Liguori static BlockDriver bdrv_nbd = {
257019d6b8fSAnthony Liguori     .format_name	= "nbd",
258019d6b8fSAnthony Liguori     .instance_size	= sizeof(BDRVNBDState),
25966f82ceeSKevin Wolf     .bdrv_file_open	= nbd_open,
260019d6b8fSAnthony Liguori     .bdrv_read		= nbd_read,
261019d6b8fSAnthony Liguori     .bdrv_write		= nbd_write,
262019d6b8fSAnthony Liguori     .bdrv_close		= nbd_close,
263019d6b8fSAnthony Liguori     .bdrv_getlength	= nbd_getlength,
264019d6b8fSAnthony Liguori     .protocol_name	= "nbd",
265019d6b8fSAnthony Liguori };
266019d6b8fSAnthony Liguori 
267019d6b8fSAnthony Liguori static void bdrv_nbd_init(void)
268019d6b8fSAnthony Liguori {
269019d6b8fSAnthony Liguori     bdrv_register(&bdrv_nbd);
270019d6b8fSAnthony Liguori }
271019d6b8fSAnthony Liguori 
272019d6b8fSAnthony Liguori block_init(bdrv_nbd_init);
273