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