1 /* 2 * QEMU Block driver for native access to files on NFS shares 3 * 4 * Copyright (c) 2014 Peter Lieven <pl@kamp.de> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "config-host.h" 26 27 #include <poll.h> 28 #include "qemu-common.h" 29 #include "qemu/config-file.h" 30 #include "qemu/error-report.h" 31 #include "block/block_int.h" 32 #include "trace.h" 33 #include "qemu/iov.h" 34 #include "qemu/uri.h" 35 #include "sysemu/sysemu.h" 36 #include <nfsc/libnfs.h> 37 38 typedef struct NFSClient { 39 struct nfs_context *context; 40 struct nfsfh *fh; 41 int events; 42 bool has_zero_init; 43 } NFSClient; 44 45 typedef struct NFSRPC { 46 int ret; 47 int complete; 48 QEMUIOVector *iov; 49 struct stat *st; 50 Coroutine *co; 51 QEMUBH *bh; 52 } NFSRPC; 53 54 static void nfs_process_read(void *arg); 55 static void nfs_process_write(void *arg); 56 57 static void nfs_set_events(NFSClient *client) 58 { 59 int ev = nfs_which_events(client->context); 60 if (ev != client->events) { 61 qemu_aio_set_fd_handler(nfs_get_fd(client->context), 62 (ev & POLLIN) ? nfs_process_read : NULL, 63 (ev & POLLOUT) ? nfs_process_write : NULL, 64 client); 65 66 } 67 client->events = ev; 68 } 69 70 static void nfs_process_read(void *arg) 71 { 72 NFSClient *client = arg; 73 nfs_service(client->context, POLLIN); 74 nfs_set_events(client); 75 } 76 77 static void nfs_process_write(void *arg) 78 { 79 NFSClient *client = arg; 80 nfs_service(client->context, POLLOUT); 81 nfs_set_events(client); 82 } 83 84 static void nfs_co_init_task(NFSClient *client, NFSRPC *task) 85 { 86 *task = (NFSRPC) { 87 .co = qemu_coroutine_self(), 88 }; 89 } 90 91 static void nfs_co_generic_bh_cb(void *opaque) 92 { 93 NFSRPC *task = opaque; 94 qemu_bh_delete(task->bh); 95 qemu_coroutine_enter(task->co, NULL); 96 } 97 98 static void 99 nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, 100 void *private_data) 101 { 102 NFSRPC *task = private_data; 103 task->complete = 1; 104 task->ret = ret; 105 if (task->ret > 0 && task->iov) { 106 if (task->ret <= task->iov->size) { 107 qemu_iovec_from_buf(task->iov, 0, data, task->ret); 108 } else { 109 task->ret = -EIO; 110 } 111 } 112 if (task->ret == 0 && task->st) { 113 memcpy(task->st, data, sizeof(struct stat)); 114 } 115 if (task->co) { 116 task->bh = qemu_bh_new(nfs_co_generic_bh_cb, task); 117 qemu_bh_schedule(task->bh); 118 } 119 } 120 121 static int coroutine_fn nfs_co_readv(BlockDriverState *bs, 122 int64_t sector_num, int nb_sectors, 123 QEMUIOVector *iov) 124 { 125 NFSClient *client = bs->opaque; 126 NFSRPC task; 127 128 nfs_co_init_task(client, &task); 129 task.iov = iov; 130 131 if (nfs_pread_async(client->context, client->fh, 132 sector_num * BDRV_SECTOR_SIZE, 133 nb_sectors * BDRV_SECTOR_SIZE, 134 nfs_co_generic_cb, &task) != 0) { 135 return -ENOMEM; 136 } 137 138 while (!task.complete) { 139 nfs_set_events(client); 140 qemu_coroutine_yield(); 141 } 142 143 if (task.ret < 0) { 144 return task.ret; 145 } 146 147 /* zero pad short reads */ 148 if (task.ret < iov->size) { 149 qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret); 150 } 151 152 return 0; 153 } 154 155 static int coroutine_fn nfs_co_writev(BlockDriverState *bs, 156 int64_t sector_num, int nb_sectors, 157 QEMUIOVector *iov) 158 { 159 NFSClient *client = bs->opaque; 160 NFSRPC task; 161 char *buf = NULL; 162 163 nfs_co_init_task(client, &task); 164 165 buf = g_malloc(nb_sectors * BDRV_SECTOR_SIZE); 166 qemu_iovec_to_buf(iov, 0, buf, nb_sectors * BDRV_SECTOR_SIZE); 167 168 if (nfs_pwrite_async(client->context, client->fh, 169 sector_num * BDRV_SECTOR_SIZE, 170 nb_sectors * BDRV_SECTOR_SIZE, 171 buf, nfs_co_generic_cb, &task) != 0) { 172 g_free(buf); 173 return -ENOMEM; 174 } 175 176 while (!task.complete) { 177 nfs_set_events(client); 178 qemu_coroutine_yield(); 179 } 180 181 g_free(buf); 182 183 if (task.ret != nb_sectors * BDRV_SECTOR_SIZE) { 184 return task.ret < 0 ? task.ret : -EIO; 185 } 186 187 return 0; 188 } 189 190 static int coroutine_fn nfs_co_flush(BlockDriverState *bs) 191 { 192 NFSClient *client = bs->opaque; 193 NFSRPC task; 194 195 nfs_co_init_task(client, &task); 196 197 if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, 198 &task) != 0) { 199 return -ENOMEM; 200 } 201 202 while (!task.complete) { 203 nfs_set_events(client); 204 qemu_coroutine_yield(); 205 } 206 207 return task.ret; 208 } 209 210 /* TODO Convert to fine grained options */ 211 static QemuOptsList runtime_opts = { 212 .name = "nfs", 213 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), 214 .desc = { 215 { 216 .name = "filename", 217 .type = QEMU_OPT_STRING, 218 .help = "URL to the NFS file", 219 }, 220 { /* end of list */ } 221 }, 222 }; 223 224 static void nfs_client_close(NFSClient *client) 225 { 226 if (client->context) { 227 if (client->fh) { 228 nfs_close(client->context, client->fh); 229 } 230 qemu_aio_set_fd_handler(nfs_get_fd(client->context), NULL, NULL, NULL); 231 nfs_destroy_context(client->context); 232 } 233 memset(client, 0, sizeof(NFSClient)); 234 } 235 236 static void nfs_file_close(BlockDriverState *bs) 237 { 238 NFSClient *client = bs->opaque; 239 nfs_client_close(client); 240 } 241 242 static int64_t nfs_client_open(NFSClient *client, const char *filename, 243 int flags, Error **errp) 244 { 245 int ret = -EINVAL, i; 246 struct stat st; 247 URI *uri; 248 QueryParams *qp = NULL; 249 char *file = NULL, *strp = NULL; 250 251 uri = uri_parse(filename); 252 if (!uri) { 253 error_setg(errp, "Invalid URL specified"); 254 goto fail; 255 } 256 strp = strrchr(uri->path, '/'); 257 if (strp == NULL) { 258 error_setg(errp, "Invalid URL specified"); 259 goto fail; 260 } 261 file = g_strdup(strp); 262 *strp = 0; 263 264 client->context = nfs_init_context(); 265 if (client->context == NULL) { 266 error_setg(errp, "Failed to init NFS context"); 267 goto fail; 268 } 269 270 qp = query_params_parse(uri->query); 271 for (i = 0; i < qp->n; i++) { 272 if (!qp->p[i].value) { 273 error_setg(errp, "Value for NFS parameter expected: %s", 274 qp->p[i].name); 275 goto fail; 276 } 277 if (!strncmp(qp->p[i].name, "uid", 3)) { 278 nfs_set_uid(client->context, atoi(qp->p[i].value)); 279 } else if (!strncmp(qp->p[i].name, "gid", 3)) { 280 nfs_set_gid(client->context, atoi(qp->p[i].value)); 281 } else if (!strncmp(qp->p[i].name, "tcp-syncnt", 10)) { 282 nfs_set_tcp_syncnt(client->context, atoi(qp->p[i].value)); 283 } else { 284 error_setg(errp, "Unknown NFS parameter name: %s", 285 qp->p[i].name); 286 goto fail; 287 } 288 } 289 290 ret = nfs_mount(client->context, uri->server, uri->path); 291 if (ret < 0) { 292 error_setg(errp, "Failed to mount nfs share: %s", 293 nfs_get_error(client->context)); 294 goto fail; 295 } 296 297 if (flags & O_CREAT) { 298 ret = nfs_creat(client->context, file, 0600, &client->fh); 299 if (ret < 0) { 300 error_setg(errp, "Failed to create file: %s", 301 nfs_get_error(client->context)); 302 goto fail; 303 } 304 } else { 305 ret = nfs_open(client->context, file, flags, &client->fh); 306 if (ret < 0) { 307 error_setg(errp, "Failed to open file : %s", 308 nfs_get_error(client->context)); 309 goto fail; 310 } 311 } 312 313 ret = nfs_fstat(client->context, client->fh, &st); 314 if (ret < 0) { 315 error_setg(errp, "Failed to fstat file: %s", 316 nfs_get_error(client->context)); 317 goto fail; 318 } 319 320 ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); 321 client->has_zero_init = S_ISREG(st.st_mode); 322 goto out; 323 fail: 324 nfs_client_close(client); 325 out: 326 if (qp) { 327 query_params_free(qp); 328 } 329 uri_free(uri); 330 g_free(file); 331 return ret; 332 } 333 334 static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, 335 Error **errp) { 336 NFSClient *client = bs->opaque; 337 int64_t ret; 338 QemuOpts *opts; 339 Error *local_err = NULL; 340 341 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); 342 qemu_opts_absorb_qdict(opts, options, &local_err); 343 if (error_is_set(&local_err)) { 344 error_propagate(errp, local_err); 345 return -EINVAL; 346 } 347 ret = nfs_client_open(client, qemu_opt_get(opts, "filename"), 348 (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, 349 errp); 350 if (ret < 0) { 351 return ret; 352 } 353 bs->total_sectors = ret; 354 return 0; 355 } 356 357 static int nfs_file_create(const char *url, QEMUOptionParameter *options, 358 Error **errp) 359 { 360 int ret = 0; 361 int64_t total_size = 0; 362 NFSClient *client = g_malloc0(sizeof(NFSClient)); 363 364 /* Read out options */ 365 while (options && options->name) { 366 if (!strcmp(options->name, "size")) { 367 total_size = options->value.n; 368 } 369 options++; 370 } 371 372 ret = nfs_client_open(client, url, O_CREAT, errp); 373 if (ret < 0) { 374 goto out; 375 } 376 ret = nfs_ftruncate(client->context, client->fh, total_size); 377 nfs_client_close(client); 378 out: 379 g_free(client); 380 return ret; 381 } 382 383 static int nfs_has_zero_init(BlockDriverState *bs) 384 { 385 NFSClient *client = bs->opaque; 386 return client->has_zero_init; 387 } 388 389 static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) 390 { 391 NFSClient *client = bs->opaque; 392 NFSRPC task = {0}; 393 struct stat st; 394 395 task.st = &st; 396 if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb, 397 &task) != 0) { 398 return -ENOMEM; 399 } 400 401 while (!task.complete) { 402 nfs_set_events(client); 403 qemu_aio_wait(); 404 } 405 406 return (task.ret < 0 ? task.ret : st.st_blocks * st.st_blksize); 407 } 408 409 static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) 410 { 411 NFSClient *client = bs->opaque; 412 return nfs_ftruncate(client->context, client->fh, offset); 413 } 414 415 static BlockDriver bdrv_nfs = { 416 .format_name = "nfs", 417 .protocol_name = "nfs", 418 419 .instance_size = sizeof(NFSClient), 420 .bdrv_needs_filename = true, 421 .bdrv_has_zero_init = nfs_has_zero_init, 422 .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, 423 .bdrv_truncate = nfs_file_truncate, 424 425 .bdrv_file_open = nfs_file_open, 426 .bdrv_close = nfs_file_close, 427 .bdrv_create = nfs_file_create, 428 429 .bdrv_co_readv = nfs_co_readv, 430 .bdrv_co_writev = nfs_co_writev, 431 .bdrv_co_flush_to_disk = nfs_co_flush, 432 }; 433 434 static void nfs_block_init(void) 435 { 436 bdrv_register(&bdrv_nfs); 437 } 438 439 block_init(nfs_block_init); 440