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