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 "qemu/osdep.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 "qapi/error.h" 32 #include "block/block_int.h" 33 #include "trace.h" 34 #include "qemu/iov.h" 35 #include "qemu/uri.h" 36 #include "qemu/cutils.h" 37 #include "sysemu/sysemu.h" 38 #include <nfsc/libnfs.h> 39 40 #define QEMU_NFS_MAX_READAHEAD_SIZE 1048576 41 #define QEMU_NFS_MAX_DEBUG_LEVEL 2 42 43 typedef struct NFSClient { 44 struct nfs_context *context; 45 struct nfsfh *fh; 46 int events; 47 bool has_zero_init; 48 AioContext *aio_context; 49 blkcnt_t st_blocks; 50 } NFSClient; 51 52 typedef struct NFSRPC { 53 int ret; 54 int complete; 55 QEMUIOVector *iov; 56 struct stat *st; 57 Coroutine *co; 58 QEMUBH *bh; 59 NFSClient *client; 60 } NFSRPC; 61 62 static void nfs_process_read(void *arg); 63 static void nfs_process_write(void *arg); 64 65 static void nfs_set_events(NFSClient *client) 66 { 67 int ev = nfs_which_events(client->context); 68 if (ev != client->events) { 69 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), 70 false, 71 (ev & POLLIN) ? nfs_process_read : NULL, 72 (ev & POLLOUT) ? nfs_process_write : NULL, client); 73 74 } 75 client->events = ev; 76 } 77 78 static void nfs_process_read(void *arg) 79 { 80 NFSClient *client = arg; 81 nfs_service(client->context, POLLIN); 82 nfs_set_events(client); 83 } 84 85 static void nfs_process_write(void *arg) 86 { 87 NFSClient *client = arg; 88 nfs_service(client->context, POLLOUT); 89 nfs_set_events(client); 90 } 91 92 static void nfs_co_init_task(NFSClient *client, NFSRPC *task) 93 { 94 *task = (NFSRPC) { 95 .co = qemu_coroutine_self(), 96 .client = client, 97 }; 98 } 99 100 static void nfs_co_generic_bh_cb(void *opaque) 101 { 102 NFSRPC *task = opaque; 103 task->complete = 1; 104 qemu_bh_delete(task->bh); 105 qemu_coroutine_enter(task->co, NULL); 106 } 107 108 static void 109 nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, 110 void *private_data) 111 { 112 NFSRPC *task = private_data; 113 task->ret = ret; 114 if (task->ret > 0 && task->iov) { 115 if (task->ret <= task->iov->size) { 116 qemu_iovec_from_buf(task->iov, 0, data, task->ret); 117 } else { 118 task->ret = -EIO; 119 } 120 } 121 if (task->ret == 0 && task->st) { 122 memcpy(task->st, data, sizeof(struct stat)); 123 } 124 if (task->ret < 0) { 125 error_report("NFS Error: %s", nfs_get_error(nfs)); 126 } 127 if (task->co) { 128 task->bh = aio_bh_new(task->client->aio_context, 129 nfs_co_generic_bh_cb, task); 130 qemu_bh_schedule(task->bh); 131 } else { 132 task->complete = 1; 133 } 134 } 135 136 static int coroutine_fn nfs_co_readv(BlockDriverState *bs, 137 int64_t sector_num, int nb_sectors, 138 QEMUIOVector *iov) 139 { 140 NFSClient *client = bs->opaque; 141 NFSRPC task; 142 143 nfs_co_init_task(client, &task); 144 task.iov = iov; 145 146 if (nfs_pread_async(client->context, client->fh, 147 sector_num * BDRV_SECTOR_SIZE, 148 nb_sectors * BDRV_SECTOR_SIZE, 149 nfs_co_generic_cb, &task) != 0) { 150 return -ENOMEM; 151 } 152 153 while (!task.complete) { 154 nfs_set_events(client); 155 qemu_coroutine_yield(); 156 } 157 158 if (task.ret < 0) { 159 return task.ret; 160 } 161 162 /* zero pad short reads */ 163 if (task.ret < iov->size) { 164 qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret); 165 } 166 167 return 0; 168 } 169 170 static int coroutine_fn nfs_co_writev(BlockDriverState *bs, 171 int64_t sector_num, int nb_sectors, 172 QEMUIOVector *iov) 173 { 174 NFSClient *client = bs->opaque; 175 NFSRPC task; 176 char *buf = NULL; 177 178 nfs_co_init_task(client, &task); 179 180 buf = g_try_malloc(nb_sectors * BDRV_SECTOR_SIZE); 181 if (nb_sectors && buf == NULL) { 182 return -ENOMEM; 183 } 184 185 qemu_iovec_to_buf(iov, 0, buf, nb_sectors * BDRV_SECTOR_SIZE); 186 187 if (nfs_pwrite_async(client->context, client->fh, 188 sector_num * BDRV_SECTOR_SIZE, 189 nb_sectors * BDRV_SECTOR_SIZE, 190 buf, nfs_co_generic_cb, &task) != 0) { 191 g_free(buf); 192 return -ENOMEM; 193 } 194 195 while (!task.complete) { 196 nfs_set_events(client); 197 qemu_coroutine_yield(); 198 } 199 200 g_free(buf); 201 202 if (task.ret != nb_sectors * BDRV_SECTOR_SIZE) { 203 return task.ret < 0 ? task.ret : -EIO; 204 } 205 206 return 0; 207 } 208 209 static int coroutine_fn nfs_co_flush(BlockDriverState *bs) 210 { 211 NFSClient *client = bs->opaque; 212 NFSRPC task; 213 214 nfs_co_init_task(client, &task); 215 216 if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, 217 &task) != 0) { 218 return -ENOMEM; 219 } 220 221 while (!task.complete) { 222 nfs_set_events(client); 223 qemu_coroutine_yield(); 224 } 225 226 return task.ret; 227 } 228 229 /* TODO Convert to fine grained options */ 230 static QemuOptsList runtime_opts = { 231 .name = "nfs", 232 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), 233 .desc = { 234 { 235 .name = "filename", 236 .type = QEMU_OPT_STRING, 237 .help = "URL to the NFS file", 238 }, 239 { /* end of list */ } 240 }, 241 }; 242 243 static void nfs_detach_aio_context(BlockDriverState *bs) 244 { 245 NFSClient *client = bs->opaque; 246 247 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), 248 false, NULL, NULL, NULL); 249 client->events = 0; 250 } 251 252 static void nfs_attach_aio_context(BlockDriverState *bs, 253 AioContext *new_context) 254 { 255 NFSClient *client = bs->opaque; 256 257 client->aio_context = new_context; 258 nfs_set_events(client); 259 } 260 261 static void nfs_client_close(NFSClient *client) 262 { 263 if (client->context) { 264 if (client->fh) { 265 nfs_close(client->context, client->fh); 266 } 267 aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), 268 false, NULL, NULL, NULL); 269 nfs_destroy_context(client->context); 270 } 271 memset(client, 0, sizeof(NFSClient)); 272 } 273 274 static void nfs_file_close(BlockDriverState *bs) 275 { 276 NFSClient *client = bs->opaque; 277 nfs_client_close(client); 278 } 279 280 static int64_t nfs_client_open(NFSClient *client, const char *filename, 281 int flags, Error **errp) 282 { 283 int ret = -EINVAL, i; 284 struct stat st; 285 URI *uri; 286 QueryParams *qp = NULL; 287 char *file = NULL, *strp = NULL; 288 289 uri = uri_parse(filename); 290 if (!uri) { 291 error_setg(errp, "Invalid URL specified"); 292 goto fail; 293 } 294 if (!uri->server) { 295 error_setg(errp, "Invalid URL specified"); 296 goto fail; 297 } 298 strp = strrchr(uri->path, '/'); 299 if (strp == NULL) { 300 error_setg(errp, "Invalid URL specified"); 301 goto fail; 302 } 303 file = g_strdup(strp); 304 *strp = 0; 305 306 client->context = nfs_init_context(); 307 if (client->context == NULL) { 308 error_setg(errp, "Failed to init NFS context"); 309 goto fail; 310 } 311 312 qp = query_params_parse(uri->query); 313 for (i = 0; i < qp->n; i++) { 314 unsigned long long val; 315 if (!qp->p[i].value) { 316 error_setg(errp, "Value for NFS parameter expected: %s", 317 qp->p[i].name); 318 goto fail; 319 } 320 if (parse_uint_full(qp->p[i].value, &val, 0)) { 321 error_setg(errp, "Illegal value for NFS parameter: %s", 322 qp->p[i].name); 323 goto fail; 324 } 325 if (!strcmp(qp->p[i].name, "uid")) { 326 nfs_set_uid(client->context, val); 327 } else if (!strcmp(qp->p[i].name, "gid")) { 328 nfs_set_gid(client->context, val); 329 } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) { 330 nfs_set_tcp_syncnt(client->context, val); 331 #ifdef LIBNFS_FEATURE_READAHEAD 332 } else if (!strcmp(qp->p[i].name, "readahead")) { 333 if (val > QEMU_NFS_MAX_READAHEAD_SIZE) { 334 error_report("NFS Warning: Truncating NFS readahead" 335 " size to %d", QEMU_NFS_MAX_READAHEAD_SIZE); 336 val = QEMU_NFS_MAX_READAHEAD_SIZE; 337 } 338 nfs_set_readahead(client->context, val); 339 #endif 340 #ifdef LIBNFS_FEATURE_DEBUG 341 } else if (!strcmp(qp->p[i].name, "debug")) { 342 /* limit the maximum debug level to avoid potential flooding 343 * of our log files. */ 344 if (val > QEMU_NFS_MAX_DEBUG_LEVEL) { 345 error_report("NFS Warning: Limiting NFS debug level" 346 " to %d", QEMU_NFS_MAX_DEBUG_LEVEL); 347 val = QEMU_NFS_MAX_DEBUG_LEVEL; 348 } 349 nfs_set_debug(client->context, val); 350 #endif 351 } else { 352 error_setg(errp, "Unknown NFS parameter name: %s", 353 qp->p[i].name); 354 goto fail; 355 } 356 } 357 358 ret = nfs_mount(client->context, uri->server, uri->path); 359 if (ret < 0) { 360 error_setg(errp, "Failed to mount nfs share: %s", 361 nfs_get_error(client->context)); 362 goto fail; 363 } 364 365 if (flags & O_CREAT) { 366 ret = nfs_creat(client->context, file, 0600, &client->fh); 367 if (ret < 0) { 368 error_setg(errp, "Failed to create file: %s", 369 nfs_get_error(client->context)); 370 goto fail; 371 } 372 } else { 373 ret = nfs_open(client->context, file, flags, &client->fh); 374 if (ret < 0) { 375 error_setg(errp, "Failed to open file : %s", 376 nfs_get_error(client->context)); 377 goto fail; 378 } 379 } 380 381 ret = nfs_fstat(client->context, client->fh, &st); 382 if (ret < 0) { 383 error_setg(errp, "Failed to fstat file: %s", 384 nfs_get_error(client->context)); 385 goto fail; 386 } 387 388 ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); 389 client->st_blocks = st.st_blocks; 390 client->has_zero_init = S_ISREG(st.st_mode); 391 goto out; 392 fail: 393 nfs_client_close(client); 394 out: 395 if (qp) { 396 query_params_free(qp); 397 } 398 uri_free(uri); 399 g_free(file); 400 return ret; 401 } 402 403 static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, 404 Error **errp) { 405 NFSClient *client = bs->opaque; 406 int64_t ret; 407 QemuOpts *opts; 408 Error *local_err = NULL; 409 410 client->aio_context = bdrv_get_aio_context(bs); 411 412 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); 413 qemu_opts_absorb_qdict(opts, options, &local_err); 414 if (local_err) { 415 error_propagate(errp, local_err); 416 ret = -EINVAL; 417 goto out; 418 } 419 ret = nfs_client_open(client, qemu_opt_get(opts, "filename"), 420 (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, 421 errp); 422 if (ret < 0) { 423 goto out; 424 } 425 bs->total_sectors = ret; 426 ret = 0; 427 out: 428 qemu_opts_del(opts); 429 return ret; 430 } 431 432 static QemuOptsList nfs_create_opts = { 433 .name = "nfs-create-opts", 434 .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head), 435 .desc = { 436 { 437 .name = BLOCK_OPT_SIZE, 438 .type = QEMU_OPT_SIZE, 439 .help = "Virtual disk size" 440 }, 441 { /* end of list */ } 442 } 443 }; 444 445 static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp) 446 { 447 int ret = 0; 448 int64_t total_size = 0; 449 NFSClient *client = g_new0(NFSClient, 1); 450 451 client->aio_context = qemu_get_aio_context(); 452 453 /* Read out options */ 454 total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), 455 BDRV_SECTOR_SIZE); 456 457 ret = nfs_client_open(client, url, O_CREAT, errp); 458 if (ret < 0) { 459 goto out; 460 } 461 ret = nfs_ftruncate(client->context, client->fh, total_size); 462 nfs_client_close(client); 463 out: 464 g_free(client); 465 return ret; 466 } 467 468 static int nfs_has_zero_init(BlockDriverState *bs) 469 { 470 NFSClient *client = bs->opaque; 471 return client->has_zero_init; 472 } 473 474 static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) 475 { 476 NFSClient *client = bs->opaque; 477 NFSRPC task = {0}; 478 struct stat st; 479 480 if (bdrv_is_read_only(bs) && 481 !(bs->open_flags & BDRV_O_NOCACHE)) { 482 return client->st_blocks * 512; 483 } 484 485 task.st = &st; 486 if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb, 487 &task) != 0) { 488 return -ENOMEM; 489 } 490 491 while (!task.complete) { 492 nfs_set_events(client); 493 aio_poll(client->aio_context, true); 494 } 495 496 return (task.ret < 0 ? task.ret : st.st_blocks * 512); 497 } 498 499 static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) 500 { 501 NFSClient *client = bs->opaque; 502 return nfs_ftruncate(client->context, client->fh, offset); 503 } 504 505 /* Note that this will not re-establish a connection with the NFS server 506 * - it is effectively a NOP. */ 507 static int nfs_reopen_prepare(BDRVReopenState *state, 508 BlockReopenQueue *queue, Error **errp) 509 { 510 NFSClient *client = state->bs->opaque; 511 struct stat st; 512 int ret = 0; 513 514 if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) { 515 error_setg(errp, "Cannot open a read-only mount as read-write"); 516 return -EACCES; 517 } 518 519 /* Update cache for read-only reopens */ 520 if (!(state->flags & BDRV_O_RDWR)) { 521 ret = nfs_fstat(client->context, client->fh, &st); 522 if (ret < 0) { 523 error_setg(errp, "Failed to fstat file: %s", 524 nfs_get_error(client->context)); 525 return ret; 526 } 527 client->st_blocks = st.st_blocks; 528 } 529 530 return 0; 531 } 532 533 static BlockDriver bdrv_nfs = { 534 .format_name = "nfs", 535 .protocol_name = "nfs", 536 537 .instance_size = sizeof(NFSClient), 538 .bdrv_needs_filename = true, 539 .create_opts = &nfs_create_opts, 540 541 .bdrv_has_zero_init = nfs_has_zero_init, 542 .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, 543 .bdrv_truncate = nfs_file_truncate, 544 545 .bdrv_file_open = nfs_file_open, 546 .bdrv_close = nfs_file_close, 547 .bdrv_create = nfs_file_create, 548 .bdrv_reopen_prepare = nfs_reopen_prepare, 549 550 .bdrv_co_readv = nfs_co_readv, 551 .bdrv_co_writev = nfs_co_writev, 552 .bdrv_co_flush_to_disk = nfs_co_flush, 553 554 .bdrv_detach_aio_context = nfs_detach_aio_context, 555 .bdrv_attach_aio_context = nfs_attach_aio_context, 556 }; 557 558 static void nfs_block_init(void) 559 { 560 bdrv_register(&bdrv_nfs); 561 } 562 563 block_init(nfs_block_init); 564