1/** 2 * \file 3 * \brief NFS client 4 */ 5 6/* 7 * Copyright (c) 2008, 2009, 2011, ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <assert.h> 16#include <barrelfish/barrelfish.h> 17#include <nfs/nfs.h> 18#include "nfs_debug.h" 19#include "rpc.h" 20#include "portmap_rpc.h" 21 22 23static errval_t portmap_lookup(struct nfs_client *client, u_int prog, u_int vers); 24 25/// What state are we at in initialising this mount? 26enum nfs_mount_state { 27 NFS_INIT_GETPORT_MOUNT, ///< 1. consult portmap for mountd port 28 NFS_INIT_GETPORT_NFS, ///< 2. consult portmap for nfsd port 29 NFS_INIT_MOUNT, ///< 3. call mountd to perform mount 30 NFS_INIT_COMPLETE ///< 4. complete 31}; 32 33/// Per-instance NFS client data 34struct nfs_client { 35 struct rpc_client rpc_client; ///< RPC client state (XXX: must be first) 36 enum nfs_mount_state mount_state; ///< State in mounting 37 nfs_mount_callback_t mount_callback;///< Callback function when mount completes 38 void *mount_cbarg; ///< Arg to #mount_callback 39 const char *mount_path; ///< Path on server to be mounted 40 uint16_t mount_port, nfs_port; ///< UDP ports for mountd and nfsd 41}; 42 43 44/// Called for portmap and mount RPC replies during the mount process 45/// Implements state machine for mount process 46static void mount_reply_handler(struct rpc_client *rpc_client, void *arg1, 47 void *arg2, uint32_t replystat, 48 uint32_t acceptstat, XDR *xdr) 49{ 50 struct nfs_client *client = (void *)rpc_client; 51 uint32_t port; 52 mountstat3 mountstat; 53 struct nfs_fh3 fh = { .data_len = 0, .data_val = NULL }; 54 errval_t r; 55 bool rb; 56 57 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 58 printf("RPC failed while mounting in state %d:" 59 "replystat = %"PRIu32", acceptstat = %"PRIu32"\n", 60 client->mount_state, replystat, acceptstat); 61 goto error; 62 } 63 64 switch (client->mount_state) { 65 case NFS_INIT_GETPORT_MOUNT: 66 rb = xdr_uint32_t(xdr, &port); 67 assert(rb); 68 if (!rb) { 69 goto error; 70 } 71 client->mount_port = port; 72 73 // lookup NFS port 74 r = portmap_lookup(client, NFS_PROGRAM, NFS_V3); 75 assert(r == SYS_ERR_OK); 76 if (r != SYS_ERR_OK) { 77 goto error; 78 } 79 client->mount_state = NFS_INIT_GETPORT_NFS; 80 break; 81 82 case NFS_INIT_GETPORT_NFS: 83 rb = xdr_uint32_t(xdr, &port); 84 assert(rb); 85 client->nfs_port = port; 86 87 // do mount call 88 r = rpc_call(&client->rpc_client, client->mount_port, MOUNT_PROGRAM, 89 MOUNT_V3, MOUNTPROC3_MNT, (xdrproc_t) xdr_dirpath, 90 (void *)&client->mount_path, 91 RNDUP(strlen(client->mount_path)) + BYTES_PER_XDR_UNIT, 92 mount_reply_handler, NULL, NULL); 93 assert(r == SYS_ERR_OK); 94 if (r != SYS_ERR_OK) { 95 goto error; 96 } 97 client->mount_state = NFS_INIT_MOUNT; 98 break; 99 100 case NFS_INIT_MOUNT: 101 rb = xdr_mountstat3(xdr, &mountstat); 102 assert(rb); 103 if (!rb) { 104 goto error; 105 } 106 107 client->mount_state = NFS_INIT_COMPLETE; 108 if (mountstat == MNT3_OK) { 109 rb = xdr_nfs_fh3(xdr, &fh); 110 assert(rb); 111 if (!rb) { 112 goto error; 113 } 114 } 115 116 // mount complete 117 client->mount_callback(client->mount_cbarg, client, mountstat, fh); 118 break; 119 120 default: 121 assert(!"invalid state"); 122 } 123 124 return; 125 126error: 127 client->mount_callback(client->mount_cbarg, NULL, -1, fh); 128 nfs_destroy(client); 129} 130 131 132 133/// Initiates a portmap GETPORT call, calling mount_reply_handler with the reply 134static errval_t portmap_lookup(struct nfs_client *client, u_int prog, u_int vers) 135{ 136 struct mapping mount_map = { 137 .prog = prog, 138 .vers = vers, 139 .prot = IPPROTO_UDP, 140 .port = 0 /* ignored */ 141 }; 142 143 NFSDEBUGPRINT("portmap_lookup: portmap_lookup calling rpc_call\n"); 144 errval_t err = rpc_call(&client->rpc_client, PMAP_PORT, PMAP_PROG, PMAP_VERS, 145 PMAPPROC_GETPORT, (xdrproc_t) xdr_mapping, &mount_map, 146 sizeof(mount_map), mount_reply_handler, NULL, NULL); 147 NFSDEBUGPRINT("portmap_lookup: portmap_lookup done with rpc_call returned %zu \n", 148 err); 149 return err; 150} 151 152/** \brief Initiate an NFS mount operation 153 * 154 * \param server IP address of NFSv3 server 155 * \param path Path on server to mount 156 * \param callback Callback function to call when mount completes or fails 157 * \param cbarg Opaque argument word passed to callback function 158 * 159 * \returns nfs_client instance pointer on success, or NULL on error. If this 160 * call succeeds, the returned client instance must be freed by a later call 161 * to nfs_destroy(). 162 */ 163struct nfs_client *nfs_mount(struct in_addr server, const char *path, 164 nfs_mount_callback_t callback, void *cbarg) 165{ 166 struct nfs_client *client; 167 168 client = malloc(sizeof(struct nfs_client)); 169 if (client == NULL) { 170 return NULL; 171 } 172 NFSDEBUGPRINT("nfs_mount: calling rpc_init\n"); 173 errval_t r = rpc_init(&client->rpc_client, server); 174 if (r != SYS_ERR_OK) { 175 free(client); 176 return NULL; 177 } 178 179 NFSDEBUGPRINT("nfs_mount: rpc_init done\n"); 180 client->mount_path = path; 181 client->mount_state = NFS_INIT_GETPORT_MOUNT; 182 client->mount_callback = callback; 183 client->mount_cbarg = cbarg; 184 185 NFSDEBUGPRINT("nfs_mount: calling portmap_lookup\n"); 186 r = portmap_lookup(client, MOUNT_PROGRAM, MOUNT_V3); 187 NFSDEBUGPRINT("nfs_mount: portmap_lookup done \n"); 188 if (r != SYS_ERR_OK) { 189 nfs_destroy(client); 190 return NULL; 191 } 192 return client; 193} 194 195 196void nfs_copyfh(struct nfs_fh3 *dest, struct nfs_fh3 src) 197{ 198 dest->data_len = src.data_len; 199 dest->data_val = malloc(src.data_len); 200 assert(dest->data_val != NULL); 201 memcpy(dest->data_val, src.data_val, src.data_len); 202} 203 204void nfs_freefh(struct nfs_fh3 fh) 205{ 206 free(fh.data_val); 207} 208 209/// RPC callback for getattr replies 210static void getattr_reply_handler(struct rpc_client *rpc_client, void *arg1, 211 void *arg2, uint32_t replystat, 212 uint32_t acceptstat, XDR *xdr) 213{ 214 struct nfs_client *client = (void *)rpc_client; 215 nfs_getattr_callback_t callback = (nfs_getattr_callback_t)arg1; 216 GETATTR3res result; 217 bool rb; 218 219 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 220 printf("Getattr failed\n"); 221 callback(arg2, client, NULL); 222 } else { 223 memset(&result, 0, sizeof(result)); 224 rb = xdr_GETATTR3res(xdr, &result); 225 assert(rb); 226 if (rb) { 227 callback(arg2, client, &result); 228 } else { 229 /* free partial results if the xdr fails */ 230 xdr_GETATTR3res(&xdr_free, &result); 231 callback(arg2, client, NULL); 232 } 233 } 234} 235 236/** \brief Initiate an NFS getattr operation 237 * 238 * \param client NFS client pointer, which has completed the mount process 239 * \param fh Filehandle for directory to stat 240 * \param callback Callback function to call when operation returns 241 * \param cbarg Opaque argument word passed to callback function 242 * 243 * \returns SYS_ERR_OK on success, error code on failure 244 */ 245errval_t nfs_getattr(struct nfs_client *client, struct nfs_fh3 fh, 246 nfs_getattr_callback_t callback, void *cbarg) 247{ 248 assert(client->mount_state == NFS_INIT_COMPLETE); 249 250 struct GETATTR3args args = { 251 .object = fh, 252 }; 253 254 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 255 NFS_V3, NFSPROC3_GETATTR, (xdrproc_t) xdr_GETATTR3args, 256 &args, sizeof(args) + RNDUP(fh.data_len), 257 getattr_reply_handler, callback, cbarg); 258} 259 260 261/// RPC callback for getattr replies 262static void setattr_reply_handler(struct rpc_client *rpc_client, void *arg1, 263 void *arg2, uint32_t replystat, 264 uint32_t acceptstat, XDR *xdr) 265{ 266 struct nfs_client *client = (void *)rpc_client; 267 nfs_setattr_callback_t callback = (nfs_setattr_callback_t)arg1; 268 SETATTR3res result; 269 bool rb; 270 271 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 272 printf("Setattr failed\n"); 273 callback(arg2, client, NULL); 274 } else { 275 memset(&result, 0, sizeof(result)); 276 rb = xdr_SETATTR3res(xdr, &result); 277 assert(rb); 278 if (rb) { 279 callback(arg2, client, &result); 280 } else { 281 /* free partial results if the xdr fails */ 282 xdr_SETATTR3res(&xdr_free, &result); 283 callback(arg2, client, NULL); 284 } 285 } 286} 287 288/** \brief Initiate an NFS setattr operation 289 * 290 * \param client NFS client pointer, which has completed the mount process 291 * \param fh Filehandle for directory which attributes are to be modified 292 * \param new New attributes for directory 293 * \param guarded TODO 294 * \param ctime TODO 295 * \param callback Callback function to call when operation returns 296 * \param cbarg Opaque argument word passed to callback function 297 * 298 * \returns SYS_ERR_OK on success, error code on failure 299 */ 300errval_t nfs_setattr(struct nfs_client *client, struct nfs_fh3 fh, 301 sattr3 new_attributes, bool guarded, 302 nfs_setattr_callback_t callback, void *cbarg) 303{ 304 assert(client->mount_state == NFS_INIT_COMPLETE); 305// struct nfstime3 time_null; 306 307 struct SETATTR3args args = { 308 .object = fh, 309 .new_attributes = new_attributes, 310 .guard = { 311 .check = guarded ? TRUE : FALSE, 312 //.sattrguard3_u.obj_ctime = time_null, 313 }, 314 }; 315 316 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 317 NFS_V3, NFSPROC3_SETATTR, (xdrproc_t) xdr_SETATTR3args, 318 &args, sizeof(args) + RNDUP(fh.data_len), 319 setattr_reply_handler, callback, cbarg); 320} 321 322 323/// RPC callback for readdir replies 324static void readdir_reply_handler(struct rpc_client *rpc_client, void *arg1, 325 void *arg2, uint32_t replystat, 326 uint32_t acceptstat, XDR *xdr) 327{ 328 struct nfs_client *client = (void *)rpc_client; 329 nfs_readdir_callback_t callback = (nfs_readdir_callback_t)arg1; 330 READDIR3res result; 331 bool rb; 332 333 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 334 printf("Readdir failed\n"); 335 callback(arg2, client, NULL); 336 } else { 337 memset(&result, 0, sizeof(result)); 338 rb = xdr_READDIR3res(xdr, &result); 339 assert(rb); 340 if (rb) { 341 callback(arg2, client, &result); 342 } else { 343 /* free partial results if the xdr fails */ 344 xdr_READDIR3res(&xdr_free, &result); 345 callback(arg2, client, NULL); 346 } 347 } 348} 349 350/** \brief Initiate an NFS readdir operation 351 * 352 * \param client NFS client pointer, which has completed the mount process 353 * \param fh Filehandle for directory to read 354 * \param cookie Cookie from a previous call, or NFS_READDIR_COOKIE for a new call 355 * \param cookieverf Cookie verifier from a previous call, or NFS_READDIR_COOKIEVERF 356 * \param callback Callback function to call when operation returns 357 * \param cbarg Opaque argument word passed to callback function 358 * 359 * \returns SYS_ERR_OK on success, error code on failure 360 */ 361errval_t nfs_readdir(struct nfs_client *client, struct nfs_fh3 fh, 362 cookie3 cookie, cookieverf3 cookieverf, 363 nfs_readdir_callback_t callback, void *cbarg) 364{ 365 assert(client->mount_state == NFS_INIT_COMPLETE); 366 367 struct READDIR3args args = { 368 .dir = fh, 369 .cookie = cookie, 370 .cookieverf = { 0 }, 371 .count = (uint32_t)-1, 372 }; 373 374 if (cookieverf != NULL) { 375 memcpy(args.cookieverf, cookieverf, sizeof(cookieverf3)); 376 } 377 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 378 NFS_V3, NFSPROC3_READDIR, (xdrproc_t) xdr_READDIR3args, 379 &args, sizeof(args) + RNDUP(fh.data_len), 380 readdir_reply_handler, callback, cbarg); 381} 382 383 384/// RPC callback for readdirplus replies 385static void readdirplus_reply_handler(struct rpc_client *rpc_client, void *arg1, 386 void *arg2, uint32_t replystat, 387 uint32_t acceptstat, XDR *xdr) 388{ 389 struct nfs_client *client = (void *)rpc_client; 390 nfs_readdirplus_callback_t callback = (nfs_readdirplus_callback_t)arg1; 391 READDIRPLUS3res result; 392 bool rb; 393 394 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 395 printf("Readdirplus failed\n"); 396 callback(arg2, client, NULL); 397 } else { 398 memset(&result, 0, sizeof(result)); 399 rb = xdr_READDIRPLUS3res(xdr, &result); 400 assert(rb); 401 if (rb) { 402 callback(arg2, client, &result); 403 } else { 404 /* free partial results if the xdr fails */ 405 xdr_READDIRPLUS3res(&xdr_free, &result); 406 callback(arg2, client, NULL); 407 } 408 } 409} 410 411/** \brief Initiate an NFS readdirplus operation 412 * 413 * \param client NFS client pointer, which has completed the mount process 414 * \param fh Filehandle for directory to read 415 * \param cookie Cookie from a previous call, or NFS_READDIR_COOKIE for a new call 416 * \param cookieverf Cookie verifier from a previous call, or NFS_READDIR_COOKIEVERF 417 * \param callback Callback function to call when operation returns 418 * \param cbarg Opaque argument word passed to callback function 419 * 420 * \returns SYS_ERR_OK on success, error code on failure 421 */ 422errval_t nfs_readdirplus(struct nfs_client *client, struct nfs_fh3 fh, 423 cookie3 cookie, cookieverf3 cookieverf, 424 nfs_readdirplus_callback_t callback, void *cbarg) 425{ 426 assert(client->mount_state == NFS_INIT_COMPLETE); 427 428 struct READDIRPLUS3args args = { 429 .dir = fh, 430 .cookie = cookie, 431 .cookieverf = { 0 }, 432 .dircount = (uint32_t)-1, 433 .maxcount = (uint32_t)-1, 434 }; 435 436 if (cookieverf != NULL) { 437 memcpy(args.cookieverf, cookieverf, sizeof(cookieverf3)); 438 } 439 440 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 441 NFS_V3, NFSPROC3_READDIRPLUS, 442 (xdrproc_t) xdr_READDIRPLUS3args, &args, 443 sizeof(args) + RNDUP(fh.data_len), 444 readdirplus_reply_handler, callback, cbarg); 445} 446 447 448/// RPC callback for lookup replies 449static void lookup_reply_handler(struct rpc_client *rpc_client, void *arg1, 450 void *arg2, uint32_t replystat, 451 uint32_t acceptstat, XDR *xdr) 452{ 453 struct nfs_client *client = (void *)rpc_client; 454 nfs_lookup_callback_t callback = (nfs_lookup_callback_t)arg1; 455 LOOKUP3res result; 456 bool rb; 457 458 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 459 printf("Lookup failed\n"); 460 callback(arg2, client, NULL); 461 } else { 462 memset(&result, 0, sizeof(result)); 463 rb = xdr_LOOKUP3res(xdr, &result); 464 assert(rb); 465 if (rb) { 466 callback(arg2, client, &result); 467 } else { 468 /* free partial results if the xdr fails */ 469 xdr_LOOKUP3res(&xdr_free, &result); 470 callback(arg2, client, NULL); 471 } 472 } 473} 474 475/** \brief Initiate an NFS lookup operation 476 * 477 * \param client NFS client pointer, which has completed the mount process 478 * \param dirfh Filehandle for directory to lookup 479 * \param name Name to lookup 480 * \param callback Callback function to call when operation returns 481 * \param cbarg Opaque argument word passed to callback function 482 * 483 * \returns SYS_ERR_OK on success, error code on failure 484 */ 485errval_t nfs_lookup(struct nfs_client *client, struct nfs_fh3 dirfh, 486 const char *name, nfs_lookup_callback_t callback, void *cbarg) 487{ 488 assert(client->mount_state == NFS_INIT_COMPLETE); 489 490 struct LOOKUP3args args = { 491 .what = { 492 .dir = dirfh, 493 .name = (char *)name 494 } 495 }; 496 497 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 498 NFS_V3, NFSPROC3_LOOKUP, (xdrproc_t) xdr_LOOKUP3args, 499 &args, 2 * BYTES_PER_XDR_UNIT + RNDUP(dirfh.data_len) 500 + RNDUP(strlen(name)), 501 lookup_reply_handler, callback, cbarg); 502} 503 504 505/// RPC callback for access replies 506static void access_reply_handler(struct rpc_client *rpc_client, void *arg1, 507 void *arg2, uint32_t replystat, 508 uint32_t acceptstat, XDR *xdr) 509{ 510 struct nfs_client *client = (void *)rpc_client; 511 nfs_access_callback_t callback = (nfs_access_callback_t)arg1; 512 ACCESS3res result; 513 bool rb; 514 515 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 516 printf("Access failed\n"); 517 callback(arg2, client, NULL); 518 } else { 519 memset(&result, 0, sizeof(result)); 520 rb = xdr_ACCESS3res(xdr, &result); 521 assert(rb); 522 if (rb) { 523 callback(arg2, client, &result); 524 } else { 525 /* free partial results if the xdr fails */ 526 xdr_ACCESS3res(&xdr_free, &result); 527 callback(arg2, client, NULL); 528 } 529 } 530} 531 532/** \brief Initiate an NFS access operation 533 * 534 * \param client NFS client pointer, which has completed the mount process 535 * \param fh Filehandle for file/object to check access to 536 * \param access Rights to check for 537 * \param callback Callback function to call when operation returns 538 * \param cbarg Opaque argument word passed to callback function 539 * 540 * \returns SYS_ERR_OK on success, error code on failure 541 */ 542errval_t nfs_access(struct nfs_client *client, struct nfs_fh3 fh, uint32_t access, 543 nfs_access_callback_t callback, void *cbarg) 544{ 545 assert(client->mount_state == NFS_INIT_COMPLETE); 546 547 struct ACCESS3args args = { 548 .object = fh, 549 .access = access, 550 }; 551 552 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 553 NFS_V3, NFSPROC3_ACCESS, (xdrproc_t) xdr_ACCESS3args, 554 &args, sizeof(args) + RNDUP(fh.data_len), 555 access_reply_handler, callback, cbarg); 556} 557 558 559/// RPC callback for read replies 560static void read_reply_handler(struct rpc_client *rpc_client, void *arg1, 561 void *arg2, uint32_t replystat, 562 uint32_t acceptstat, XDR *xdr) 563{ 564 struct nfs_client *client = (void *)rpc_client; 565 nfs_read_callback_t callback = (nfs_read_callback_t)arg1; 566 READ3res result; 567 bool rb; 568 569 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 570 printf("Read failed\n"); 571 callback(arg2, client, NULL); 572 } else { 573 memset(&result, 0, sizeof(result)); 574 rb = xdr_READ3res(xdr, &result); 575 assert(rb); 576 if (rb) { 577 callback(arg2, client, &result); 578 } else { 579 /* free partial results if the xdr fails */ 580 xdr_READ3res(&xdr_free, &result); 581 callback(arg2, client, NULL); 582 } 583 } 584} 585 586/** \brief Initiate an NFS read operation 587 * 588 * \param client NFS client pointer, which has completed the mount process 589 * \param fh Filehandle for file to read 590 * \param offset Offset from start of file to read from 591 * \param count Maximum number of bytes to read 592 * \param callback Callback function to call when operation returns 593 * \param cbarg Opaque argument word passed to callback function 594 * 595 * \returns SYS_ERR_OK on success, error code on failure 596 */ 597errval_t nfs_read(struct nfs_client *client, struct nfs_fh3 fh, offset3 offset, 598 count3 count, nfs_read_callback_t callback, void *cbarg) 599{ 600 NFSDEBUGPRINT("nfs read called on offset %"PRIu32" and size %d\n", 601 (uint32_t)offset, count); 602 assert(client->mount_state == NFS_INIT_COMPLETE); 603 604 struct READ3args args = { 605 .file = fh, 606 .offset = offset, 607 .count = count 608 }; 609 610 errval_t errval = rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 611 NFS_V3, NFSPROC3_READ, (xdrproc_t) xdr_READ3args, 612 &args, sizeof(args) + RNDUP(fh.data_len), 613 read_reply_handler, callback, cbarg); 614 615 return errval; 616} 617 618 619/// RPC callback for write replies 620static void write_reply_handler(struct rpc_client *rpc_client, void *arg1, 621 void *arg2, uint32_t replystat, 622 uint32_t acceptstat, XDR *xdr) 623{ 624 struct nfs_client *client = (void *)rpc_client; 625 nfs_write_callback_t callback = (nfs_write_callback_t)arg1; 626 WRITE3res result; 627 bool rb; 628 629 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 630 printf("Write failed\n"); 631 callback(arg2, client, NULL); 632 } else { 633 memset(&result, 0, sizeof(result)); 634 rb = xdr_WRITE3res(xdr, &result); 635 assert(rb); 636 if (rb) { 637 callback(arg2, client, &result); 638 } else { 639 /* free partial results if the xdr fails */ 640 xdr_WRITE3res(&xdr_free, &result); 641 callback(arg2, client, NULL); 642 } 643 } 644} 645 646/** \brief Initiate an NFS write operation 647 * 648 * \param client NFS client pointer, which has completed the mount process 649 * \param fh Filehandle for file to write 650 * \param offset Offset from start of file to write from 651 * \param data Pointer to data to write 652 * \param count Number of bytes of data to write 653 * \param stable Specifies when the server may commit data to stable storage 654 * \param callback Callback function to call when operation returns 655 * \param cbarg Opaque argument word passed to callback function 656 * 657 * \returns SYS_ERR_OK on success, error code on failure 658 */ 659errval_t nfs_write(struct nfs_client *client, struct nfs_fh3 fh, offset3 offset, 660 const void *data, count3 count, stable_how stable, 661 nfs_write_callback_t callback, void *cbarg) 662{ 663 assert(client->mount_state == NFS_INIT_COMPLETE); 664 665 struct WRITE3args args = { 666 .file = fh, 667 .offset = offset, 668 .count = count, 669 .stable = stable, 670 .data = { 671 .data_len = count, 672 .data_val = (void *)data, /* XXX: discarding const */ 673 } 674 }; 675 676 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 677 NFS_V3, NFSPROC3_WRITE, (xdrproc_t) xdr_WRITE3args, 678 &args, sizeof(args) + RNDUP(fh.data_len) + RNDUP(count), 679 write_reply_handler, callback, cbarg); 680} 681 682 683/// RPC callback for create replies 684static void create_reply_handler(struct rpc_client *rpc_client, void *arg1, 685 void *arg2, uint32_t replystat, 686 uint32_t acceptstat, XDR *xdr) 687{ 688 struct nfs_client *client = (void *)rpc_client; 689 nfs_create_callback_t callback = (nfs_create_callback_t)arg1; 690 CREATE3res result; 691 bool rb; 692 693 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 694 printf("Create failed\n"); 695 callback(arg2, client, NULL); 696 } else { 697 memset(&result, 0, sizeof(result)); 698 rb = xdr_CREATE3res(xdr, &result); 699 assert(rb); 700 if (rb) { 701 callback(arg2, client, &result); 702 } else { 703 /* free partial results if the xdr fails */ 704 xdr_CREATE3res(&xdr_free, &result); 705 callback(arg2, client, NULL); 706 } 707 } 708} 709 710/** \brief Initiate an NFS create operation (unchecked or guarded) 711 * 712 * \param client NFS client pointer, which has completed the mount process 713 * \param dir Filehandle for directory in which to create file 714 * \param name Name of file to create 715 * \param guarded True iff the operation should fail if the file already exists 716 * \param attributes Initial attributes for the file 717 * \param callback Callback function to call when operation returns 718 * \param cbarg Opaque argument word passed to callback function 719 * 720 * \todo Exclusive create will be implemented by a different call. 721 * 722 * \returns SYS_ERR_OK on success, error code on failure 723 */ 724errval_t nfs_create(struct nfs_client *client, struct nfs_fh3 dir, 725 const char *name, bool guarded, sattr3 attributes, 726 nfs_create_callback_t callback, void *cbarg) 727{ 728 assert(client->mount_state == NFS_INIT_COMPLETE); 729 730 struct CREATE3args args = { 731 .where = { 732 .dir = dir, 733 .name = (char *)name, 734 }, 735 .how = { 736 .mode = guarded ? GUARDED : UNCHECKED, 737 .createhow3_u.obj_attributes = attributes, 738 }, 739 }; 740 741 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 742 NFS_V3, NFSPROC3_CREATE, (xdrproc_t) xdr_CREATE3args, &args, 743 sizeof(args) + RNDUP(dir.data_len) + RNDUP(strlen(name)), 744 create_reply_handler, callback, cbarg); 745} 746 747 748/// RPC callback for mkdir replies 749static void mkdir_reply_handler(struct rpc_client *rpc_client, void *arg1, 750 void *arg2, uint32_t replystat, 751 uint32_t acceptstat, XDR *xdr) 752{ 753 struct nfs_client *client = (void *)rpc_client; 754 nfs_mkdir_callback_t callback = (nfs_mkdir_callback_t)arg1; 755 MKDIR3res result; 756 bool rb; 757 758 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 759 printf("Mkdir failed\n"); 760 callback(arg2, client, NULL); 761 } else { 762 memset(&result, 0, sizeof(result)); 763 rb = xdr_MKDIR3res(xdr, &result); 764 assert(rb); 765 if (rb) { 766 callback(arg2, client, &result); 767 } else { 768 /* free partial results if the xdr fails */ 769 xdr_MKDIR3res(&xdr_free, &result); 770 callback(arg2, client, NULL); 771 } 772 } 773} 774 775/** \brief Initiate an NFS mkdir operation 776 * 777 * \param client NFS client pointer, which has completed the mount process 778 * \param dir Filehandle for directory in which to create directory 779 * \param name Name of directory to create 780 * \param attributes Initial attributes for the directory 781 * \param callback Callback function to call when operation returns 782 * \param cbarg Opaque argument word passed to callback function 783 * 784 * \returns SYS_ERR_OK on success, error code on failure 785 */ 786errval_t nfs_mkdir(struct nfs_client *client, struct nfs_fh3 dir, const char *name, 787 sattr3 attributes, nfs_mkdir_callback_t callback, void *cbarg) 788{ 789 assert(client->mount_state == NFS_INIT_COMPLETE); 790 791 struct MKDIR3args args = { 792 .where = { 793 .dir = dir, 794 .name = (char *)name, 795 }, 796 .attributes = attributes, 797 }; 798 799 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 800 NFS_V3, NFSPROC3_MKDIR, (xdrproc_t) xdr_MKDIR3args, &args, 801 sizeof(args) + RNDUP(dir.data_len) + RNDUP(strlen(name)), 802 mkdir_reply_handler, callback, cbarg); 803} 804 805 806/// RPC callback for remove replies 807static void remove_reply_handler(struct rpc_client *rpc_client, void *arg1, 808 void *arg2, uint32_t replystat, 809 uint32_t acceptstat, XDR *xdr) 810{ 811 struct nfs_client *client = (void *)rpc_client; 812 nfs_remove_callback_t callback = (nfs_remove_callback_t)arg1; 813 REMOVE3res result; 814 bool rb; 815 816 if (replystat != RPC_MSG_ACCEPTED || acceptstat != RPC_SUCCESS) { 817 printf("Remove failed\n"); 818 callback(arg2, client, NULL); 819 } else { 820 memset(&result, 0, sizeof(result)); 821 rb = xdr_REMOVE3res(xdr, &result); 822 assert(rb); 823 if (rb) { 824 callback(arg2, client, &result); 825 } else { 826 /* free partial results if the xdr fails */ 827 xdr_REMOVE3res(&xdr_free, &result); 828 callback(arg2, client, NULL); 829 } 830 } 831} 832 833/** \brief Initiate an NFS remove operation 834 * 835 * \param client NFS client pointer, which has completed the mount process 836 * \param dir Filehandle for directory in which to remove file 837 * \param name Name of file to remove 838 * \param callback Callback function to call when operation returns 839 * \param cbarg Opaque argument word passed to callback function 840 * 841 * \returns SYS_ERR_OK on success, error code on failure 842 */ 843errval_t nfs_remove(struct nfs_client *client, struct nfs_fh3 dir, 844 const char *name, nfs_remove_callback_t callback, 845 void *cbarg) 846{ 847 assert(client->mount_state == NFS_INIT_COMPLETE); 848 849 struct REMOVE3args args = { 850 .object = { 851 .dir = dir, 852 .name = (char *)name, 853 } 854 }; 855 856 return rpc_call(&client->rpc_client, client->nfs_port, NFS_PROGRAM, 857 NFS_V3, NFSPROC3_REMOVE, (xdrproc_t) xdr_REMOVE3args, &args, 858 sizeof(args) + RNDUP(dir.data_len) + RNDUP(strlen(name)), 859 remove_reply_handler, callback, cbarg); 860} 861 862 863/** 864 * \brief Reclaim memory and terminate any outstanding operations 865 */ 866void nfs_destroy(struct nfs_client *client) 867{ 868 rpc_destroy(&client->rpc_client); 869 free(client); 870} 871 872 873errval_t nfsstat_to_errval(enum nfsstat3 s) 874{ 875 switch(s) { 876 case NFS3_OK: return SYS_ERR_OK; 877 case NFS3ERR_PERM: return NFS_ERR_PERM; 878 case NFS3ERR_NOENT: return NFS_ERR_NOENT; 879 case NFS3ERR_IO: return NFS_ERR_IO; 880 case NFS3ERR_NXIO: return NFS_ERR_NXIO; 881 case NFS3ERR_ACCES: return NFS_ERR_ACCES; 882 case NFS3ERR_EXIST: return NFS_ERR_EXIST; 883 case NFS3ERR_XDEV: return NFS_ERR_XDEV; 884 case NFS3ERR_NODEV: return NFS_ERR_NODEV; 885 case NFS3ERR_NOTDIR: return NFS_ERR_NOTDIR; 886 case NFS3ERR_ISDIR: return NFS_ERR_ISDIR; 887 case NFS3ERR_INVAL: return NFS_ERR_INVAL; 888 case NFS3ERR_FBIG: return NFS_ERR_FBIG; 889 case NFS3ERR_NOSPC: return NFS_ERR_NOSPC; 890 case NFS3ERR_ROFS: return NFS_ERR_ROFS; 891 case NFS3ERR_MLINK: return NFS_ERR_MLINK; 892 case NFS3ERR_NAMETOOLONG: return NFS_ERR_NAMETOOLONG; 893 case NFS3ERR_NOTEMPTY: return NFS_ERR_NOTEMPTY; 894 case NFS3ERR_DQUOT: return NFS_ERR_DQUOT; 895 case NFS3ERR_STALE: return NFS_ERR_STALE; 896 case NFS3ERR_REMOTE: return NFS_ERR_REMOTE; 897 case NFS3ERR_BADHANDLE: return NFS_ERR_BADHANDLE; 898 case NFS3ERR_NOT_SYNC: return NFS_ERR_NOT_SYNC; 899 case NFS3ERR_BAD_COOKIE: return NFS_ERR_BAD_COOKIE; 900 case NFS3ERR_NOTSUPP: return NFS_ERR_NOTSUPP; 901 case NFS3ERR_TOOSMALL: return NFS_ERR_TOOSMALL; 902 case NFS3ERR_SERVERFAULT: return NFS_ERR_SERVERFAULT; 903 case NFS3ERR_BADTYPE: return NFS_ERR_BADTYPE; 904 case NFS3ERR_JUKEBOX: return NFS_ERR_JUKEBOX; 905 default: return NFS_ERR_TRANSPORT; // XXX: unknown 906 } 907} 908 909errval_t mountstat_to_errval(enum mountstat3 s) 910{ 911 switch(s) { 912 case MNT3_OK: return SYS_ERR_OK; 913 case MNT3ERR_PERM: return NFS_ERR_MNT_PERM; 914 case MNT3ERR_NOENT: return NFS_ERR_MNT_NOENT; 915 case MNT3ERR_IO: return NFS_ERR_MNT_IO; 916 case MNT3ERR_ACCES: return NFS_ERR_MNT_ACCES; 917 case MNT3ERR_NOTDIR: return NFS_ERR_MNT_NOTDIR; 918 case MNT3ERR_INVAL: return NFS_ERR_MNT_INVAL; 919 case MNT3ERR_NAMETOOLONG: return NFS_ERR_MNT_NAMETOOLONG; 920 case MNT3ERR_NOTSUPP: return NFS_ERR_MNT_NOTSUPP; 921 case MNT3ERR_SERVERFAULT: return NFS_ERR_MNT_SERVERFAULT; 922 default: return NFS_ERR_TRANSPORT; // XXX: unknown 923 } 924} 925