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