Statistics
| Branch: | Revision:

root / block / nbd.c @ 7267c094

History | View | Annotate | Download (6.9 kB)

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 "module.h"
32
#include "qemu_socket.h"
33

    
34
#include <sys/types.h>
35
#include <unistd.h>
36

    
37
#define EN_OPTSTR ":exportname="
38

    
39
/* #define DEBUG_NBD */
40

    
41
#if defined(DEBUG_NBD)
42
#define logout(fmt, ...) \
43
                fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
44
#else
45
#define logout(fmt, ...) ((void)0)
46
#endif
47

    
48
typedef struct BDRVNBDState {
49
    int sock;
50
    off_t size;
51
    size_t blocksize;
52
    char *export_name; /* An NBD server may export several devices */
53

    
54
    /* If it begins with  '/', this is a UNIX domain socket. Otherwise,
55
     * it's a string of the form <hostname|ip4|\[ip6\]>:port
56
     */
57
    char *host_spec;
58
} BDRVNBDState;
59

    
60
static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
61
{
62
    char *file;
63
    char *export_name;
64
    const char *host_spec;
65
    const char *unixpath;
66
    int err = -EINVAL;
67

    
68
    file = g_strdup(filename);
69

    
70
    export_name = strstr(file, EN_OPTSTR);
71
    if (export_name) {
72
        if (export_name[strlen(EN_OPTSTR)] == 0) {
73
            goto out;
74
        }
75
        export_name[0] = 0; /* truncate 'file' */
76
        export_name += strlen(EN_OPTSTR);
77
        s->export_name = g_strdup(export_name);
78
    }
79

    
80
    /* extract the host_spec - fail if it's not nbd:... */
81
    if (!strstart(file, "nbd:", &host_spec)) {
82
        goto out;
83
    }
84

    
85
    /* are we a UNIX or TCP socket? */
86
    if (strstart(host_spec, "unix:", &unixpath)) {
87
        if (unixpath[0] != '/') { /* We demand  an absolute path*/
88
            goto out;
89
        }
90
        s->host_spec = g_strdup(unixpath);
91
    } else {
92
        s->host_spec = g_strdup(host_spec);
93
    }
94

    
95
    err = 0;
96

    
97
out:
98
    g_free(file);
99
    if (err != 0) {
100
        g_free(s->export_name);
101
        g_free(s->host_spec);
102
    }
103
    return err;
104
}
105

    
106
static int nbd_establish_connection(BlockDriverState *bs)
107
{
108
    BDRVNBDState *s = bs->opaque;
109
    int sock;
110
    int ret;
111
    off_t size;
112
    size_t blocksize;
113
    uint32_t nbdflags;
114

    
115
    if (s->host_spec[0] == '/') {
116
        sock = unix_socket_outgoing(s->host_spec);
117
    } else {
118
        sock = tcp_socket_outgoing_spec(s->host_spec);
119
    }
120

    
121
    /* Failed to establish connection */
122
    if (sock == -1) {
123
        logout("Failed to establish connection to NBD server\n");
124
        return -errno;
125
    }
126

    
127
    /* NBD handshake */
128
    ret = nbd_receive_negotiate(sock, s->export_name, &nbdflags, &size,
129
                                &blocksize);
130
    if (ret == -1) {
131
        logout("Failed to negotiate with the NBD server\n");
132
        closesocket(sock);
133
        return -errno;
134
    }
135

    
136
    /* Now that we're connected, set the socket to be non-blocking */
137
    socket_set_nonblock(sock);
138

    
139
    s->sock = sock;
140
    s->size = size;
141
    s->blocksize = blocksize;
142

    
143
    logout("Established connection with NBD server\n");
144
    return 0;
145
}
146

    
147
static void nbd_teardown_connection(BlockDriverState *bs)
148
{
149
    BDRVNBDState *s = bs->opaque;
150
    struct nbd_request request;
151

    
152
    request.type = NBD_CMD_DISC;
153
    request.handle = (uint64_t)(intptr_t)bs;
154
    request.from = 0;
155
    request.len = 0;
156
    nbd_send_request(s->sock, &request);
157

    
158
    closesocket(s->sock);
159
}
160

    
161
static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
162
{
163
    BDRVNBDState *s = bs->opaque;
164
    int result;
165

    
166
    /* Pop the config into our state object. Exit if invalid. */
167
    result = nbd_config(s, filename, flags);
168
    if (result != 0) {
169
        return result;
170
    }
171

    
172
    /* establish TCP connection, return error if it fails
173
     * TODO: Configurable retry-until-timeout behaviour.
174
     */
175
    result = nbd_establish_connection(bs);
176

    
177
    return result;
178
}
179

    
180
static int nbd_read(BlockDriverState *bs, int64_t sector_num,
181
                    uint8_t *buf, int nb_sectors)
182
{
183
    BDRVNBDState *s = bs->opaque;
184
    struct nbd_request request;
185
    struct nbd_reply reply;
186

    
187
    request.type = NBD_CMD_READ;
188
    request.handle = (uint64_t)(intptr_t)bs;
189
    request.from = sector_num * 512;;
190
    request.len = nb_sectors * 512;
191

    
192
    if (nbd_send_request(s->sock, &request) == -1)
193
        return -errno;
194

    
195
    if (nbd_receive_reply(s->sock, &reply) == -1)
196
        return -errno;
197

    
198
    if (reply.error !=0)
199
        return -reply.error;
200

    
201
    if (reply.handle != request.handle)
202
        return -EIO;
203

    
204
    if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
205
        return -EIO;
206

    
207
    return 0;
208
}
209

    
210
static int nbd_write(BlockDriverState *bs, int64_t sector_num,
211
                     const uint8_t *buf, int nb_sectors)
212
{
213
    BDRVNBDState *s = bs->opaque;
214
    struct nbd_request request;
215
    struct nbd_reply reply;
216

    
217
    request.type = NBD_CMD_WRITE;
218
    request.handle = (uint64_t)(intptr_t)bs;
219
    request.from = sector_num * 512;;
220
    request.len = nb_sectors * 512;
221

    
222
    if (nbd_send_request(s->sock, &request) == -1)
223
        return -errno;
224

    
225
    if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
226
        return -EIO;
227

    
228
    if (nbd_receive_reply(s->sock, &reply) == -1)
229
        return -errno;
230

    
231
    if (reply.error !=0)
232
        return -reply.error;
233

    
234
    if (reply.handle != request.handle)
235
        return -EIO;
236

    
237
    return 0;
238
}
239

    
240
static void nbd_close(BlockDriverState *bs)
241
{
242
    BDRVNBDState *s = bs->opaque;
243
    g_free(s->export_name);
244
    g_free(s->host_spec);
245

    
246
    nbd_teardown_connection(bs);
247
}
248

    
249
static int64_t nbd_getlength(BlockDriverState *bs)
250
{
251
    BDRVNBDState *s = bs->opaque;
252

    
253
    return s->size;
254
}
255

    
256
static BlockDriver bdrv_nbd = {
257
    .format_name        = "nbd",
258
    .instance_size        = sizeof(BDRVNBDState),
259
    .bdrv_file_open        = nbd_open,
260
    .bdrv_read                = nbd_read,
261
    .bdrv_write                = nbd_write,
262
    .bdrv_close                = nbd_close,
263
    .bdrv_getlength        = nbd_getlength,
264
    .protocol_name        = "nbd",
265
};
266

    
267
static void bdrv_nbd_init(void)
268
{
269
    bdrv_register(&bdrv_nbd);
270
}
271

    
272
block_init(bdrv_nbd_init);