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