From f91748e6a99dfd3b6565aed4e98e84b4365eb9c2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 9 Nov 2010 18:34:21 +1030 Subject: [PATCH] nfs: initial import. Another Ronnie module! --- ccan/nfs/Makefile | 79 ++ ccan/nfs/dlinklist.h | 181 +++ ccan/nfs/init.c | 143 ++ ccan/nfs/libnfs-private.h | 68 + ccan/nfs/libnfs-raw.h | 559 ++++++++ ccan/nfs/libnfs-sync.c | 1069 ++++++++++++++ ccan/nfs/libnfs.c | 2727 ++++++++++++++++++++++++++++++++++++ ccan/nfs/libnfs.h | 910 ++++++++++++ ccan/nfs/mount.c | 191 +++ ccan/nfs/mount.x | 71 + ccan/nfs/nfs.c | 680 +++++++++ ccan/nfs/nfs.x | 840 +++++++++++ ccan/nfs/nfsacl.c | 45 + ccan/nfs/nfsacl.x | 8 + ccan/nfs/nfsclient-async.c | 233 +++ ccan/nfs/nfsclient-raw.c | 220 +++ ccan/nfs/nfsclient-sync.c | 202 +++ ccan/nfs/pdu.c | 226 +++ ccan/nfs/portmap.c | 73 + ccan/nfs/portmap.x | 37 + ccan/nfs/socket.c | 300 ++++ 21 files changed, 8862 insertions(+) create mode 100644 ccan/nfs/Makefile create mode 100644 ccan/nfs/dlinklist.h create mode 100644 ccan/nfs/init.c create mode 100644 ccan/nfs/libnfs-private.h create mode 100644 ccan/nfs/libnfs-raw.h create mode 100644 ccan/nfs/libnfs-sync.c create mode 100644 ccan/nfs/libnfs.c create mode 100644 ccan/nfs/libnfs.h create mode 100644 ccan/nfs/mount.c create mode 100644 ccan/nfs/mount.x create mode 100644 ccan/nfs/nfs.c create mode 100755 ccan/nfs/nfs.x create mode 100644 ccan/nfs/nfsacl.c create mode 100644 ccan/nfs/nfsacl.x create mode 100644 ccan/nfs/nfsclient-async.c create mode 100644 ccan/nfs/nfsclient-raw.c create mode 100644 ccan/nfs/nfsclient-sync.c create mode 100644 ccan/nfs/pdu.c create mode 100644 ccan/nfs/portmap.c create mode 100644 ccan/nfs/portmap.x create mode 100644 ccan/nfs/socket.c diff --git a/ccan/nfs/Makefile b/ccan/nfs/Makefile new file mode 100644 index 00000000..f4425d7e --- /dev/null +++ b/ccan/nfs/Makefile @@ -0,0 +1,79 @@ +CC=gcc +CFLAGS=-g -O0 -Wall -W -I. "-D_U_=__attribute__((unused))" -D_FILE_OFFSET_BITS=64 +LIBS= + +LIBNFS_OBJ = libnfs-raw-mount.o libnfs-raw-portmap.o libnfs-raw-nfs.o libnfs-raw-nfsacl.o mount.o nfs.o nfsacl.o portmap.o pdu.o init.o socket.o libnfs.o libnfs-sync.o + +all: nfsclient-raw nfsclient-async nfsclient-sync + +nfsclient-async: nfsclient-async.c libnfs.a + $(CC) -o $@ nfsclient-async.c libnfs.a $(LIBS) + +nfsclient-sync: nfsclient-sync.c libnfs.a + $(CC) -o $@ nfsclient-sync.c libnfs.a $(LIBS) + +nfsclient-raw: nfsclient-raw.c libnfs.a + $(CC) -o $@ nfsclient-raw.c libnfs.a $(LIBS) + +libnfs.a: $(LIBNFS_OBJ) + @echo Creating library $@ + ar r libnfs.a $(LIBNFS_OBJ) + ranlib libnfs.a + +libnfs-raw-mount.h: mount.x + @echo Generating $@ + rpcgen -h mount.x > libnfs-raw-mount.h + +libnfs-raw-mount.c: mount.x + @echo Generating $@ + rpcgen -c mount.x | sed -e "s/#include \"mount.h\"/#include \"libnfs-raw-mount.h\"/" > libnfs-raw-mount.c + +libnfs-raw-mount.o: libnfs-raw-mount.c libnfs-raw-mount.h + @echo Compiling $@ + gcc -g -c libnfs-raw-mount.c -o $@ + +libnfs-raw-nfs.h: nfs.x + @echo Generating $@ + rpcgen -h nfs.x > libnfs-raw-nfs.h + +libnfs-raw-nfs.c: nfs.x + @echo Generating $@ + rpcgen -c nfs.x | sed -e "s/#include \"nfs.h\"/#include \"libnfs-raw-nfs.h\"/" > libnfs-raw-nfs.c + +libnfs-raw-nfs.o: libnfs-raw-nfs.c libnfs-raw-nfs.h + @echo Compiling $@ + gcc -g -c libnfs-raw-nfs.c -o $@ + +libnfs-raw-nfsacl.h: nfsacl.x + @echo Generating $@ + rpcgen -h nfsacl.x > libnfs-raw-nfsacl.h + +libnfs-raw-nfsacl.c: nfsacl.x + @echo Generating $@ + rpcgen -c nfsacl.x | sed -e "s/#include \"nfsacl.h\"/#include \"libnfs-raw-nfsacl.h\"/" > libnfs-raw-nfsacl.c + +libnfs-raw-nfsacl.o: libnfs-raw-nfsacl.c libnfs-raw-nfsacl.h + @echo Compiling $@ + gcc -g -c libnfs-raw-nfsacl.c -o $@ + +libnfs-raw-portmap.h: portmap.x + @echo Generating $@ + rpcgen -h portmap.x > libnfs-raw-portmap.h + +libnfs-raw-portmap.c: portmap.x + @echo Generating $@ + rpcgen -c portmap.x | sed -e "s/#include \"portmap.h\"/#include \"libnfs-raw-portmap.h\"/" > libnfs-raw-portmap.c + +libnfs-raw-portmap.o: libnfs-raw-portmap.c libnfs-raw-portmap.h + @echo Compiling $@ + gcc -g -c libnfs-raw-portmap.c -o $@ + +clean: + rm -f *.o + rm -f *.a + rm -f libnfs-raw-mount.h libnfs-raw-mount.c + rm -f libnfs-raw-nfs.h libnfs-raw-nfs.c + rm -f libnfs-raw-nfsacl.h libnfs-raw-nfsacl.c + rm -f libnfs-raw-portmap.h libnfs-raw-portmap.c + rm -f nfsclient-raw nfsclient-async nfsclient-sync + diff --git a/ccan/nfs/dlinklist.h b/ccan/nfs/dlinklist.h new file mode 100644 index 00000000..6d525f90 --- /dev/null +++ b/ccan/nfs/dlinklist.h @@ -0,0 +1,181 @@ +/* + Unix SMB/CIFS implementation. + some simple double linked list macros + + Copyright (C) Andrew Tridgell 1998-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* To use these macros you must have a structure containing a next and + prev pointer */ + +#ifndef _DLINKLIST_H +#define _DLINKLIST_H + +/* + February 2010 - changed list format to have a prev pointer from the + list head. This makes DLIST_ADD_END() O(1) even though we only have + one list pointer. + + The scheme is as follows: + + 1) with no entries in the list: + list_head == NULL + + 2) with 1 entry in the list: + list_head->next == NULL + list_head->prev == list_head + + 3) with 2 entries in the list: + list_head->next == element2 + list_head->prev == element2 + element2->prev == list_head + element2->next == NULL + + 4) with N entries in the list: + list_head->next == element2 + list_head->prev == elementN + elementN->prev == element{N-1} + elementN->next == NULL + + This allows us to find the tail of the list by using + list_head->prev, which means we can add to the end of the list in + O(1) time + + + Note that the 'type' arguments below are no longer needed, but + are kept for now to prevent an incompatible argument change + */ + + +/* + add an element at the front of a list +*/ +#define DLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (p)->prev = (list) = (p); \ + (p)->next = NULL; \ + } else { \ + (p)->prev = (list)->prev; \ + (list)->prev = (p); \ + (p)->next = (list); \ + (list) = (p); \ + } \ +} while (0) + +/* + remove an element from a list + Note that the element doesn't have to be in the list. If it + isn't then this is a no-op +*/ +#define DLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + (list) = (p)->next; \ + } else if ((list) && (p) == (list)->prev) { \ + (p)->prev->next = NULL; \ + (list)->prev = (p)->prev; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) != (list)) (p)->next = (p)->prev = NULL; \ +} while (0) + +/* + find the head of the list given any element in it. + Note that this costs O(N), so you should avoid this macro + if at all possible! +*/ +#define DLIST_HEAD(p, result_head) \ +do { \ + (result_head) = (p); \ + while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \ +} while(0) + +/* return the last element in the list */ +#define DLIST_TAIL(list) ((list)?(list)->prev:NULL) + +/* return the previous element in the list. */ +#define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL) + +/* insert 'p' after the given element 'el' in a list. If el is NULL then + this is the same as a DLIST_ADD() */ +#define DLIST_ADD_AFTER(list, p, el) \ +do { \ + if (!(list) || !(el)) { \ + DLIST_ADD(list, p); \ + } else { \ + (p)->prev = (el); \ + (p)->next = (el)->next; \ + (el)->next = (p); \ + if ((p)->next) (p)->next->prev = (p); \ + if ((list)->prev == (el)) (list)->prev = (p); \ + }\ +} while (0) + + +/* + add to the end of a list. + Note that 'type' is ignored +*/ +#define DLIST_ADD_END(list, p, type) \ +do { \ + if (!(list)) { \ + DLIST_ADD(list, p); \ + } else { \ + DLIST_ADD_AFTER(list, p, (list)->prev); \ + } \ +} while (0) + +/* promote an element to the from of a list */ +#define DLIST_PROMOTE(list, p) \ +do { \ + DLIST_REMOVE(list, p); \ + DLIST_ADD(list, p); \ +} while (0) + +/* + demote an element to the end of a list. + Note that 'type' is ignored +*/ +#define DLIST_DEMOTE(list, p, type) \ +do { \ + DLIST_REMOVE(list, p); \ + DLIST_ADD_END(list, p, NULL); \ +} while (0) + +/* + concatenate two lists - putting all elements of the 2nd list at the + end of the first list. + Note that 'type' is ignored +*/ +#define DLIST_CONCATENATE(list1, list2, type) \ +do { \ + if (!(list1)) { \ + (list1) = (list2); \ + } else { \ + (list1)->prev->next = (list2); \ + if (list2) { \ + void *_tmplist = (void *)(list1)->prev; \ + (list1)->prev = (list2)->prev; \ + (list2)->prev = _tmplist; \ + } \ + } \ +} while (0) + +#endif /* _DLINKLIST_H */ diff --git a/ccan/nfs/init.c b/ccan/nfs/init.c new file mode 100644 index 00000000..ae0c7878 --- /dev/null +++ b/ccan/nfs/init.c @@ -0,0 +1,143 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include "dlinklist.h" +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-private.h" + +struct rpc_context *rpc_init_context(void) +{ + struct rpc_context *rpc; + + rpc = malloc(sizeof(struct rpc_context)); + if (rpc == NULL) { + printf("Failed to allocate rpc context\n"); + return NULL; + } + bzero(rpc, sizeof(struct rpc_context)); + + rpc->encodebuflen = 65536; + rpc->encodebuf = malloc(rpc->encodebuflen); + if (rpc->encodebuf == NULL) { + printf("Failed to allocate a buffer for rpc encoding\n"); + free(rpc); + return NULL; + } + + rpc->auth = authunix_create_default(); + if (rpc->auth == NULL) { + printf("failed to create authunix\n"); + free(rpc->encodebuf); + free(rpc); + return NULL; + } + rpc->xid = 1; + rpc->fd = -1; + + return rpc; +} + + +void rpc_set_auth(struct rpc_context *rpc, struct AUTH *auth) +{ + if (rpc->auth != NULL) { + auth_destroy(rpc->auth); + } + rpc->auth = auth; +} + + +void rpc_set_error(struct rpc_context *rpc, char *error_string, ...) +{ + va_list ap; + char *str; + + if (rpc->error_string != NULL) { + free(rpc->error_string); + } + va_start(ap, error_string); + vasprintf(&str, error_string, ap); + rpc->error_string = str; + va_end(ap); +} + +char *rpc_get_error(struct rpc_context *rpc) +{ + return rpc->error_string; +} + +void rpc_error_all_pdus(struct rpc_context *rpc, char *error) +{ + struct rpc_pdu *pdu; + + while((pdu = rpc->outqueue) != NULL) { + pdu->cb(rpc, RPC_STATUS_ERROR, error, pdu->private_data); + DLIST_REMOVE(rpc->outqueue, pdu); + rpc_free_pdu(rpc, pdu); + } + while((pdu = rpc->waitpdu) != NULL) { + pdu->cb(rpc, RPC_STATUS_ERROR, error, pdu->private_data); + DLIST_REMOVE(rpc->waitpdu, pdu); + rpc_free_pdu(rpc, pdu); + } +} + + +void rpc_destroy_context(struct rpc_context *rpc) +{ + struct rpc_pdu *pdu; + + while((pdu = rpc->outqueue) != NULL) { + pdu->cb(rpc, RPC_STATUS_CANCEL, NULL, pdu->private_data); + DLIST_REMOVE(rpc->outqueue, pdu); + rpc_free_pdu(rpc, pdu); + } + while((pdu = rpc->waitpdu) != NULL) { + pdu->cb(rpc, RPC_STATUS_CANCEL, NULL, pdu->private_data); + DLIST_REMOVE(rpc->waitpdu, pdu); + rpc_free_pdu(rpc, pdu); + } + + auth_destroy(rpc->auth); + rpc->auth =NULL; + + if (rpc->fd != -1) { + close(rpc->fd); + } + + if (rpc->encodebuf != NULL) { + free(rpc->encodebuf); + rpc->encodebuf = NULL; + } + + if (rpc->error_string != NULL) { + free(rpc->error_string); + rpc->error_string = NULL; + } + + free(rpc); +} + + diff --git a/ccan/nfs/libnfs-private.h b/ccan/nfs/libnfs-private.h new file mode 100644 index 00000000..d075f171 --- /dev/null +++ b/ccan/nfs/libnfs-private.h @@ -0,0 +1,68 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include + +struct rpc_context { + int fd; + int is_connected; + + char *error_string; + + rpc_cb connect_cb; + void *connect_data; + + AUTH *auth; + unsigned long xid; + + /* buffer used for encoding RPC PDU */ + char *encodebuf; + int encodebuflen; + + struct rpc_pdu *outqueue; + struct rpc_pdu *waitpdu; + + int insize; + int inpos; + char *inbuf; +}; + +struct rpc_pdu { + struct rpc_pdu *prev, *next; + + unsigned long xid; + XDR xdr; + + int written; + struct rpc_data outdata; + + rpc_cb cb; + void *private_data; + + /* function to decode the xdr reply data and buffer to decode into */ + xdrproc_t xdr_decode_fn; + caddr_t xdr_decode_buf; + int xdr_decode_bufsize; +}; + +struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, xdrproc_t xdr_decode_fn, int xdr_bufsize); +void rpc_free_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu); +int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu); +int rpc_get_pdu_size(char *buf); +int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size); +void rpc_error_all_pdus(struct rpc_context *rpc, char *error); + diff --git a/ccan/nfs/libnfs-raw.h b/ccan/nfs/libnfs-raw.h new file mode 100644 index 00000000..c4589cb5 --- /dev/null +++ b/ccan/nfs/libnfs-raw.h @@ -0,0 +1,559 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ +/* + * This is the lowlevel interface to access NFS resources. + * Through this interface you have access to the full gamut of nfs and nfs related + * protocol as well as the XDR encoded/decoded structures. + */ +#include + +struct rpc_data { + int size; + unsigned char *data; +}; + +struct rpc_context; +struct rpc_context *rpc_init_context(void); +void rpc_destroy_context(struct rpc_context *rpc); + +struct AUTH; +void rpc_set_auth(struct rpc_context *rpc, struct AUTH *auth); + +int rpc_get_fd(struct rpc_context *rpc); +int rpc_which_events(struct rpc_context *rpc); +int rpc_service(struct rpc_context *rpc, int revents); +char *rpc_get_error(struct rpc_context *rpc); + + +#define RPC_STATUS_SUCCESS 0 +#define RPC_STATUS_ERROR 1 +#define RPC_STATUS_CANCEL 2 + +typedef void (*rpc_cb)(struct rpc_context *rpc, int status, void *data, void *private_data); + +/* + * Async connection to the tcp port at server:port. + * Function returns + * 0 : The connection was initiated. Once the connection establish finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the connection. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : The tcp connection was successfully established. + * data is NULL. + * RPC_STATUS_ERROR : The connection failed to establish. + * data is the erro string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * : data is NULL. + */ +int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, int use_privileged_port, rpc_cb cb, void *private_data); +/* + * When disconnecting a connection in flight. All commands in flight will be called with the callback + * and status RPC_STATUS_ERROR. Data will be the error string for the disconnection. + */ +int rpc_disconnect(struct rpc_context *rpc, char *error); + +void rpc_set_error(struct rpc_context *rpc, char *error_string, ...); + + +/* + * PORTMAP FUNCTIONS + */ + +/* + * Call PORTMAPPER/NULL + * Function returns + * 0 : The connection was initiated. Once the connection establish finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the connection. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the portmapper daemon. + * data is NULL. + * RPC_STATUS_ERROR : An error occured when trying to contact the portmapper. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_pmap_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data); + + +/* + * Call PORTMAPPER/GETPORT. + * Function returns + * 0 : The connection was initiated. Once the connection establish finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the connection. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the portmapper daemon. + * data is a (uint32_t *), containing the port returned. + * RPC_STATUS_ERROR : An error occured when trying to contact the portmapper. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_pmap_getport_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data); + + + +/* + * MOUNT FUNCTIONS + */ +char *mountstat3_to_str(int stat); +int mountstat3_to_errno(int error); + +/* + * Call MOUNT/NULL + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon. + * data is NULL. + * RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_mount_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data); + +/* + * Call MOUNT/MNT + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon. + * data is mountres3 *. + * RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_mount_mnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data); + +/* + * Call MOUNT/DUMP + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon. + * data is a mountlist. + * RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_mount_dump_async(struct rpc_context *rpc, rpc_cb cb, void *private_data); + +/* + * Call MOUNT/UMNT + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon. + * data NULL. + * RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_mount_umnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data); + +/* + * Call MOUNT/UMNTALL + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon. + * data NULL. + * RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_mount_umntall_async(struct rpc_context *rpc, rpc_cb cb, void *private_data); + +/* + * Call MOUNT/EXPORT + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon. + * data is an exports. + * RPC_STATUS_ERROR : An error occured when trying to contact the mount daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_mount_export_async(struct rpc_context *rpc, rpc_cb cb, void *private_data); + + + + +/* + * NFS FUNCTIONS + */ +struct nfs_fh3; +char *nfsstat3_to_str(int error); +int nfsstat3_to_errno(int error); + +/* + * Call NFS/NULL + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is NULL. + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data); + +/* + * Call NFS/GETATTR + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is GETATTR3res + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_getattr_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data); + +/* + * Call NFS/LOOKUP + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is LOOKUP3res + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_lookup_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, void *private_data); + +/* + * Call NFS/ACCESS + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is ACCESS3res + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_access_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, int access, void *private_data); + +/* + * Call NFS/READ + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is ACCESS3res + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_read_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, off_t offset, size_t count, void *private_data); + +/* + * Call NFS/WRITE + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is WRITE3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_write_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *buf, off_t offset, size_t count, int stable_how, void *private_data); + +/* + * Call NFS/COMMIT + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is COMMIT3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_commit_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data); + + +/* + * Call NFS/SETATTR + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is SETATTR3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +struct SETATTR3args; +int rpc_nfs_setattr_async(struct rpc_context *rpc, rpc_cb cb, struct SETATTR3args *args, void *private_data); + + + +/* + * Call NFS/MKDIR + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is MKDIR3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_mkdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data); + + + + + +/* + * Call NFS/RMDIR + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is RMDIR3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_rmdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data); + + + + +/* + * Call NFS/CREATE + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is CREATE3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_create_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, int mode, void *private_data); + + + + +/* + * Call NFS/REMOVE + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is REMOVE3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_remove_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, void *private_data); + + + +/* + * Call NFS/REMOVE + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is READDIR3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_readdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, uint64_t cookie, char *cookieverf, int count, void *private_data); + +/* + * Call NFS/FSSTAT + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is FSSTAT3res + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_fsstat_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data); + + + + +/* + * Call NFS/READLINK + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is READLINK3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_readlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data); + + + +/* + * Call NFS/SYMLINK + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is SYMLINK3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_symlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *newname, char *oldpath, void *private_data); + + +/* + * Call NFS/RENAME + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is RENAME3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_rename_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *olddir, char *oldname, struct nfs_fh3 *newdir, char *newname, void *private_data); + + + +/* + * Call NFS/LINK + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is LINK3res * + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfs_link_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *file, struct nfs_fh3 *newdir, char *newname, void *private_data); + + + + + + + +/* + * NFSACL FUNCTIONS + */ +/* + * Call NFSACL/NULL + * Function returns + * 0 : The call was initiated. The callback will be invoked when the call completes. + * <0 : An error occured when trying to set up the call. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon. + * data is NULL. + * RPC_STATUS_ERROR : An error occured when trying to contact the nfs daemon. + * data is the error string. + * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete. + * data is NULL. + */ +int rpc_nfsacl_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data); + diff --git a/ccan/nfs/libnfs-sync.c b/ccan/nfs/libnfs-sync.c new file mode 100644 index 00000000..f8f98bc1 --- /dev/null +++ b/ccan/nfs/libnfs-sync.c @@ -0,0 +1,1069 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ +/* + * High level api to nfs filesystems + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-raw-mount.h" +#include "libnfs-raw-nfs.h" + +struct sync_cb_data { + int is_finished; + int status; + off_t offset; + void *return_data; + int return_int; +}; + + +static void wait_for_reply(struct nfs_context *nfs, struct sync_cb_data *cb_data) +{ + struct pollfd pfd; + + for (;;) { + if (cb_data->is_finished) { + break; + } + pfd.fd = nfs_get_fd(nfs); + pfd.events = nfs_which_events(nfs); + + if (poll(&pfd, 1, -1) < 0) { + printf("Poll failed"); + cb_data->status = -EIO; + break; + } + if (nfs_service(nfs, pfd.revents) < 0) { + printf("nfs_service failed\n"); + cb_data->status = -EIO; + break; + } + } +} + + + + + + +/* + * connect to the server and mount the export + */ +static void mount_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("mount/mnt call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_mount_sync(struct nfs_context *nfs, const char *server, const char *export) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_mount_async(nfs, server, export, mount_cb, &cb_data) != 0) { + printf("nfs_mount_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + +/* + * stat() + */ +static void stat_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("stat call failed with \"%s\"\n", (char *)data); + return; + } + + memcpy(cb_data->return_data, data, sizeof(struct stat)); +} + +int nfs_stat_sync(struct nfs_context *nfs, const char *path, struct stat *st) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + cb_data.return_data = st; + + if (nfs_stat_async(nfs, path, stat_cb, &cb_data) != 0) { + printf("nfs_stat_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + +/* + * open() + */ +static void open_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + struct nfsfh *fh, **nfsfh; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("open call failed with \"%s\"\n", (char *)data); + return; + } + + fh = data; + nfsfh = cb_data->return_data; + *nfsfh = fh; +} + +int nfs_open_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + cb_data.return_data = nfsfh; + + if (nfs_open_async(nfs, path, mode, open_cb, &cb_data) != 0) { + printf("nfs_open_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + +/* + * pread() + */ +static void pread_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + char *buffer; + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("pread call failed with \"%s\"\n", (char *)data); + return; + } + + buffer = cb_data->return_data; + memcpy(buffer, (char *)data, status); +} + +int nfs_pread_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buffer) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + cb_data.return_data = buffer; + + if (nfs_pread_async(nfs, nfsfh, offset, count, pread_cb, &cb_data) != 0) { + printf("nfs_pread_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + +/* + * read() + */ +int nfs_read_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buffer) +{ + return nfs_pread_sync(nfs, nfsfh, nfs_get_current_offset(nfsfh), count, buffer); +} + +/* + * close() + */ +static void close_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("close call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_close_sync(struct nfs_context *nfs, struct nfsfh *nfsfh) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_close_async(nfs, nfsfh, close_cb, &cb_data) != 0) { + printf("nfs_close_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + +/* + * fstat() + */ +int nfs_fstat_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + cb_data.return_data = st; + + if (nfs_fstat_async(nfs, nfsfh, stat_cb, &cb_data) != 0) { + printf("nfs_fstat_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + +/* + * pwrite() + */ +static void pwrite_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("pwrite call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_pwrite_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_pwrite_async(nfs, nfsfh, offset, count, buf, pwrite_cb, &cb_data) != 0) { + printf("nfs_pwrite_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + +/* + * write() + */ +int nfs_write_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf) +{ + return nfs_pwrite_sync(nfs, nfsfh, nfs_get_current_offset(nfsfh), count, buf); +} + + +/* + * fsync() + */ +static void fsync_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("fsync call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_fsync_sync(struct nfs_context *nfs, struct nfsfh *nfsfh) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_fsync_async(nfs, nfsfh, fsync_cb, &cb_data) != 0) { + printf("nfs_fsync_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + +/* + * ftruncate() + */ +static void ftruncate_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("ftruncate call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_ftruncate_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_ftruncate_async(nfs, nfsfh, length, ftruncate_cb, &cb_data) != 0) { + printf("nfs_ftruncate_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * truncate() + */ +static void truncate_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("truncate call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_truncate_sync(struct nfs_context *nfs, const char *path, off_t length) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_truncate_async(nfs, path, length, truncate_cb, &cb_data) != 0) { + printf("nfs_ftruncate_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + + +/* + * mkdir() + */ +static void mkdir_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("mkdir call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_mkdir_sync(struct nfs_context *nfs, const char *path) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_mkdir_async(nfs, path, mkdir_cb, &cb_data) != 0) { + printf("nfs_mkdir_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + + +/* + * rmdir() + */ +static void rmdir_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("rmdir call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_rmdir_sync(struct nfs_context *nfs, const char *path) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_rmdir_async(nfs, path, rmdir_cb, &cb_data) != 0) { + printf("nfs_rmdir_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * creat() + */ +static void creat_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + struct nfsfh *fh, **nfsfh; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("creat call failed with \"%s\"\n", (char *)data); + return; + } + + fh = data; + nfsfh = cb_data->return_data; + *nfsfh = fh; +} + +int nfs_creat_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + cb_data.return_data = nfsfh; + + if (nfs_creat_async(nfs, path, mode, creat_cb, &cb_data) != 0) { + printf("nfs_creat_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + +/* + * unlink() + */ +static void unlink_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("unlink call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_unlink_sync(struct nfs_context *nfs, const char *path) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_unlink_async(nfs, path, unlink_cb, &cb_data) != 0) { + printf("nfs_unlink_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * opendir() + */ +static void opendir_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + struct nfsdir *dir, **nfsdir; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("opendir call failed with \"%s\"\n", (char *)data); + return; + } + + dir = data; + nfsdir = cb_data->return_data; + *nfsdir = dir; +} + +int nfs_opendir_sync(struct nfs_context *nfs, const char *path, struct nfsdir **nfsdir) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + cb_data.return_data = nfsdir; + + if (nfs_opendir_async(nfs, path, opendir_cb, &cb_data) != 0) { + printf("nfs_opendir_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + +/* + * lseek() + */ +static void lseek_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("lseek call failed with \"%s\"\n", (char *)data); + return; + } + + if (cb_data->return_data != NULL) { + memcpy(cb_data->return_data, data, sizeof(off_t)); + } +} + +int nfs_lseek_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, off_t *current_offset) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + cb_data.return_data = current_offset; + + if (nfs_lseek_async(nfs, nfsfh, offset, whence, lseek_cb, &cb_data) != 0) { + printf("nfs_lseek_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * statvfs() + */ +static void statvfs_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("statvfs call failed with \"%s\"\n", (char *)data); + return; + } + + memcpy(cb_data->return_data, data, sizeof(struct statvfs)); +} + +int nfs_statvfs_sync(struct nfs_context *nfs, const char *path, struct statvfs *svfs) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + cb_data.return_data = svfs; + + if (nfs_statvfs_async(nfs, path, statvfs_cb, &cb_data) != 0) { + printf("nfs_statvfs_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + + +/* + * readlink() + */ +static void readlink_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("readlink call failed with \"%s\"\n", (char *)data); + return; + } + + if (strlen(data) > (size_t)cb_data->return_int) { + printf("Too small buffer for readlink\n"); + cb_data->status = -ENAMETOOLONG; + return; + } + + memcpy(cb_data->return_data, data, strlen(data)+1); +} + +int nfs_readlink_sync(struct nfs_context *nfs, const char *path, char *buf, int bufsize) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + cb_data.return_data = buf; + cb_data.return_int = bufsize; + + if (nfs_readlink_async(nfs, path, readlink_cb, &cb_data) != 0) { + printf("nfs_readlink_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * chmod() + */ +static void chmod_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("chmod call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_chmod_sync(struct nfs_context *nfs, const char *path, int mode) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_chmod_async(nfs, path, mode, chmod_cb, &cb_data) != 0) { + printf("nfs_chmod_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + +/* + * fchmod() + */ +static void fchmod_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("fchmod call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_fchmod_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_fchmod_async(nfs, nfsfh, mode, fchmod_cb, &cb_data) != 0) { + printf("nfs_fchmod_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + +/* + * chown() + */ +static void chown_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("chown call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_chown_sync(struct nfs_context *nfs, const char *path, int uid, int gid) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_chown_async(nfs, path, uid, gid, chown_cb, &cb_data) != 0) { + printf("nfs_chown_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + +/* + * fchown() + */ +static void fchown_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("fchown call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_fchown_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_fchown_async(nfs, nfsfh, uid, gid, fchown_cb, &cb_data) != 0) { + printf("nfs_fchown_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * utimes() + */ +static void utimes_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("utimes call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_utimes_sync(struct nfs_context *nfs, const char *path, struct timeval *times) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_utimes_async(nfs, path, times, utimes_cb, &cb_data) != 0) { + printf("nfs_utimes_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * utime() + */ +static void utime_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("utime call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_utime_sync(struct nfs_context *nfs, const char *path, struct utimbuf *times) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_utime_async(nfs, path, times, utime_cb, &cb_data) != 0) { + printf("nfs_utimes_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + + +/* + * access() + */ +static void access_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("access call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_access_sync(struct nfs_context *nfs, const char *path, int mode) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_access_async(nfs, path, mode, access_cb, &cb_data) != 0) { + printf("nfs_access_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * symlink() + */ +static void symlink_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("symlink call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_symlink_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_symlink_async(nfs, oldpath, newpath, symlink_cb, &cb_data) != 0) { + printf("nfs_symlink_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * rename() + */ +static void rename_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("rename call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_rename_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_rename_async(nfs, oldpath, newpath, rename_cb, &cb_data) != 0) { + printf("nfs_rename_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} + + + +/* + * link() + */ +static void link_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + + if (status < 0) { + printf("link call failed with \"%s\"\n", (char *)data); + return; + } +} + +int nfs_link_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath) +{ + struct sync_cb_data cb_data; + + cb_data.is_finished = 0; + + if (nfs_link_async(nfs, oldpath, newpath, link_cb, &cb_data) != 0) { + printf("nfs_link_async failed\n"); + return -1; + } + + wait_for_reply(nfs, &cb_data); + + return cb_data.status; +} diff --git a/ccan/nfs/libnfs.c b/ccan/nfs/libnfs.c new file mode 100644 index 00000000..d3257247 --- /dev/null +++ b/ccan/nfs/libnfs.c @@ -0,0 +1,2727 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ +/* + * High level api to nfs filesystems + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-raw-mount.h" +#include "libnfs-raw-nfs.h" + +struct nfsfh { + struct nfs_fh3 fh; + int is_sync; + off_t offset; +}; + +struct nfsdir { + struct nfsdirent *entries; + struct nfsdirent *current; +}; + +void nfs_free_nfsdir(struct nfsdir *nfsdir) +{ + while (nfsdir->entries) { + struct nfsdirent *dirent = nfsdir->entries->next; + if (nfsdir->entries->name != NULL) { + free(nfsdir->entries->name); + } + free(nfsdir->entries); + nfsdir->entries = dirent; + } + free(nfsdir); +} + +struct nfs_context { + struct rpc_context *rpc; + char *server; + char *export; + struct nfs_fh3 rootfh; + int acl_support; +}; + +struct nfs_cb_data; +typedef int (*continue_func)(struct nfs_context *nfs, struct nfs_cb_data *data); + +struct nfs_cb_data { + struct nfs_context *nfs; + struct nfsfh *nfsfh; + char *saved_path, *path; + + nfs_cb cb; + void *private_data; + + continue_func continue_cb; + void *continue_data; + void (*free_continue_data)(void *); + int continue_int; + + struct nfs_fh3 fh; +}; + +static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh); + + +void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth) +{ + return rpc_set_auth(nfs->rpc, auth); +} + +int nfs_get_fd(struct nfs_context *nfs) +{ + return rpc_get_fd(nfs->rpc); +} + +int nfs_which_events(struct nfs_context *nfs) +{ + return rpc_which_events(nfs->rpc); +} + +int nfs_service(struct nfs_context *nfs, int revents) +{ + return rpc_service(nfs->rpc, revents); +} + +char *nfs_get_error(struct nfs_context *nfs) +{ + return rpc_get_error(nfs->rpc); +}; + +struct nfs_context *nfs_init_context(void) +{ + struct nfs_context *nfs; + + nfs = malloc(sizeof(struct nfs_context)); + if (nfs == NULL) { + printf("Failed to allocate nfs context\n"); + return NULL; + } + nfs->rpc = rpc_init_context(); + if (nfs->rpc == NULL) { + printf("Failed to allocate rpc sub-context\n"); + free(nfs); + return NULL; + } + + return nfs; +} + +void nfs_destroy_context(struct nfs_context *nfs) +{ + rpc_destroy_context(nfs->rpc); + nfs->rpc = NULL; + + if (nfs->server) { + free(nfs->server); + nfs->server = NULL; + } + + if (nfs->export) { + free(nfs->export); + nfs->export = NULL; + } + + if (nfs->rootfh.data.data_val != NULL) { + free(nfs->rootfh.data.data_val); + nfs->rootfh.data.data_val = NULL; + } + + free(nfs); +} + +void free_nfs_cb_data(struct nfs_cb_data *data) +{ + if (data->saved_path != NULL) { + free(data->saved_path); + data->saved_path = NULL; + } + + if (data->continue_data != NULL) { + data->free_continue_data(data->continue_data); + data->continue_data = NULL; + } + + if (data->fh.data.data_val != NULL) { + free(data->fh.data.data_val); + data->fh.data.data_val = NULL; + } + + free(data); +} + + + + + +static void nfs_mount_10_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static void nfs_mount_9_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + nfs->acl_support = 1; + if (status == RPC_STATUS_ERROR) { + nfs->acl_support = 0; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + if (rpc_nfs_getattr_async(rpc, nfs_mount_10_cb, &nfs->rootfh, data) != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } +} + +static void nfs_mount_8_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + if (rpc_nfsacl_null_async(rpc, nfs_mount_9_cb, data) != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } +} + +static void nfs_mount_7_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + if (rpc_nfs_null_async(rpc, nfs_mount_8_cb, data) != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } +} + + +static void nfs_mount_6_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + mountres3 *res; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->fhs_status != MNT3_OK) { + rpc_set_error(rpc, "RPC error: Mount failed with error %s(%d) %s(%d)", mountstat3_to_str(res->fhs_status), res->fhs_status, strerror(-mountstat3_to_errno(res->fhs_status)), -mountstat3_to_errno(res->fhs_status)); + data->cb(mountstat3_to_errno(res->fhs_status), nfs, rpc_get_error(rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + nfs->rootfh.data.data_len = res->mountres3_u.mountinfo.fhandle.fhandle3_len; + nfs->rootfh.data.data_val = malloc(nfs->rootfh.data.data_len); + if (nfs->rootfh.data.data_val == NULL) { + rpc_set_error(rpc, "Out of memory. Could not allocate memory to store root filehandle"); + data->cb(-ENOMEM, nfs, rpc_get_error(rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + memcpy(nfs->rootfh.data.data_val, res->mountres3_u.mountinfo.fhandle.fhandle3_val, nfs->rootfh.data.data_len); + + rpc_disconnect(rpc, "normal disconnect"); + if (rpc_connect_async(rpc, nfs->server, 2049, 1, nfs_mount_7_cb, data) != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } +} + + +static void nfs_mount_5_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + if (rpc_mount_mnt_async(rpc, nfs_mount_6_cb, nfs->export, data) != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } +} + +static void nfs_mount_4_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + if (rpc_mount_null_async(rpc, nfs_mount_5_cb, data) != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } +} + +static void nfs_mount_3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + uint32_t mount_port; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + mount_port = *(uint32_t *)command_data; + if (mount_port == 0) { + rpc_set_error(rpc, "RPC error. Mount program is not available on %s", nfs->server); + data->cb(-ENOENT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + + rpc_disconnect(rpc, "normal disconnect"); + if (rpc_connect_async(rpc, nfs->server, mount_port, 1, nfs_mount_4_cb, data) != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } +} + + +static void nfs_mount_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, nfs_mount_3_cb, private_data) != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } +} + +static void nfs_mount_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + if (rpc_pmap_null_async(rpc, nfs_mount_2_cb, data) != 0) { + data->cb(-ENOMEM, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } +} + +/* + * Async call for mounting an nfs share and geting the root filehandle + */ +int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *export, nfs_cb cb, void *private_data) +{ + struct nfs_cb_data *data; + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory"); + printf("failed to allocate memory for nfs mount data\n"); + return -1; + } + bzero(data, sizeof(struct nfs_cb_data)); + nfs->server = strdup(server); + nfs->export = strdup(export); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + + if (rpc_connect_async(nfs->rpc, server, 111, 0, nfs_mount_1_cb, data) != 0) { + printf("Failed to start connection\n"); + free_nfs_cb_data(data); + return -4; + } + + return 0; +} + + + +/* + * Functions to first look up a path, component by component, and then finally call a specific function once + * the filehandle for the final component is found. + */ +static void nfs_lookup_path_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + LOOKUP3res *res; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: Lookup of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + if (nfs_lookup_path_async_internal(nfs, data, &res->LOOKUP3res_u.resok.object) != 0) { + rpc_set_error(nfs->rpc, "Failed to create lookup pdu"); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } +} + +static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh) +{ + char *path, *str; + + while (*data->path == '/') { + data->path++; + } + + path = data->path; + str = index(path, '/'); + if (str != NULL) { + *str = 0; + data->path = str+1; + } else { + while (*data->path != 0) { + data->path++; + } + } + + if (*path == 0) { + data->fh.data.data_len = fh->data.data_len; + data->fh.data.data_val = malloc(data->fh.data.data_len); + if (data->fh.data.data_val == NULL) { + rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + memcpy(data->fh.data.data_val, fh->data.data_val, data->fh.data.data_len); + data->continue_cb(nfs, data); + return 0; + } + + if (rpc_nfs_lookup_async(nfs->rpc, nfs_lookup_path_1_cb, fh, path, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send lookup call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data, continue_func continue_cb, void *continue_data, void (*free_continue_data)(void *), int continue_int) +{ + struct nfs_cb_data *data; + + if (path[0] != '/') { + rpc_set_error(nfs->rpc, "Pathname is not absulute %s", path); + return -1; + } + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure"); + printf("failed to allocate memory for nfs cb data\n"); + free_nfs_cb_data(data); + return -2; + } + bzero(data, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->continue_cb = continue_cb; + data->continue_data = continue_data; + data->free_continue_data = free_continue_data; + data->continue_int = continue_int; + data->private_data = private_data; + data->saved_path = strdup(path); + if (data->saved_path == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to copy path string"); + printf("failed to allocate memory for path string\n"); + free_nfs_cb_data(data); + return -2; + } + data->path = data->saved_path; + + if (nfs_lookup_path_async_internal(nfs, data, &nfs->rootfh) != 0) { + printf("failed to lookup path\n"); + /* return 0 here since the callback will be invoked if there is a failure */ + return 0; + } + return 0; +} + + + + + +/* + * Async stat() + */ +static void nfs_stat_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + GETATTR3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + struct stat st; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: GETATTR of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + st.st_dev = -1; + st.st_ino = res->GETATTR3res_u.resok.obj_attributes.fileid; + st.st_mode = res->GETATTR3res_u.resok.obj_attributes.mode; + st.st_nlink = res->GETATTR3res_u.resok.obj_attributes.nlink; + st.st_uid = res->GETATTR3res_u.resok.obj_attributes.uid; + st.st_gid = res->GETATTR3res_u.resok.obj_attributes.gid; + st.st_rdev = 0; + st.st_size = res->GETATTR3res_u.resok.obj_attributes.size; + st.st_blksize = 4096; + st.st_blocks = res->GETATTR3res_u.resok.obj_attributes.size / 4096; + st.st_atime = res->GETATTR3res_u.resok.obj_attributes.atime.seconds; + st.st_mtime = res->GETATTR3res_u.resok.obj_attributes.mtime.seconds; + st.st_ctime = res->GETATTR3res_u.resok.obj_attributes.ctime.seconds; + + data->cb(0, nfs, &st, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_stat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + if (rpc_nfs_getattr_async(nfs->rpc, nfs_stat_1_cb, &data->fh, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send STAT GETATTR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) +{ + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_stat_continue_internal, NULL, NULL, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -1; + } + + return 0; +} + + + + + +/* + * Async open() + */ +static void nfs_open_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + ACCESS3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + struct nfsfh *nfsfh; + unsigned int nfsmode = 0; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: ACCESS of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + if (data->continue_int & O_WRONLY) { + nfsmode |= ACCESS3_MODIFY; + } + if (data->continue_int & O_RDWR) { + nfsmode |= ACCESS3_READ|ACCESS3_MODIFY; + } + if (!(data->continue_int & (O_WRONLY|O_RDWR))) { + nfsmode |= ACCESS3_READ; + } + + + if (res->ACCESS3res_u.resok.access != nfsmode) { + rpc_set_error(nfs->rpc, "NFS: ACCESS denied. Required access %c%c%c. Allowed access %c%c%c", + nfsmode&ACCESS3_READ?'r':'-', + nfsmode&ACCESS3_MODIFY?'w':'-', + nfsmode&ACCESS3_EXECUTE?'x':'-', + res->ACCESS3res_u.resok.access&ACCESS3_READ?'r':'-', + res->ACCESS3res_u.resok.access&ACCESS3_MODIFY?'w':'-', + res->ACCESS3res_u.resok.access&ACCESS3_EXECUTE?'x':'-'); + data->cb(-EACCES, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + nfsfh = malloc(sizeof(struct nfsfh)); + if (nfsfh == NULL) { + rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure"); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + bzero(nfsfh, sizeof(struct nfsfh)); + + if (data->continue_int & O_SYNC) { + nfsfh->is_sync = 1; + } + + /* steal the filehandle */ + nfsfh->fh.data.data_len = data->fh.data.data_len; + nfsfh->fh.data.data_val = data->fh.data.data_val; + data->fh.data.data_val = NULL; + + data->cb(0, nfs, nfsfh, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_open_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + int nfsmode = 0; + + if (data->continue_int & O_WRONLY) { + nfsmode |= ACCESS3_MODIFY; + } + if (data->continue_int & O_RDWR) { + nfsmode |= ACCESS3_READ|ACCESS3_MODIFY; + } + if (!(data->continue_int & (O_WRONLY|O_RDWR))) { + nfsmode |= ACCESS3_READ; + } + + if (rpc_nfs_access_async(nfs->rpc, nfs_open_cb, &data->fh, nfsmode, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send OPEN ACCESS call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_open_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) +{ + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_open_continue_internal, NULL, NULL, mode) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -2; + } + + return 0; +} + + + + + +/* + * Async pread() + */ +static void nfs_pread_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + READ3res *res; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: Read failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->nfsfh->offset += res->READ3res_u.resok.count; + data->cb(res->READ3res_u.resok.count, nfs, res->READ3res_u.resok.data.data_val, data->private_data); + free_nfs_cb_data(data); +} + +int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, nfs_cb cb, void *private_data) +{ + struct nfs_cb_data *data; + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure"); + printf("failed to allocate memory for nfs cb data\n"); + free_nfs_cb_data(data); + return -1; + } + bzero(data, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + data->nfsfh = nfsfh; + + nfsfh->offset = offset; + if (rpc_nfs_read_async(nfs->rpc, nfs_pread_cb, &nfsfh->fh, offset, count, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send READ call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +/* + * Async read() + */ +int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, nfs_cb cb, void *private_data) +{ + return nfs_pread_async(nfs, nfsfh, nfsfh->offset, count, cb, private_data); +} + + + +/* + * Async pwrite() + */ +static void nfs_pwrite_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + WRITE3res *res; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: Write failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->nfsfh->offset += res->WRITE3res_u.resok.count; + data->cb(res->WRITE3res_u.resok.count, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf, nfs_cb cb, void *private_data) +{ + struct nfs_cb_data *data; + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure"); + printf("failed to allocate memory for nfs cb data\n"); + free_nfs_cb_data(data); + return -1; + } + bzero(data, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + data->nfsfh = nfsfh; + + nfsfh->offset = offset; + if (rpc_nfs_write_async(nfs->rpc, nfs_pwrite_cb, &nfsfh->fh, buf, offset, count, nfsfh->is_sync?FILE_SYNC:UNSTABLE, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send WRITE call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +/* + * Async write() + */ +int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf, nfs_cb cb, void *private_data) +{ + return nfs_pwrite_async(nfs, nfsfh, nfsfh->offset, count, buf, cb, private_data); +} + + + + +/* + * close + */ + +int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data) +{ + if (nfsfh->fh.data.data_val != NULL){ + free(nfsfh->fh.data.data_val); + nfsfh->fh.data.data_val = NULL; + } + free(nfsfh); + + cb(0, nfs, NULL, private_data); + return 0; +}; + + + + + +/* + * Async fstat() + */ +int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data) +{ + struct nfs_cb_data *data; + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure"); + printf("failed to allocate memory for nfs cb data\n"); + free_nfs_cb_data(data); + return -1; + } + bzero(data, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + + if (rpc_nfs_getattr_async(nfs->rpc, nfs_stat_1_cb, &nfsfh->fh, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send STAT GETATTR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + + + +/* + * Async fsync() + */ +static void nfs_fsync_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + COMMIT3res *res; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: Commit failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data) +{ + struct nfs_cb_data *data; + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure"); + printf("failed to allocate memory for nfs cb data\n"); + free_nfs_cb_data(data); + return -1; + } + bzero(data, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + + if (rpc_nfs_commit_async(nfs->rpc, nfs_fsync_cb, &nfsfh->fh, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send COMMIT call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + + + + +/* + * Async ftruncate() + */ +static void nfs_ftruncate_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + SETATTR3res *res; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: Setattr failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length, nfs_cb cb, void *private_data) +{ + struct nfs_cb_data *data; + SETATTR3args args; + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure"); + printf("failed to allocate memory for nfs cb data\n"); + free_nfs_cb_data(data); + return -1; + } + bzero(data, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + + bzero(&args, sizeof(SETATTR3args)); + args.object.data.data_len = nfsfh->fh.data.data_len; + args.object.data.data_val = nfsfh->fh.data.data_val; + args.new_attributes.size.set_it = 1; + args.new_attributes.size.set_size3_u.size = length; + + if (rpc_nfs_setattr_async(nfs->rpc, nfs_ftruncate_cb, &args, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + + +/* + * Async truncate() + */ +static int nfs_truncate_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + off_t offset = data->continue_int; + struct nfsfh nfsfh; + + nfsfh.fh.data.data_val = data->fh.data.data_val; + nfsfh.fh.data.data_len = data->fh.data.data_len; + + if (nfs_ftruncate_async(nfs, &nfsfh, offset, data->cb, data->private_data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + free_nfs_cb_data(data); + return 0; +} + +int nfs_truncate_async(struct nfs_context *nfs, const char *path, off_t length, nfs_cb cb, void *private_data) +{ + off_t offset; + + offset = length; + + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_truncate_continue_internal, NULL, NULL, offset) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -2; + } + + return 0; +} + + + + +/* + * Async mkdir() + */ +static void nfs_mkdir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + MKDIR3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + char *str = data->continue_data; + + str = &str[strlen(str) + 1]; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: MKDIR of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_mkdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + char *str = data->continue_data; + + str = &str[strlen(str) + 1]; + + if (rpc_nfs_mkdir_async(nfs->rpc, nfs_mkdir_cb, &data->fh, str, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send MKDIR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) +{ + char *new_path; + char *ptr; + + new_path = strdup(path); + if (new_path == NULL) { + printf("Out of memory, failed to allocate mode buffer for path\n"); + return -1; + } + + ptr = rindex(new_path, '/'); + if (ptr == NULL) { + printf("Invalid path %s\n", path); + return -2; + } + *ptr = 0; + + /* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */ + if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_mkdir_continue_internal, new_path, free, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -3; + } + + return 0; +} + + + + + +/* + * Async rmdir() + */ +static void nfs_rmdir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + RMDIR3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + char *str = data->continue_data; + + str = &str[strlen(str) + 1]; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: RMDIR of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_rmdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + char *str = data->continue_data; + + str = &str[strlen(str) + 1]; + + if (rpc_nfs_rmdir_async(nfs->rpc, nfs_rmdir_cb, &data->fh, str, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send RMDIR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) +{ + char *new_path; + char *ptr; + + new_path = strdup(path); + if (new_path == NULL) { + printf("Out of memory, failed to allocate mode buffer for path\n"); + return -1; + } + + ptr = rindex(new_path, '/'); + if (ptr == NULL) { + printf("Invalid path %s\n", path); + return -2; + } + *ptr = 0; + + /* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */ + if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_rmdir_continue_internal, new_path, free, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -3; + } + + return 0; +} + + + + +/* + * Async creat() + */ +static void nfs_create_2_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + LOOKUP3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + struct nfsfh *nfsfh; + char *str = data->continue_data; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + str = &str[strlen(str) + 1]; + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: CREATE of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + + return; + } + + nfsfh = malloc(sizeof(struct nfsfh)); + if (nfsfh == NULL) { + rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure"); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + bzero(nfsfh, sizeof(struct nfsfh)); + + /* steal the filehandle */ + nfsfh->fh.data.data_len = data->fh.data.data_len; + nfsfh->fh.data.data_val = data->fh.data.data_val; + data->fh.data.data_val = NULL; + + data->cb(0, nfs, nfsfh, data->private_data); + free_nfs_cb_data(data); +} + + + +static void nfs_creat_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + CREATE3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + char *str = data->continue_data; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + str = &str[strlen(str) + 1]; + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: CREATE of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + + return; + } + + if (rpc_nfs_lookup_async(nfs->rpc, nfs_create_2_cb, &data->fh, str, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send lookup call for %s/%s", data->saved_path, str); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + return; +} + +static int nfs_creat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + char *str = data->continue_data; + + str = &str[strlen(str) + 1]; + + if (rpc_nfs_create_async(nfs->rpc, nfs_creat_1_cb, &data->fh, str, data->continue_int, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send CREATE call for %s/%s", data->path, str); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) +{ + char *new_path; + char *ptr; + + new_path = strdup(path); + if (new_path == NULL) { + printf("Out of memory, failed to allocate mode buffer for path\n"); + return -1; + } + + ptr = rindex(new_path, '/'); + if (ptr == NULL) { + printf("Invalid path %s\n", path); + return -2; + } + *ptr = 0; + + /* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */ + if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_creat_continue_internal, new_path, free, mode) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -3; + } + + return 0; +} + + + + +/* + * Async unlink() + */ +static void nfs_unlink_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + REMOVE3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + char *str = data->continue_data; + + str = &str[strlen(str) + 1]; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: REMOVE of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_unlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + char *str = data->continue_data; + + str = &str[strlen(str) + 1]; + + if (rpc_nfs_remove_async(nfs->rpc, nfs_unlink_cb, &data->fh, str, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send REMOVE call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) +{ + char *new_path; + char *ptr; + + new_path = strdup(path); + if (new_path == NULL) { + printf("Out of memory, failed to allocate mode buffer for path\n"); + return -1; + } + + ptr = rindex(new_path, '/'); + if (ptr == NULL) { + printf("Invalid path %s\n", path); + return -2; + } + *ptr = 0; + + /* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */ + if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_unlink_continue_internal, new_path, free, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -3; + } + + return 0; +} + + + + + +/* + * Async opendir() + */ +static void nfs_opendir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + READDIR3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + struct nfsdir *nfsdir = data->continue_data;; + struct entry3 *entry; + uint64_t cookie; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + nfs_free_nfsdir(nfsdir); + data->continue_data = NULL; + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + nfs_free_nfsdir(nfsdir); + data->continue_data = NULL; + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: READDIR of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + nfs_free_nfsdir(nfsdir); + data->continue_data = NULL; + free_nfs_cb_data(data); + return; + } + + entry =res->READDIR3res_u.resok.reply.entries; + while (entry != NULL) { + struct nfsdirent *nfsdirent; + + nfsdirent = malloc(sizeof(struct nfsdirent)); + if (nfsdirent == NULL) { + data->cb(-ENOMEM, nfs, "Failed to allocate dirent", data->private_data); + nfs_free_nfsdir(nfsdir); + data->continue_data = NULL; + free_nfs_cb_data(data); + return; + } + bzero(nfsdirent, sizeof(struct nfsdirent)); + nfsdirent->name = strdup(entry->name); + if (nfsdirent->name == NULL) { + data->cb(-ENOMEM, nfs, "Failed to allocate dirent->name", data->private_data); + nfs_free_nfsdir(nfsdir); + data->continue_data = NULL; + free_nfs_cb_data(data); + return; + } + nfsdirent->inode = entry->fileid; + nfsdirent->next = nfsdir->entries; + nfsdir->entries = nfsdirent; + + cookie = entry->cookie; + entry = entry->nextentry; + } + + if (res->READDIR3res_u.resok.reply.eof == 0) { + if (rpc_nfs_readdir_async(nfs->rpc, nfs_opendir_cb, &data->fh, cookie, res->READDIR3res_u.resok.cookieverf, 20000, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + nfs_free_nfsdir(nfsdir); + data->continue_data = NULL; + free_nfs_cb_data(data); + return; + } + return; + } + + /* steal the dirhandle */ + data->continue_data = NULL; + nfsdir->current = nfsdir->entries; + + data->cb(0, nfs, nfsdir, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_opendir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + cookieverf3 cv; + + bzero(cv, sizeof(cookieverf3)); + if (rpc_nfs_readdir_async(nfs->rpc, nfs_opendir_cb, &data->fh, 0, (char *)&cv, 20000, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_opendir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) +{ + struct nfsdir *nfsdir; + + nfsdir = malloc(sizeof(struct nfsdir)); + if (nfsdir == NULL) { + printf("failed to allocate buffer for nfsdir\n"); + return -1; + } + bzero(nfsdir, sizeof(struct nfsdir)); + + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_opendir_continue_internal, nfsdir, free, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -2; + } + + return 0; +} + + +struct nfsdirent *nfs_readdir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir) +{ + struct nfsdirent *nfsdirent = nfsdir->current; + + if (nfsdir->current != NULL) { + nfsdir->current = nfsdir->current->next; + } + return nfsdirent; +} + + +void nfs_closedir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir) +{ + nfs_free_nfsdir(nfsdir); +} + + + + + + + +/* + * Async lseek() + */ +struct lseek_cb_data { + struct nfs_context *nfs; + struct nfsfh *nfsfh; + off_t offset; + nfs_cb cb; + void *private_data; +}; + +static void nfs_lseek_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + GETATTR3res *res; + struct lseek_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: GETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free(data); + return; + } + + data->nfsfh->offset = data->offset + res->GETATTR3res_u.resok.obj_attributes.size; + data->cb(0, nfs, &data->nfsfh->offset, data->private_data); + free(data); +} + +int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, nfs_cb cb, void *private_data) +{ + struct lseek_cb_data *data; + + if (whence == SEEK_SET) { + nfsfh->offset = offset; + cb(0, nfs, &nfsfh->offset, private_data); + return 0; + } + if (whence == SEEK_CUR) { + nfsfh->offset += offset; + cb(0, nfs, &nfsfh->offset, private_data); + return 0; + } + + data = malloc(sizeof(struct lseek_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "Out Of Memory: Failed to malloc lseek cb data"); + return -1; + } + + data->nfs = nfs; + data->nfsfh = nfsfh; + data->offset = offset; + data->cb = cb; + data->private_data = private_data; + + if (rpc_nfs_getattr_async(nfs->rpc, nfs_lseek_1_cb, &nfsfh->fh, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send LSEEK GETATTR call"); + free(data); + return -2; + } + return 0; +} + + + + +/* + * Async statvfs() + */ +static void nfs_statvfs_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + FSSTAT3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + struct statvfs svfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: FSSTAT of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + svfs.f_bsize = 4096; + svfs.f_frsize = 4096; + svfs.f_blocks = res->FSSTAT3res_u.resok.tbytes/4096; + svfs.f_bfree = res->FSSTAT3res_u.resok.fbytes/4096; + svfs.f_bavail = res->FSSTAT3res_u.resok.abytes/4096; + svfs.f_files = res->FSSTAT3res_u.resok.tfiles; + svfs.f_ffree = res->FSSTAT3res_u.resok.ffiles; + svfs.f_favail = res->FSSTAT3res_u.resok.afiles; + svfs.f_fsid = 0; + svfs.f_flag = 0; + svfs.f_namemax = 256; + + data->cb(0, nfs, &svfs, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_statvfs_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + if (rpc_nfs_fsstat_async(nfs->rpc, nfs_statvfs_1_cb, &data->fh, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send FSSTAT call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_statvfs_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) +{ + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_statvfs_continue_internal, NULL, NULL, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -1; + } + + return 0; +} + + + + +/* + * Async readlink() + */ +static void nfs_readlink_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + READLINK3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: READLINK of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + + data->cb(0, nfs, res->READLINK3res_u.resok.data, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_readlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + if (rpc_nfs_readlink_async(nfs->rpc, nfs_readlink_1_cb, &data->fh, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send READLINK call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data) +{ + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_readlink_continue_internal, NULL, NULL, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -1; + } + + return 0; +} + + + + +/* + * Async chmod() + */ +static void nfs_chmod_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + SETATTR3res *res; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: SETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_chmod_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + SETATTR3args args; + + bzero(&args, sizeof(SETATTR3args)); + args.object.data.data_len = data->fh.data.data_len; + args.object.data.data_val = data->fh.data.data_val; + args.new_attributes.mode.set_it = 1; + args.new_attributes.mode.set_mode3_u.mode = data->continue_int; + + if (rpc_nfs_setattr_async(nfs->rpc, nfs_chmod_cb, &args, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + + +int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) +{ + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_chmod_continue_internal, NULL, NULL, mode) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -1; + } + + return 0; +} + +/* + * Async fchmod() + */ +int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs_cb cb, void *private_data) +{ + struct nfs_cb_data *data; + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory"); + printf("failed to allocate memory for nfs mount data\n"); + return -1; + } + bzero(data, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + data->continue_int = mode; + data->fh.data.data_len = nfsfh->fh.data.data_len; + data->fh.data.data_val = malloc(data->fh.data.data_len); + if (data->fh.data.data_val == NULL) { + rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh"); + free_nfs_cb_data(data); + return -1; + } + memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len); + + if (nfs_chmod_continue_internal(nfs, data) != 0) { + free_nfs_cb_data(data); + return -1; + } + + return 0; +} + + + +/* + * Async chown() + */ +static void nfs_chown_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + SETATTR3res *res; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: SETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +struct nfs_chown_data { + uid_t uid; + gid_t gid; +}; + +static int nfs_chown_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + SETATTR3args args; + struct nfs_chown_data *chown_data = data->continue_data; + + bzero(&args, sizeof(SETATTR3args)); + args.object.data.data_len = data->fh.data.data_len; + args.object.data.data_val = data->fh.data.data_val; + if (chown_data->uid != (uid_t)-1) { + args.new_attributes.uid.set_it = 1; + args.new_attributes.uid.set_uid3_u.uid = chown_data->uid; + } + if (chown_data->gid != (gid_t)-1) { + args.new_attributes.gid.set_it = 1; + args.new_attributes.gid.set_gid3_u.gid = chown_data->gid; + } + + if (rpc_nfs_setattr_async(nfs->rpc, nfs_chown_cb, &args, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + + +int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data) +{ + struct nfs_chown_data *chown_data; + + chown_data = malloc(sizeof(struct nfs_chown_data)); + if (chown_data == NULL) { + printf("Failed to allocate memory for chown data structure\n"); + return -1; + } + + chown_data->uid = uid; + chown_data->gid = gid; + + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_chown_continue_internal, chown_data, free, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -1; + } + + return 0; +} + + +/* + * Async fchown() + */ +int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid, nfs_cb cb, void *private_data) +{ + struct nfs_cb_data *data; + struct nfs_chown_data *chown_data; + + chown_data = malloc(sizeof(struct nfs_chown_data)); + if (chown_data == NULL) { + printf("Failed to allocate memory for chown data structure\n"); + return -1; + } + + chown_data->uid = uid; + chown_data->gid = gid; + + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory"); + printf("failed to allocate memory for fchown data\n"); + return -1; + } + bzero(data, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + data->continue_data = chown_data; + data->fh.data.data_len = nfsfh->fh.data.data_len; + data->fh.data.data_val = malloc(data->fh.data.data_len); + if (data->fh.data.data_val == NULL) { + rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh"); + free_nfs_cb_data(data); + return -1; + } + memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len); + + + if (nfs_chown_continue_internal(nfs, data) != 0) { + free_nfs_cb_data(data); + return -1; + } + + return 0; +} + + + + + +/* + * Async utimes() + */ +static void nfs_utimes_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + SETATTR3res *res; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: SETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_utimes_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + SETATTR3args args; + struct timeval *utimes_data = data->continue_data; + + bzero(&args, sizeof(SETATTR3args)); + args.object.data.data_len = data->fh.data.data_len; + args.object.data.data_val = data->fh.data.data_val; + if (utimes_data != NULL) { + args.new_attributes.atime.set_it = SET_TO_CLIENT_TIME; + args.new_attributes.atime.set_atime_u.atime.seconds = utimes_data[0].tv_sec; + args.new_attributes.atime.set_atime_u.atime.nseconds = utimes_data[0].tv_usec * 1000; + args.new_attributes.mtime.set_it = SET_TO_CLIENT_TIME; + args.new_attributes.mtime.set_mtime_u.mtime.seconds = utimes_data[1].tv_sec; + args.new_attributes.mtime.set_mtime_u.mtime.nseconds = utimes_data[1].tv_usec * 1000; + } else { + args.new_attributes.atime.set_it = SET_TO_SERVER_TIME; + args.new_attributes.mtime.set_it = SET_TO_SERVER_TIME; + } + + if (rpc_nfs_setattr_async(nfs->rpc, nfs_utimes_cb, &args, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + + +int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data) +{ + struct timeval *new_times = NULL; + + if (times != NULL) { + new_times = malloc(sizeof(struct timeval)*2); + if (new_times == NULL) { + printf("Failed to allocate memory for timeval structure\n"); + return -1; + } + + memcpy(new_times, times, sizeof(struct timeval)*2); + } + + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_utimes_continue_internal, new_times, free, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -1; + } + + return 0; +} + +/* + * Async utime() + */ +int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *times, nfs_cb cb, void *private_data) +{ + struct timeval *new_times = NULL; + + if (times != NULL) { + new_times = malloc(sizeof(struct timeval)*2); + if (new_times == NULL) { + printf("Failed to allocate memory for timeval structure\n"); + return -1; + } + + new_times[0].tv_sec = times->actime; + new_times[0].tv_usec = 0; + new_times[1].tv_sec = times->modtime; + new_times[1].tv_usec = 0; + } + + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_utimes_continue_internal, new_times, free, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -1; + } + + return 0; +} + + + + + +/* + * Async access() + */ +static void nfs_access_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + ACCESS3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + unsigned int nfsmode = 0; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: ACCESS of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + if (data->continue_int & R_OK) { + nfsmode |= ACCESS3_READ; + } + if (data->continue_int & W_OK) { + nfsmode |= ACCESS3_MODIFY; + } + if (data->continue_int & X_OK) { + nfsmode |= ACCESS3_EXECUTE; + } + + if (res->ACCESS3res_u.resok.access != nfsmode) { + rpc_set_error(nfs->rpc, "NFS: ACCESS denied. Required access %c%c%c. Allowed access %c%c%c", + nfsmode&ACCESS3_READ?'r':'-', + nfsmode&ACCESS3_MODIFY?'w':'-', + nfsmode&ACCESS3_EXECUTE?'x':'-', + res->ACCESS3res_u.resok.access&ACCESS3_READ?'r':'-', + res->ACCESS3res_u.resok.access&ACCESS3_MODIFY?'w':'-', + res->ACCESS3res_u.resok.access&ACCESS3_EXECUTE?'x':'-'); + data->cb(-EACCES, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_access_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + int nfsmode = 0; + + if (data->continue_int & R_OK) { + nfsmode |= ACCESS3_READ; + } + if (data->continue_int & W_OK) { + nfsmode |= ACCESS3_MODIFY; + } + if (data->continue_int & X_OK) { + nfsmode |= ACCESS3_EXECUTE; + } + + if (rpc_nfs_access_async(nfs->rpc, nfs_access_cb, &data->fh, nfsmode, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send OPEN ACCESS call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_access_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) +{ + if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_access_continue_internal, NULL, NULL, mode) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -2; + } + + return 0; +} + + + +/* + * Async symlink() + */ +struct nfs_symlink_data { + char *oldpath; + char *newpathparent; + char *newpathobject; +}; + +static void free_nfs_symlink_data(void *mem) +{ + struct nfs_symlink_data *data = mem; + + if (data->oldpath != NULL) { + free(data->oldpath); + } + if (data->newpathparent != NULL) { + free(data->newpathparent); + } + if (data->newpathobject != NULL) { + free(data->newpathobject); + } + free(data); +} + +static void nfs_symlink_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + SYMLINK3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + struct nfs_symlink_data *symlink_data = data->continue_data; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: SYMLINK %s/%s -> %s failed with %s(%d)", symlink_data->newpathparent, symlink_data->newpathobject, symlink_data->oldpath, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_symlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + struct nfs_symlink_data *symlink_data = data->continue_data; + + if (rpc_nfs_symlink_async(nfs->rpc, nfs_symlink_cb, &data->fh, symlink_data->newpathobject, symlink_data->oldpath, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send SYMLINK call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + +int nfs_symlink_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data) +{ + char *ptr; + struct nfs_symlink_data *symlink_data; + + symlink_data = malloc(sizeof(struct nfs_symlink_data)); + if (symlink_data == NULL) { + printf("Out of memory, failed to allocate buffer for symlink data\n"); + return -1; + } + bzero(symlink_data, sizeof(struct nfs_symlink_data)); + + symlink_data->oldpath = strdup(oldpath); + if (symlink_data->oldpath == NULL) { + printf("Out of memory, failed to allocate buffer for oldpath\n"); + free_nfs_symlink_data(symlink_data); + return -2; + } + + symlink_data->newpathparent = strdup(newpath); + if (symlink_data->newpathparent == NULL) { + printf("Out of memory, failed to allocate mode buffer for new path\n"); + free_nfs_symlink_data(symlink_data); + return -3; + } + + ptr = rindex(symlink_data->newpathparent, '/'); + if (ptr == NULL) { + printf("Invalid path %s\n", oldpath); + free_nfs_symlink_data(symlink_data); + return -4; + } + *ptr = 0; + ptr++; + + symlink_data->newpathobject = strdup(ptr); + if (symlink_data->newpathobject == NULL) { + printf("Out of memory, failed to allocate mode buffer for new path\n"); + free_nfs_symlink_data(symlink_data); + return -5; + } + + if (nfs_lookuppath_async(nfs, symlink_data->newpathparent, cb, private_data, nfs_symlink_continue_internal, symlink_data, free_nfs_symlink_data, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -6; + } + + return 0; +} + + + +/* + * Async rename() + */ +struct nfs_rename_data { + char *oldpath; + char *oldobject; + struct nfs_fh3 olddir; + char *newpath; + char *newobject; + struct nfs_fh3 newdir; +}; + +static void free_nfs_rename_data(void *mem) +{ + struct nfs_rename_data *data = mem; + + if (data->oldpath != NULL) { + free(data->oldpath); + } + if (data->olddir.data.data_val != NULL) { + free(data->olddir.data.data_val); + } + if (data->newpath != NULL) { + free(data->newpath); + } + if (data->newdir.data.data_val != NULL) { + free(data->newdir.data.data_val); + } + free(data); +} + +static void nfs_rename_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + RENAME3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + struct nfs_rename_data *rename_data = data->continue_data; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: RENAME %s/%s -> %s/%s failed with %s(%d)", rename_data->oldpath, rename_data->oldobject, rename_data->newpath, rename_data->newobject, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_rename_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + struct nfs_rename_data *rename_data = data->continue_data; + + /* steal the filehandle */ + rename_data->newdir.data.data_len = data->fh.data.data_len; + rename_data->newdir.data.data_val = data->fh.data.data_val; + data->fh.data.data_val = NULL; + + if (rpc_nfs_rename_async(nfs->rpc, nfs_rename_cb, &rename_data->olddir, rename_data->oldobject, &rename_data->newdir, rename_data->newobject, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send RENAME call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + + +static int nfs_rename_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + struct nfs_rename_data *rename_data = data->continue_data; + + /* steal the filehandle */ + rename_data->olddir.data.data_len = data->fh.data.data_len; + rename_data->olddir.data.data_val = data->fh.data.data_val; + data->fh.data.data_val = NULL; + + if (nfs_lookuppath_async(nfs, rename_data->newpath, data->cb, data->private_data, nfs_rename_continue_2_internal, rename_data, free_nfs_rename_data, 0) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", rename_data->newpath); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + data->continue_data = NULL; + free_nfs_cb_data(data); + + return 0; +} + + +int nfs_rename_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data) +{ + char *ptr; + struct nfs_rename_data *rename_data; + + rename_data = malloc(sizeof(struct nfs_rename_data)); + if (rename_data == NULL) { + printf("Out of memory, failed to allocate buffer for rename data\n"); + return -1; + } + bzero(rename_data, sizeof(struct nfs_rename_data)); + + rename_data->oldpath = strdup(oldpath); + if (rename_data->oldpath == NULL) { + printf("Out of memory, failed to allocate buffer for oldpath\n"); + free_nfs_rename_data(rename_data); + return -2; + } + ptr = rindex(rename_data->oldpath, '/'); + if (ptr == NULL) { + printf("Invalid path %s\n", oldpath); + free_nfs_rename_data(rename_data); + return -3; + } + *ptr = 0; + ptr++; + rename_data->oldobject = ptr; + + + rename_data->newpath = strdup(newpath); + if (rename_data->newpath == NULL) { + printf("Out of memory, failed to allocate buffer for newpath\n"); + free_nfs_rename_data(rename_data); + return -4; + } + ptr = rindex(rename_data->newpath, '/'); + if (ptr == NULL) { + printf("Invalid path %s\n", newpath); + free_nfs_rename_data(rename_data); + return -5; + } + *ptr = 0; + ptr++; + rename_data->newobject = ptr; + + + if (nfs_lookuppath_async(nfs, rename_data->oldpath, cb, private_data, nfs_rename_continue_1_internal, rename_data, free_nfs_rename_data, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -6; + } + + return 0; +} + + +/* + * Async link() + */ +struct nfs_link_data { + char *oldpath; + struct nfs_fh3 oldfh; + char *newpath; + char *newobject; + struct nfs_fh3 newdir; +}; + +static void free_nfs_link_data(void *mem) +{ + struct nfs_link_data *data = mem; + + if (data->oldpath != NULL) { + free(data->oldpath); + } + if (data->oldfh.data.data_val != NULL) { + free(data->oldfh.data.data_val); + } + if (data->newpath != NULL) { + free(data->newpath); + } + if (data->newdir.data.data_val != NULL) { + free(data->newdir.data.data_val); + } + free(data); +} + +static void nfs_link_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data) +{ + LINK3res *res; + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + struct nfs_link_data *link_data = data->continue_data; + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: LINK %s -> %s/%s failed with %s(%d)", link_data->oldpath, link_data->newpath, link_data->newobject, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs_cb_data(data); +} + +static int nfs_link_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + struct nfs_link_data *link_data = data->continue_data; + + /* steal the filehandle */ + link_data->newdir.data.data_len = data->fh.data.data_len; + link_data->newdir.data.data_val = data->fh.data.data_val; + data->fh.data.data_val = NULL; + + if (rpc_nfs_link_async(nfs->rpc, nfs_link_cb, &link_data->oldfh, &link_data->newdir, link_data->newobject, data) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send LINK call for %s", data->path); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + return 0; +} + + +static int nfs_link_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data) +{ + struct nfs_link_data *link_data = data->continue_data; + + /* steal the filehandle */ + link_data->oldfh.data.data_len = data->fh.data.data_len; + link_data->oldfh.data.data_val = data->fh.data.data_val; + data->fh.data.data_val = NULL; + + if (nfs_lookuppath_async(nfs, link_data->newpath, data->cb, data->private_data, nfs_link_continue_2_internal, link_data, free_nfs_link_data, 0) != 0) { + rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", link_data->newpath); + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return -1; + } + data->continue_data = NULL; + free_nfs_cb_data(data); + + return 0; +} + + +int nfs_link_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data) +{ + char *ptr; + struct nfs_link_data *link_data; + + link_data = malloc(sizeof(struct nfs_link_data)); + if (link_data == NULL) { + printf("Out of memory, failed to allocate buffer for link data\n"); + return -1; + } + bzero(link_data, sizeof(struct nfs_link_data)); + + link_data->oldpath = strdup(oldpath); + if (link_data->oldpath == NULL) { + printf("Out of memory, failed to allocate buffer for oldpath\n"); + free_nfs_link_data(link_data); + return -2; + } + + link_data->newpath = strdup(newpath); + if (link_data->newpath == NULL) { + printf("Out of memory, failed to allocate buffer for newpath\n"); + free_nfs_link_data(link_data); + return -4; + } + ptr = rindex(link_data->newpath, '/'); + if (ptr == NULL) { + printf("Invalid path %s\n", newpath); + free_nfs_link_data(link_data); + return -5; + } + *ptr = 0; + ptr++; + link_data->newobject = ptr; + + + if (nfs_lookuppath_async(nfs, link_data->oldpath, cb, private_data, nfs_link_continue_1_internal, link_data, free_nfs_link_data, 0) != 0) { + printf("Out of memory: failed to start parsing the path components\n"); + return -6; + } + + return 0; +} + + +//qqq replace later with lseek() +off_t nfs_get_current_offset(struct nfsfh *nfsfh) +{ + return nfsfh->offset; +} + diff --git a/ccan/nfs/libnfs.h b/ccan/nfs/libnfs.h new file mode 100644 index 00000000..ec4b6053 --- /dev/null +++ b/ccan/nfs/libnfs.h @@ -0,0 +1,910 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ +/* + * This is the highlevel interface to access NFS resources using a posix-like interface + */ +#include + +struct nfs_context; + +/* + * Used for interfacing the async version of the api into an external eventsystem + */ +int nfs_get_fd(struct nfs_context *nfs); +int nfs_which_events(struct nfs_context *nfs); +int nfs_service(struct nfs_context *nfs, int revents); + +/* + * Used if you need different credentials than the default for the current user. + */ +struct AUTH; +void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth); + + +/* + * When an operation failed, this function can extract a detailed error string. + */ +char *nfs_get_error(struct nfs_context *nfs); + + +/* + * Callback for all async nfs functions + */ +typedef void (*nfs_cb)(int err, struct nfs_context *nfs, void *data, void *private_data); + + + + +/* + * NFS CONTEXT. + */ +/* + * Create an NFS context. + * Function returns + * NULL : Failed to create a context. + * struct nfs_context * : A pointer to an nfs context. + */ +struct nfs_context *nfs_init_context(void); +/* + * Destroy an nfs context. + */ +void nfs_destroy_context(struct nfs_context *nfs); + + +/* + * A libnfs filehandle + */ +struct nfsfh; + + + +/* + * MOUNT THE EXPORT + */ +/* + * Async nfs mount. + * This function will try to connect to the server and mount the export. + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *export, nfs_cb cb, void *private_data); +/* + * Sync nfs mount. + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_mount_sync(struct nfs_context *nfs, const char *server, const char *export); + + + + +/* + * STAT() + */ +/* + * Async stat() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is struct stat * + * -errno : An error occured. + * data is the error string. + */ +struct stat; +int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data); +/* + * Sync stat() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_stat_sync(struct nfs_context *nfs, const char *path, struct stat *st); + + +/* + * FSTAT() + */ +/* + * Async fstat(nfsfh *) + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is struct stat * + * -errno : An error occured. + * data is the error string. + */ +int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data); +/* + * Sync fstat(nfsfh *) + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_fstat_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st); + + + +/* + * OPEN() + */ +/* + * Async open() + * + * mode is a combination of the flags : O_RDOLNY, O_WRONLY, O_RDWR , O_SYNC + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is a struct *nfsfh; + * The nfsfh is close using nfs_close(). + * -errno : An error occured. + * data is the error string. + */ +int nfs_open_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data); +/* + * Sync open() + * Function returns + * 0 : The operation was successfull. *nfsfh is filled in. + * -errno : The command failed. + */ +int nfs_open_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh); + + + + +/* + * CLOSE + */ +/* + * Async close(nfsfh) + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL. + * -errno : An error occured. + * data is the error string. + */ +int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data); +/* + * Sync close(nfsfh) + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_close_sync(struct nfs_context *nfs, struct nfsfh *nfsfh); + + +/* + * PREAD() + */ +/* + * Async pread() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * >=0 : Success. + * status is numer of bytes read. + * data is a pointer to the returned data. + * -errno : An error occured. + * data is the error string. + */ +int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, nfs_cb cb, void *private_data); +/* + * Sync pread() + * Function returns + * >=0 : numer of bytes read. + * -errno : An error occured. + */ +int nfs_pread_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf); + + + +/* + * READ() + */ +/* + * Async read() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * >=0 : Success. + * status is numer of bytes read. + * data is a pointer to the returned data. + * -errno : An error occured. + * data is the error string. + */ +int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, nfs_cb cb, void *private_data); +/* + * Sync read() + * Function returns + * >=0 : numer of bytes read. + * -errno : An error occured. + */ +int nfs_read_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf); + + + + +/* + * PWRITE() + */ +/* + * Async pwrite() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * >=0 : Success. + * status is numer of bytes written. + * -errno : An error occured. + * data is the error string. + */ +int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf, nfs_cb cb, void *private_data); +/* + * Sync pwrite() + * Function returns + * >=0 : numer of bytes written. + * -errno : An error occured. + */ +int nfs_pwrite_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf); + + +/* + * WRITE() + */ +/* + * Async write() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * >=0 : Success. + * status is numer of bytes written. + * -errno : An error occured. + * data is the error string. + */ +int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf, nfs_cb cb, void *private_data); +/* + * Sync write() + * Function returns + * >=0 : numer of bytes written. + * -errno : An error occured. + */ +int nfs_write_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf); + + +/* + * LSEEK() + */ +/* + * Async lseek() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * >=0 : Success. + * data is off_t * for the current position. + * -errno : An error occured. + * data is the error string. + */ +int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, nfs_cb cb, void *private_data); +/* + * Sync lseek() + * Function returns + * >=0 : numer of bytes read. + * -errno : An error occured. + */ +int nfs_lseek_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, off_t *current_offset); + + +/* + * FSYNC() + */ +/* + * Async fsync() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * -errno : An error occured. + * data is the error string. + */ +int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data); +/* + * Sync fsync() + * Function returns + * 0 : Success + * -errno : An error occured. + */ +int nfs_fsync_sync(struct nfs_context *nfs, struct nfsfh *nfsfh); + + + +/* + * TRUNCATE() + */ +/* + * Async truncate() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * -errno : An error occured. + * data is the error string. + */ +int nfs_truncate_async(struct nfs_context *nfs, const char *path, off_t length, nfs_cb cb, void *private_data); +/* + * Sync truncate() + * Function returns + * 0 : Success + * -errno : An error occured. + */ +int nfs_truncate_sync(struct nfs_context *nfs, const char *path, off_t length); + + + +/* + * FTRUNCATE() + */ +/* + * Async ftruncate() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * -errno : An error occured. + * data is the error string. + */ +int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length, nfs_cb cb, void *private_data); +/* + * Sync ftruncate() + * Function returns + * 0 : Success + * -errno : An error occured. + */ +int nfs_ftruncate_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length); + + + + + + +/* + * MKDIR() + */ +/* + * Async mkdir() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * -errno : An error occured. + * data is the error string. + */ +int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data); +/* + * Sync mkdir() + * Function returns + * 0 : Success + * -errno : An error occured. + */ +int nfs_mkdir_sync(struct nfs_context *nfs, const char *path); + + + +/* + * RMDIR() + */ +/* + * Async rmdir() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * -errno : An error occured. + * data is the error string. + */ +int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data); +/* + * Sync rmdir() + * Function returns + * 0 : Success + * -errno : An error occured. + */ +int nfs_rmdir_sync(struct nfs_context *nfs, const char *path); + + + + +/* + * CREAT() + */ +/* + * Async creat() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is a struct *nfsfh; + * -errno : An error occured. + * data is the error string. + */ +int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data); +/* + * Sync creat() + * Function returns + * 0 : Success + * -errno : An error occured. + */ +int nfs_creat_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh); + + + + + +/* + * UNLINK() + */ +/* + * Async unlink() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data); +/* + * Sync unlink() + * Function returns + * 0 : Success + * -errno : An error occured. + */ +int nfs_unlink_sync(struct nfs_context *nfs, const char *path); + + + + +/* + * OPENDIR() + */ +struct nfsdir; +/* + * Async opendir() + * + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When struct nfsdir * is returned, this resource is closed/freed by calling nfs_closedir() + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is struct nfsdir * + * -errno : An error occured. + * data is the error string. + */ +int nfs_opendir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data); +/* + * Sync opendir() + * Function returns + * 0 : Success + * -errno : An error occured. + */ +int nfs_opendir_sync(struct nfs_context *nfs, const char *path, struct nfsdir **nfsdir); + + + +/* + * READDIR() + */ +struct nfsdirent { + struct nfsdirent *next; + char *name; + uint64_t inode; +}; +/* + * nfs_readdir() never blocks, so no special sync/async versions are available + */ +struct nfsdirent *nfs_readdir(struct nfs_context *nfs, struct nfsdir *nfsdir); + + + +/* + * READDIR() + */ +/* + * nfs_closedir() never blocks, so no special sync/async versions are available + */ +void nfs_closedir(struct nfs_context *nfs, struct nfsdir *nfsdir); + + + +/* + * STATVFS() + */ +/* + * Async statvfs() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is struct statvfs * + * -errno : An error occured. + * data is the error string. + */ +struct statvfs; +int nfs_statvfs_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data); +/* + * Sync statvfs() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_statvfs_sync(struct nfs_context *nfs, const char *path, struct statvfs *svfs); + + +/* + * READLINK() + */ +/* + * Async readlink() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is a char * + * data is only valid during the callback and is automatically freed when the callback returns. + * -errno : An error occured. + * data is the error string. + */ +struct statvfs; +int nfs_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data); +/* + * Sync readlink() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_readlink_sync(struct nfs_context *nfs, const char *path, char *buf, int bufsize); + + + +/* + * CHMOD() + */ +/* + * Async chmod() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data); +/* + * Sync chmod() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_chmod_sync(struct nfs_context *nfs, const char *path, int mode); + + + +/* + * FCHMOD() + */ +/* + * Async fchmod() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs_cb cb, void *private_data); +/* + * Sync fchmod() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_fchmod_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode); + + + +/* + * CHOWN() + */ +/* + * Async chown() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data); +/* + * Sync chown() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_chown_sync(struct nfs_context *nfs, const char *path, int uid, int gid); + + + +/* + * FCHOWN() + */ +/* + * Async fchown() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid, nfs_cb cb, void *private_data); +/* + * Sync fchown() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_fchown_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid); + + + + +/* + * UTIMES() + */ +/* + * Async utimes() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data); +/* + * Sync utimes() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_utimes_sync(struct nfs_context *nfs, const char *path, struct timeval *times); + + +/* + * UTIME() + */ +/* + * Async utime() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +struct utimbuf; +int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *times, nfs_cb cb, void *private_data); +/* + * Sync utime() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_utime_sync(struct nfs_context *nfs, const char *path, struct utimbuf *times); + + + + +/* + * ACCESS() + */ +/* + * Async access() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_access_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data); +/* + * Sync access() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_access_sync(struct nfs_context *nfs, const char *path, int mode); + + + + +/* + * SYMLINK() + */ +/* + * Async symlink() + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_symlink_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data); +/* + * Sync symlink() + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_symlink_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath); + + +/* + * RENAME() + */ +/* + * Async rename(, ) + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_rename_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data); +/* + * Sync rename(, ) + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_rename_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath); + + + +/* + * LINK() + */ +/* + * Async link(, ) + * Function returns + * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked. + * <0 : An error occured when trying to set up the operation. The callback will not be invoked. + * + * When the callback is invoked, status indicates the result: + * 0 : Success. + * data is NULL + * -errno : An error occured. + * data is the error string. + */ +int nfs_link_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data); +/* + * Sync link(, ) + * Function returns + * 0 : The operation was successfull. + * -errno : The command failed. + */ +int nfs_link_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath); + + + +//qqq replace later with lseek(cur, 0) +off_t nfs_get_current_offset(struct nfsfh *nfsfh); diff --git a/ccan/nfs/mount.c b/ccan/nfs/mount.c new file mode 100644 index 00000000..212eaedc --- /dev/null +++ b/ccan/nfs/mount.c @@ -0,0 +1,191 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-private.h" +#include "libnfs-raw-mount.h" + + +int rpc_mount_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for mount/null call"); + return -1; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for mount/null call"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} + +int rpc_mount_mnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_MNT, cb, private_data, (xdrproc_t)xdr_mountres3, sizeof(mountres3)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for mount/mnt call"); + return -1; + } + + if (xdr_dirpath(&pdu->xdr, &export) == 0) { + rpc_set_error(rpc, "XDR error. Failed to encode mount/mnt call"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for mount/mnt call"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} + +int rpc_mount_dump_async(struct rpc_context *rpc, rpc_cb cb, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_DUMP, cb, private_data, (xdrproc_t)xdr_mountlist, sizeof(mountlist)); + if (pdu == NULL) { + printf("Failed to allocate pdu for mount/dump\n"); + return -1; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + printf("Failed to queue mount/dump pdu\n"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} + +int rpc_mount_umnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_UMNT, cb, private_data, (xdrproc_t)xdr_void, 0); + if (pdu == NULL) { + printf("Failed to allocate pdu for mount/umnt\n"); + return -1; + } + + if (xdr_dirpath(&pdu->xdr, &export) == 0) { + printf("failed to encode dirpath for mount/umnt\n"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + printf("Failed to queue mount/umnt pdu\n"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} + +int rpc_mount_umntall_async(struct rpc_context *rpc, rpc_cb cb, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_UMNTALL, cb, private_data, (xdrproc_t)xdr_void, 0); + if (pdu == NULL) { + printf("Failed to allocate pdu for mount/umntall\n"); + return -1; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + printf("Failed to queue mount/umntall pdu\n"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} + +int rpc_mount_export_async(struct rpc_context *rpc, rpc_cb cb, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_EXPORT, cb, private_data, (xdrproc_t)xdr_exports, sizeof(exports)); + if (pdu == NULL) { + printf("Failed to allocate pdu for mount/export\n"); + return -1; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + printf("Failed to queue mount/export pdu\n"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} + +char *mountstat3_to_str(int st) +{ + enum mountstat3 stat = st; + + char *str = "unknown mount stat"; + switch (stat) { + case MNT3_OK: str="MNT3_OK"; break; + case MNT3ERR_PERM: str="MNT3ERR_PERM"; break; + case MNT3ERR_NOENT: str="MNT3ERR_NOENT"; break; + case MNT3ERR_IO: str="MNT3ERR_IO"; break; + case MNT3ERR_ACCES: str="MNT3ERR_ACCES"; break; + case MNT3ERR_NOTDIR: str="MNT3ERR_NOTDIR"; break; + case MNT3ERR_INVAL: str="MNT3ERR_INVAL"; break; + case MNT3ERR_NAMETOOLONG: str="MNT3ERR_NAMETOOLONG"; break; + case MNT3ERR_NOTSUPP: str="MNT3ERR_NOTSUPP"; break; + case MNT3ERR_SERVERFAULT: str="MNT3ERR_SERVERFAULT"; break; + } + return str; +} + + +int mountstat3_to_errno(int st) +{ + enum mountstat3 stat = st; + + switch (stat) { + case MNT3_OK: return 0; break; + case MNT3ERR_PERM: return -EPERM; break; + case MNT3ERR_NOENT: return -EPERM; break; + case MNT3ERR_IO: return -EIO; break; + case MNT3ERR_ACCES: return -EACCES; break; + case MNT3ERR_NOTDIR: return -ENOTDIR; break; + case MNT3ERR_INVAL: return -EINVAL; break; + case MNT3ERR_NAMETOOLONG: return -E2BIG; break; + case MNT3ERR_NOTSUPP: return -EINVAL; break; + case MNT3ERR_SERVERFAULT: return -EIO; break; + } + return -ERANGE; +} diff --git a/ccan/nfs/mount.x b/ccan/nfs/mount.x new file mode 100644 index 00000000..6c2f0713 --- /dev/null +++ b/ccan/nfs/mount.x @@ -0,0 +1,71 @@ +/* copied from RFC1813 */ + +const MNTPATHLEN = 1024; /* Maximum bytes in a path name */ +const MNTNAMLEN = 255; /* Maximum bytes in a name */ +const FHSIZE3 = 64; /* Maximum bytes in a V3 file handle */ + + +typedef opaque fhandle3; +typedef string dirpath; +typedef string name; + +enum mountstat3 { + MNT3_OK = 0, /* no error */ + MNT3ERR_PERM = 1, /* Not owner */ + MNT3ERR_NOENT = 2, /* No such file or directory */ + MNT3ERR_IO = 5, /* I/O error */ + MNT3ERR_ACCES = 13, /* Permission denied */ + MNT3ERR_NOTDIR = 20, /* Not a directory */ + MNT3ERR_INVAL = 22, /* Invalid argument */ + MNT3ERR_NAMETOOLONG = 63, /* Filename too long */ + MNT3ERR_NOTSUPP = 10004, /* Operation not supported */ + MNT3ERR_SERVERFAULT = 10006 /* A failure on the server */ +}; + + +typedef struct mountbody *mountlist; + +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; + +typedef struct groupnode *groups; + +struct groupnode { + name gr_name; + groups gr_next; +}; + + +typedef struct exportnode *exports; + +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; + +struct mountres3_ok { + fhandle3 fhandle; + int auth_flavors<>; +}; + +union mountres3 switch (mountstat3 fhs_status) { + case MNT3_OK: + mountres3_ok mountinfo; + default: + void; +}; + +program MOUNT_PROGRAM { + version MOUNT_V3 { + void MOUNT3_NULL(void) = 0; + mountres3 MOUNT3_MNT(dirpath) = 1; + mountlist MOUNT3_DUMP(void) = 2; + void MOUNT3_UMNT(dirpath) = 3; + void MOUNT3_UMNTALL(void) = 4; + exports MOUNT3_EXPORT(void) = 5; + } = 3; +} = 100005; diff --git a/ccan/nfs/nfs.c b/ccan/nfs/nfs.c new file mode 100644 index 00000000..6d3e82f9 --- /dev/null +++ b/ccan/nfs/nfs.c @@ -0,0 +1,680 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-private.h" +#include "libnfs-raw-nfs.h" + + + +char *nfsstat3_to_str(int error) +{ + switch (error) { + case NFS3_OK: return "NFS3_OK"; break; + case NFS3ERR_PERM: return "NFS3ERR_PERM"; break; + case NFS3ERR_NOENT: return "NFS3ERR_NOENT"; break; + case NFS3ERR_IO: return "NFS3ERR_IO"; break; + case NFS3ERR_NXIO: return "NFS3ERR_NXIO"; break; + case NFS3ERR_ACCES: return "NFS3ERR_ACCES"; break; + case NFS3ERR_EXIST: return "NFS3ERR_EXIST"; break; + case NFS3ERR_XDEV: return "NFS3ERR_XDEV"; break; + case NFS3ERR_NODEV: return "NFS3ERR_NODEV"; break; + case NFS3ERR_NOTDIR: return "NFS3ERR_NOTDIR"; break; + case NFS3ERR_ISDIR: return "NFS3ERR_ISDIR"; break; + case NFS3ERR_INVAL: return "NFS3ERR_INVAL"; break; + case NFS3ERR_FBIG: return "NFS3ERR_FBIG"; break; + case NFS3ERR_NOSPC: return "NFS3ERR_NOSPC"; break; + case NFS3ERR_ROFS: return "NFS3ERR_ROFS"; break; + case NFS3ERR_MLINK: return "NFS3ERR_MLINK"; break; + case NFS3ERR_NAMETOOLONG: return "NFS3ERR_NAMETOOLONG"; break; + case NFS3ERR_NOTEMPTY: return "NFS3ERR_NOTEMPTY"; break; + case NFS3ERR_DQUOT: return "NFS3ERR_DQUOT"; break; + case NFS3ERR_STALE: return "NFS3ERR_STALE"; break; + case NFS3ERR_REMOTE: return "NFS3ERR_REMOTE"; break; + case NFS3ERR_BADHANDLE: return "NFS3ERR_BADHANDLE"; break; + case NFS3ERR_NOT_SYNC: return "NFS3ERR_NOT_SYNC"; break; + case NFS3ERR_BAD_COOKIE: return "NFS3ERR_BAD_COOKIE"; break; + case NFS3ERR_NOTSUPP: return "NFS3ERR_NOTSUPP"; break; + case NFS3ERR_TOOSMALL: return "NFS3ERR_TOOSMALL"; break; + case NFS3ERR_SERVERFAULT: return "NFS3ERR_SERVERFAULT"; break; + case NFS3ERR_BADTYPE: return "NFS3ERR_BADTYPE"; break; + case NFS3ERR_JUKEBOX: return "NFS3ERR_JUKEBOX"; break; + }; + return "unknown nfs error"; +} + +int nfsstat3_to_errno(int error) +{ + switch (error) { + case NFS3_OK: return 0; break; + case NFS3ERR_PERM: return -EPERM; break; + case NFS3ERR_NOENT: return -ENOENT; break; + case NFS3ERR_IO: return -EIO; break; + case NFS3ERR_NXIO: return -ENXIO; break; + case NFS3ERR_ACCES: return -EACCES; break; + case NFS3ERR_EXIST: return -EEXIST; break; + case NFS3ERR_XDEV: return -EXDEV; break; + case NFS3ERR_NODEV: return -ENODEV; break; + case NFS3ERR_NOTDIR: return -ENOTDIR; break; + case NFS3ERR_ISDIR: return -EISDIR; break; + case NFS3ERR_INVAL: return -EINVAL; break; + case NFS3ERR_FBIG: return -EFBIG; break; + case NFS3ERR_NOSPC: return -ENOSPC; break; + case NFS3ERR_ROFS: return -EROFS; break; + case NFS3ERR_MLINK: return -EMLINK; break; + case NFS3ERR_NAMETOOLONG: return -ENAMETOOLONG; break; + case NFS3ERR_NOTEMPTY: return -EEXIST; break; + case NFS3ERR_DQUOT: return -ERANGE; break; + case NFS3ERR_STALE: return -EIO; break; + case NFS3ERR_REMOTE: return -EIO; break; + case NFS3ERR_BADHANDLE: return -EIO; break; + case NFS3ERR_NOT_SYNC: return -EIO; break; + case NFS3ERR_BAD_COOKIE: return -EIO; break; + case NFS3ERR_NOTSUPP: return -EINVAL; break; + case NFS3ERR_TOOSMALL: return -EIO; break; + case NFS3ERR_SERVERFAULT: return -EIO; break; + case NFS3ERR_BADTYPE: return -EINVAL; break; + case NFS3ERR_JUKEBOX: return -EAGAIN; break; + }; + return -ERANGE; +} + + +int rpc_nfs_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/null call"); + return -1; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/null call"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} + +int rpc_nfs_getattr_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data) +{ + struct rpc_pdu *pdu; + GETATTR3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_GETATTR, cb, private_data, (xdrproc_t)xdr_GETATTR3res, sizeof(GETATTR3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/null call"); + return -1; + } + + args.object.data.data_len = fh->data.data_len; + args.object.data.data_val = fh->data.data_val; + + if (xdr_GETATTR3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode GETATTR3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/null call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + +int rpc_nfs_lookup_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, void *private_data) +{ + struct rpc_pdu *pdu; + LOOKUP3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_LOOKUP, cb, private_data, (xdrproc_t)xdr_LOOKUP3res, sizeof(LOOKUP3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/lookup call"); + return -1; + } + + args.what.dir.data.data_len = fh->data.data_len; + args.what.dir.data.data_val = fh->data.data_val; + args.what.name = name; + + if (xdr_LOOKUP3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode LOOKUP3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/lookup call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + +int rpc_nfs_access_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, int access, void *private_data) +{ + struct rpc_pdu *pdu; + ACCESS3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_ACCESS, cb, private_data, (xdrproc_t)xdr_ACCESS3res, sizeof(ACCESS3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/access call"); + return -1; + } + + args.object.data.data_len = fh->data.data_len; + args.object.data.data_val = fh->data.data_val; + args.access = access; + + if (xdr_ACCESS3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode ACCESS3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/access call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + + +int rpc_nfs_read_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, off_t offset, size_t count, void *private_data) +{ + struct rpc_pdu *pdu; + READ3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_READ, cb, private_data, (xdrproc_t)xdr_READ3res, sizeof(READ3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/read call"); + return -1; + } + + args.file.data.data_len = fh->data.data_len; + args.file.data.data_val = fh->data.data_val; + args.offset = offset; + args.count = count; + + if (xdr_READ3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode READ3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/read call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + +int rpc_nfs_write_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *buf, off_t offset, size_t count, int stable_how, void *private_data) +{ + struct rpc_pdu *pdu; + WRITE3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_WRITE, cb, private_data, (xdrproc_t)xdr_WRITE3res, sizeof(WRITE3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/write call"); + return -1; + } + + args.file.data.data_len = fh->data.data_len; + args.file.data.data_val = fh->data.data_val; + args.offset = offset; + args.count = count; + args.stable = stable_how;; + args.data.data_len = count; + args.data.data_val = buf; + + if (xdr_WRITE3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode WRITE3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/write call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + + +int rpc_nfs_commit_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data) +{ + struct rpc_pdu *pdu; + COMMIT3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_COMMIT, cb, private_data, (xdrproc_t)xdr_COMMIT3res, sizeof(COMMIT3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/commit call"); + return -1; + } + + args.file.data.data_len = fh->data.data_len; + args.file.data.data_val = fh->data.data_val; + args.offset = 0; + args.count = 0; + + if (xdr_COMMIT3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode WRITE3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/commit call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + +int rpc_nfs_setattr_async(struct rpc_context *rpc, rpc_cb cb, SETATTR3args *args, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_SETATTR, cb, private_data, (xdrproc_t)xdr_SETATTR3res, sizeof(SETATTR3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/setattr call"); + return -1; + } + + if (xdr_SETATTR3args(&pdu->xdr, args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode SETATTR3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/setattr call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + + +int rpc_nfs_mkdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data) +{ + struct rpc_pdu *pdu; + MKDIR3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_MKDIR, cb, private_data, (xdrproc_t)xdr_MKDIR3res, sizeof(MKDIR3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/setattr call"); + return -1; + } + + bzero(&args, sizeof(MKDIR3args)); + args.where.dir.data.data_len = fh->data.data_len; + args.where.dir.data.data_val = fh->data.data_val; + args.where.name = dir; + args.attributes.mode.set_it = 1; + args.attributes.mode.set_mode3_u.mode = 0755; + + if (xdr_MKDIR3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode MKDIR3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/mkdir call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + + + +int rpc_nfs_rmdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data) +{ + struct rpc_pdu *pdu; + RMDIR3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_RMDIR, cb, private_data, (xdrproc_t)xdr_RMDIR3res, sizeof(RMDIR3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/rmdir call"); + return -1; + } + + bzero(&args, sizeof(RMDIR3args)); + args.object.dir.data.data_len = fh->data.data_len; + args.object.dir.data.data_val = fh->data.data_val; + args.object.name = dir; + + if (xdr_RMDIR3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode RMDIR3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/rmdir call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + + +int rpc_nfs_create_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *file, int mode, void *private_data) +{ + struct rpc_pdu *pdu; + CREATE3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_CREATE, cb, private_data, (xdrproc_t)xdr_CREATE3res, sizeof(CREATE3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/create call"); + return -1; + } + + bzero(&args, sizeof(CREATE3args)); + args.where.dir.data.data_len = fh->data.data_len; + args.where.dir.data.data_val = fh->data.data_val; + args.where.name = file; + args.how.mode = UNCHECKED; + args.how.createhow3_u.obj_attributes.mode.set_it = 1; + args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = mode; + + if (xdr_CREATE3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode CREATE3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/create call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + + + +int rpc_nfs_remove_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *file, void *private_data) +{ + struct rpc_pdu *pdu; + REMOVE3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_REMOVE, cb, private_data, (xdrproc_t)xdr_REMOVE3res, sizeof(REMOVE3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/remove call"); + return -1; + } + + bzero(&args, sizeof(REMOVE3args)); + args.object.dir.data.data_len = fh->data.data_len; + args.object.dir.data.data_val = fh->data.data_val; + args.object.name = file; + + if (xdr_REMOVE3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode REMOVE3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/remove call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + +int rpc_nfs_readdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, uint64_t cookie, char *cookieverf, int count, void *private_data) +{ + struct rpc_pdu *pdu; + READDIR3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_READDIR, cb, private_data, (xdrproc_t)xdr_READDIR3res, sizeof(READDIR3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/readdir call"); + return -1; + } + + bzero(&args, sizeof(READDIR3args)); + args.dir.data.data_len = fh->data.data_len; + args.dir.data.data_val = fh->data.data_val; + args.cookie = cookie; + memcpy(&args.cookieverf, cookieverf, sizeof(cookieverf3)); + args.count = count; + + if (xdr_READDIR3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode READDIR3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/readdir call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + +int rpc_nfs_fsstat_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data) +{ + struct rpc_pdu *pdu; + FSSTAT3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_FSSTAT, cb, private_data, (xdrproc_t)xdr_FSSTAT3res, sizeof(FSSTAT3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/fsstat call"); + return -1; + } + + args.fsroot.data.data_len = fh->data.data_len; + args.fsroot.data.data_val = fh->data.data_val; + + if (xdr_FSSTAT3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode FSSTAT3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/fsstat call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + +int rpc_nfs_readlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data) +{ + struct rpc_pdu *pdu; + READLINK3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_READLINK, cb, private_data, (xdrproc_t)xdr_READLINK3res, sizeof(READLINK3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/readlink call"); + return -1; + } + + args.symlink.data.data_len = fh->data.data_len; + args.symlink.data.data_val = fh->data.data_val; + + if (xdr_READLINK3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode READLINK3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/readlink call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + +int rpc_nfs_symlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *newname, char *oldpath, void *private_data) +{ + struct rpc_pdu *pdu; + SYMLINK3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_SYMLINK, cb, private_data, (xdrproc_t)xdr_SYMLINK3res, sizeof(SYMLINK3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/symlink call"); + return -1; + } + + bzero(&args, sizeof(SYMLINK3args)); + args.where.dir.data.data_len = fh->data.data_len; + args.where.dir.data.data_val = fh->data.data_val; + args.where.name = newname; + args.symlink.symlink_attributes.mode.set_it = 1; + args.symlink.symlink_attributes.mode.set_mode3_u.mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH; + args.symlink.symlink_data = oldpath; + + if (xdr_SYMLINK3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode SYMLINK3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/symlink call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + + + +int rpc_nfs_rename_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *olddir, char *oldname, struct nfs_fh3 *newdir, char *newname, void *private_data) +{ + struct rpc_pdu *pdu; + RENAME3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_RENAME, cb, private_data, (xdrproc_t)xdr_RENAME3res, sizeof(RENAME3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/rename call"); + return -1; + } + + bzero(&args, sizeof(RENAME3args)); + args.from.dir.data.data_len = olddir->data.data_len; + args.from.dir.data.data_val = olddir->data.data_val; + args.from.name = oldname; + args.to.dir.data.data_len = newdir->data.data_len; + args.to.dir.data.data_val = newdir->data.data_val; + args.to.name = newname; + + if (xdr_RENAME3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode RENAME3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/rename call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + + + +int rpc_nfs_link_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *file, struct nfs_fh3 *newdir, char *newname, void *private_data) +{ + struct rpc_pdu *pdu; + LINK3args args; + + pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_LINK, cb, private_data, (xdrproc_t)xdr_LINK3res, sizeof(LINK3res)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/link call"); + return -1; + } + + bzero(&args, sizeof(LINK3args)); + args.file.data.data_len = file->data.data_len; + args.file.data.data_val = file->data.data_val; + args.link.dir.data.data_len = newdir->data.data_len; + args.link.dir.data.data_val = newdir->data.data_val; + args.link.name = newname; + + if (xdr_LINK3args(&pdu->xdr, &args) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode LINK3args"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/link call"); + rpc_free_pdu(rpc, pdu); + return -3; + } + + return 0; +} + + + + diff --git a/ccan/nfs/nfs.x b/ccan/nfs/nfs.x new file mode 100755 index 00000000..58fc34e6 --- /dev/null +++ b/ccan/nfs/nfs.x @@ -0,0 +1,840 @@ +/* copied from rfc 1813 */ + +const NFS3_FHSIZE = 64; /* Maximum bytes in a V3 file handle */ +const NFS3_WRITEVERFSIZE = 8; +const NFS3_CREATEVERFSIZE = 8; +const NFS3_COOKIEVERFSIZE = 8; + +typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE]; + +typedef unsigned hyper uint64; +typedef uint64 cookie3; + +struct nfs_fh3 { + opaque data; +}; + +typedef string filename3<>; + +struct diropargs3 { + nfs_fh3 dir; + filename3 name; +}; + +enum ftype3 { + NF3REG = 1, + NF3DIR = 2, + NF3BLK = 3, + NF3CHR = 4, + NF3LNK = 5, + NF3SOCK = 6, + NF3FIFO = 7 +}; + +typedef unsigned long uint32; + +typedef long int32; + +typedef uint32 mode3; + +typedef uint32 uid3; + +typedef uint32 gid3; + +typedef uint64 size3; + +typedef uint64 fileid3; + +struct specdata3 { + uint32 specdata1; + uint32 specdata2; +}; + +struct nfstime3 { + uint32 seconds; + uint32 nseconds; +}; + +struct fattr3 { + ftype3 type; + mode3 mode; + uint32 nlink; + uid3 uid; + gid3 gid; + size3 size; + size3 used; + specdata3 rdev; + uint64 fsid; + fileid3 fileid; + nfstime3 atime; + nfstime3 mtime; + nfstime3 ctime; +}; + +union post_op_attr switch (bool attributes_follow) { + case TRUE: + fattr3 attributes; + case FALSE: + void; +}; + + +enum nfsstat3 { + NFS3_OK = 0, + NFS3ERR_PERM = 1, + NFS3ERR_NOENT = 2, + NFS3ERR_IO = 5, + NFS3ERR_NXIO = 6, + NFS3ERR_ACCES = 13, + NFS3ERR_EXIST = 17, + NFS3ERR_XDEV = 18, + NFS3ERR_NODEV = 19, + NFS3ERR_NOTDIR = 20, + NFS3ERR_ISDIR = 21, + NFS3ERR_INVAL = 22, + NFS3ERR_FBIG = 27, + NFS3ERR_NOSPC = 28, + NFS3ERR_ROFS = 30, + NFS3ERR_MLINK = 31, + NFS3ERR_NAMETOOLONG = 63, + NFS3ERR_NOTEMPTY = 66, + NFS3ERR_DQUOT = 69, + NFS3ERR_STALE = 70, + NFS3ERR_REMOTE = 71, + NFS3ERR_BADHANDLE = 10001, + NFS3ERR_NOT_SYNC = 10002, + NFS3ERR_BAD_COOKIE = 10003, + NFS3ERR_NOTSUPP = 10004, + NFS3ERR_TOOSMALL = 10005, + NFS3ERR_SERVERFAULT = 10006, + NFS3ERR_BADTYPE = 10007, + NFS3ERR_JUKEBOX = 10008 +}; + +enum stable_how { + UNSTABLE = 0, + DATA_SYNC = 1, + FILE_SYNC = 2 +}; + +typedef uint64 offset3; + +typedef uint32 count3; + +struct wcc_attr { + size3 size; + nfstime3 mtime; + nfstime3 ctime; +}; + +union pre_op_attr switch (bool attributes_follow) { + case TRUE: + wcc_attr attributes; + case FALSE: + void; +}; + +struct wcc_data { + pre_op_attr before; + post_op_attr after; +}; + +struct WRITE3args { + nfs_fh3 file; + offset3 offset; + count3 count; + stable_how stable; + opaque data<>; +}; + +typedef opaque writeverf3[NFS3_WRITEVERFSIZE]; + +struct WRITE3resok { + wcc_data file_wcc; + count3 count; + stable_how committed; + writeverf3 verf; +}; + +struct WRITE3resfail { + wcc_data file_wcc; +}; + +union WRITE3res switch (nfsstat3 status) { + case NFS3_OK: + WRITE3resok resok; + default: + WRITE3resfail resfail; +}; + +struct LOOKUP3args { + diropargs3 what; +}; + +struct LOOKUP3resok { + nfs_fh3 object; + post_op_attr obj_attributes; + post_op_attr dir_attributes; +}; + +struct LOOKUP3resfail { + post_op_attr dir_attributes; +}; + + + +union LOOKUP3res switch (nfsstat3 status) { + case NFS3_OK: + LOOKUP3resok resok; + default: + LOOKUP3resfail resfail; +}; + +struct COMMIT3args { + nfs_fh3 file; + offset3 offset; + count3 count; +}; + +struct COMMIT3resok { + wcc_data file_wcc; + writeverf3 verf; +}; + +struct COMMIT3resfail { + wcc_data file_wcc; +}; + +union COMMIT3res switch (nfsstat3 status) { + case NFS3_OK: + COMMIT3resok resok; + default: + COMMIT3resfail resfail; +}; + +const ACCESS3_READ = 0x0001; +const ACCESS3_LOOKUP = 0x0002; +const ACCESS3_MODIFY = 0x0004; +const ACCESS3_EXTEND = 0x0008; +const ACCESS3_DELETE = 0x0010; +const ACCESS3_EXECUTE = 0x0020; + +struct ACCESS3args { + nfs_fh3 object; + uint32 access; +}; + +struct ACCESS3resok { + post_op_attr obj_attributes; + uint32 access; +}; + +struct ACCESS3resfail { + post_op_attr obj_attributes; +}; + +union ACCESS3res switch (nfsstat3 status) { +case NFS3_OK: + ACCESS3resok resok; +default: + ACCESS3resfail resfail; +}; + +struct GETATTR3args { + nfs_fh3 object; +}; + +struct GETATTR3resok { + fattr3 obj_attributes; +}; + +union GETATTR3res switch (nfsstat3 status) { + case NFS3_OK: + GETATTR3resok resok; + default: + void; +}; + + + +enum time_how { + DONT_CHANGE = 0, + SET_TO_SERVER_TIME = 1, + SET_TO_CLIENT_TIME = 2 +}; + +union set_mode3 switch (bool set_it) { + case TRUE: + mode3 mode; + default: + void; +}; + +union set_uid3 switch (bool set_it) { + case TRUE: + uid3 uid; + default: + void; +}; + +union set_gid3 switch (bool set_it) { + case TRUE: + gid3 gid; + default: + void; +}; + +union set_size3 switch (bool set_it) { + case TRUE: + size3 size; + default: + void; +}; + +union set_atime switch (time_how set_it) { + case SET_TO_CLIENT_TIME: + nfstime3 atime; + default: + void; +}; + +union set_mtime switch (time_how set_it) { + case SET_TO_CLIENT_TIME: + nfstime3 mtime; + default: + void; +}; + +struct sattr3 { + set_mode3 mode; + set_uid3 uid; + set_gid3 gid; + set_size3 size; + set_atime atime; + set_mtime mtime; +}; + +enum createmode3 { + UNCHECKED = 0, + GUARDED = 1, + EXCLUSIVE = 2 +}; + + +typedef opaque createverf3[NFS3_CREATEVERFSIZE]; + +union createhow3 switch (createmode3 mode) { + case UNCHECKED: + case GUARDED: + sattr3 obj_attributes; + case EXCLUSIVE: + createverf3 verf; +}; + +struct CREATE3args { + diropargs3 where; + createhow3 how; +}; + +union post_op_fh3 switch (bool handle_follows) { + case TRUE: + nfs_fh3 handle; + case FALSE: + void; +}; + +struct CREATE3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; + +struct CREATE3resfail { + wcc_data dir_wcc; + }; + +union CREATE3res switch (nfsstat3 status) { + case NFS3_OK: + CREATE3resok resok; + default: + CREATE3resfail resfail; +}; + +struct REMOVE3args { + diropargs3 object; +}; + +struct REMOVE3resok { + wcc_data dir_wcc; +}; + +struct REMOVE3resfail { + wcc_data dir_wcc; +}; + +union REMOVE3res switch (nfsstat3 status) { + case NFS3_OK: + REMOVE3resok resok; + default: + REMOVE3resfail resfail; +}; + +struct READ3args { + nfs_fh3 file; + offset3 offset; + count3 count; +}; + +struct READ3resok { + post_op_attr file_attributes; + count3 count; + bool eof; + opaque data<>; +}; + +struct READ3resfail { + post_op_attr file_attributes; +}; + +union READ3res switch (nfsstat3 status) { + case NFS3_OK: + READ3resok resok; + default: + READ3resfail resfail; +}; + + +const FSF3_LINK = 0x0001; +const FSF3_SYMLINK = 0x0002; +const FSF3_HOMOGENEOUS = 0x0008; +const FSF3_CANSETTIME = 0x0010; + +struct FSINFO3args { + nfs_fh3 fsroot; +}; + +struct FSINFO3resok { + post_op_attr obj_attributes; + uint32 rtmax; + uint32 rtpref; + uint32 rtmult; + uint32 wtmax; + uint32 wtpref; + uint32 wtmult; + uint32 dtpref; + size3 maxfilesize; + nfstime3 time_delta; + uint32 properties; +}; + +struct FSINFO3resfail { + post_op_attr obj_attributes; +}; + +union FSINFO3res switch (nfsstat3 status) { + case NFS3_OK: + FSINFO3resok resok; + default: + FSINFO3resfail resfail; +}; + + +struct FSSTAT3args { + nfs_fh3 fsroot; +}; + +struct FSSTAT3resok { + post_op_attr obj_attributes; + size3 tbytes; + size3 fbytes; + size3 abytes; + size3 tfiles; + size3 ffiles; + size3 afiles; + uint32 invarsec; +}; + +struct FSSTAT3resfail { + post_op_attr obj_attributes; +}; + +union FSSTAT3res switch (nfsstat3 status) { + case NFS3_OK: + FSSTAT3resok resok; + default: + FSSTAT3resfail resfail; +}; + +struct PATHCONF3args { + nfs_fh3 object; +}; + +struct PATHCONF3resok { + post_op_attr obj_attributes; + uint32 linkmax; + uint32 name_max; + bool no_trunc; + bool chown_restricted; + bool case_insensitive; + bool case_preserving; +}; + +struct PATHCONF3resfail { + post_op_attr obj_attributes; +}; + +union PATHCONF3res switch (nfsstat3 status) { + case NFS3_OK: + PATHCONF3resok resok; + default: + PATHCONF3resfail resfail; +}; + +typedef string nfspath3<>; + +struct symlinkdata3 { + sattr3 symlink_attributes; + nfspath3 symlink_data; +}; + +struct SYMLINK3args { + diropargs3 where; + symlinkdata3 symlink; +}; + +struct SYMLINK3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; + +struct SYMLINK3resfail { + wcc_data dir_wcc; +}; + +union SYMLINK3res switch (nfsstat3 status) { + case NFS3_OK: + SYMLINK3resok resok; + default: + SYMLINK3resfail resfail; +}; + + +struct READLINK3args { + nfs_fh3 symlink; +}; + +struct READLINK3resok { + post_op_attr symlink_attributes; + nfspath3 data; +}; + +struct READLINK3resfail { + post_op_attr symlink_attributes; +}; + +union READLINK3res switch (nfsstat3 status) { + case NFS3_OK: + READLINK3resok resok; + default: + READLINK3resfail resfail; +}; + + +struct devicedata3 { + sattr3 dev_attributes; + specdata3 spec; +}; + +union mknoddata3 switch (ftype3 type) { + case NF3CHR: + case NF3BLK: + devicedata3 device; + case NF3SOCK: + case NF3FIFO: + sattr3 pipe_attributes; + default: + void; +}; + +struct MKNOD3args { + diropargs3 where; + mknoddata3 what; +}; + +struct MKNOD3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; + +struct MKNOD3resfail { + wcc_data dir_wcc; +}; + +union MKNOD3res switch (nfsstat3 status) { + case NFS3_OK: + MKNOD3resok resok; + default: + MKNOD3resfail resfail; +}; + + +struct MKDIR3args { + diropargs3 where; + sattr3 attributes; +}; + +struct MKDIR3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; + +struct MKDIR3resfail { + wcc_data dir_wcc; +}; + +union MKDIR3res switch (nfsstat3 status) { + case NFS3_OK: + MKDIR3resok resok; + default: + MKDIR3resfail resfail; +}; + +struct RMDIR3args { + diropargs3 object; +}; + +struct RMDIR3resok { + wcc_data dir_wcc; +}; + +struct RMDIR3resfail { + wcc_data dir_wcc; +}; + +union RMDIR3res switch (nfsstat3 status) { + case NFS3_OK: + RMDIR3resok resok; + default: + RMDIR3resfail resfail; +}; + +struct RENAME3args { + diropargs3 from; + diropargs3 to; +}; + +struct RENAME3resok { + wcc_data fromdir_wcc; + wcc_data todir_wcc; +}; + +struct RENAME3resfail { + wcc_data fromdir_wcc; + wcc_data todir_wcc; +}; + +union RENAME3res switch (nfsstat3 status) { + case NFS3_OK: + RENAME3resok resok; + default: + RENAME3resfail resfail; +}; + +struct READDIRPLUS3args { + nfs_fh3 dir; + cookie3 cookie; + cookieverf3 cookieverf; + count3 dircount; + count3 maxcount; +}; + +struct entryplus3 { + fileid3 fileid; + filename3 name; + cookie3 cookie; + post_op_attr name_attributes; + post_op_fh3 name_handle; + entryplus3 *nextentry; +}; + +struct dirlistplus3 { + entryplus3 *entries; + bool eof; +}; + +struct READDIRPLUS3resok { + post_op_attr dir_attributes; + cookieverf3 cookieverf; + dirlistplus3 reply; +}; + + +struct READDIRPLUS3resfail { + post_op_attr dir_attributes; +}; + +union READDIRPLUS3res switch (nfsstat3 status) { + case NFS3_OK: + READDIRPLUS3resok resok; + default: + READDIRPLUS3resfail resfail; +}; + +struct READDIR3args { + nfs_fh3 dir; + cookie3 cookie; + cookieverf3 cookieverf; + count3 count; +}; + + +struct entry3 { + fileid3 fileid; + filename3 name; + cookie3 cookie; + entry3 *nextentry; +}; + +struct dirlist3 { + entry3 *entries; + bool eof; +}; + +struct READDIR3resok { + post_op_attr dir_attributes; + cookieverf3 cookieverf; + dirlist3 reply; +}; + +struct READDIR3resfail { + post_op_attr dir_attributes; +}; + +union READDIR3res switch (nfsstat3 status) { + case NFS3_OK: + READDIR3resok resok; + default: + READDIR3resfail resfail; +}; + +struct LINK3args { + nfs_fh3 file; + diropargs3 link; +}; + +struct LINK3resok { + post_op_attr file_attributes; + wcc_data linkdir_wcc; +}; + +struct LINK3resfail { + post_op_attr file_attributes; + wcc_data linkdir_wcc; +}; + +union LINK3res switch (nfsstat3 status) { + case NFS3_OK: + LINK3resok resok; + default: + LINK3resfail resfail; +}; + +union sattrguard3 switch (bool check) { + case TRUE: + nfstime3 obj_ctime; + case FALSE: + void; +}; + +struct SETATTR3args { + nfs_fh3 object; + sattr3 new_attributes; + sattrguard3 guard; +}; + +struct SETATTR3resok { + wcc_data obj_wcc; +}; + +struct SETATTR3resfail { + wcc_data obj_wcc; +}; + +union SETATTR3res switch (nfsstat3 status) { + case NFS3_OK: + SETATTR3resok resok; + default: + SETATTR3resfail resfail; +}; + +program NFS_PROGRAM { + version NFS_V3 { + void + NFS3_NULL(void) = 0; + + GETATTR3res + NFS3_GETATTR(GETATTR3args) = 1; + + SETATTR3res + NFS3_SETATTR(SETATTR3args) = 2; + + LOOKUP3res + NFS3_LOOKUP(LOOKUP3args) = 3; + + ACCESS3res + NFS3_ACCESS(ACCESS3args) = 4; + + READLINK3res + NFS3_READLINK(READLINK3args) = 5; + + READ3res + NFS3_READ(READ3args) = 6; + + WRITE3res + NFS3_WRITE(WRITE3args) = 7; + + CREATE3res + NFS3_CREATE(CREATE3args) = 8; + + MKDIR3res + NFS3_MKDIR(MKDIR3args) = 9; + + SYMLINK3res + NFS3_SYMLINK(SYMLINK3args) = 10; + +/* MKNOD3res NFSPROC3_MKNOD(MKNOD3args) = 11;*/ + + REMOVE3res + NFS3_REMOVE(REMOVE3args) = 12; + + RMDIR3res + NFS3_RMDIR(RMDIR3args) = 13; + + RENAME3res + NFS3_RENAME(RENAME3args) = 14; + + LINK3res + NFS3_LINK(LINK3args) = 15; + + READDIR3res + NFS3_READDIR(READDIR3args) = 16; + + READDIRPLUS3res + NFS3_READDIRPLUS(READDIRPLUS3args) = 17; + + FSSTAT3res + NFS3_FSSTAT(FSSTAT3args) = 18; + + FSINFO3res + NFS3_FSINFO(FSINFO3args) = 19; + + PATHCONF3res + NFS3_PATHCONF(PATHCONF3args) = 20; + + COMMIT3res + NFS3_COMMIT(COMMIT3args) = 21; + } = 3; +} = 100003; diff --git a/ccan/nfs/nfsacl.c b/ccan/nfs/nfsacl.c new file mode 100644 index 00000000..c1053340 --- /dev/null +++ b/ccan/nfs/nfsacl.c @@ -0,0 +1,45 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-private.h" +#include "libnfs-raw-nfsacl.h" + + +int rpc_nfsacl_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, NFSACL_PROGRAM, NFSACL_V3, NFSACL3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfsacl/null call"); + return -1; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfsacl/null call"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} + diff --git a/ccan/nfs/nfsacl.x b/ccan/nfs/nfsacl.x new file mode 100644 index 00000000..68313c98 --- /dev/null +++ b/ccan/nfs/nfsacl.x @@ -0,0 +1,8 @@ +/* deducted from wireshark traces */ + + +program NFSACL_PROGRAM { + version NFSACL_V3 { + void NFSACL3_NULL(void) = 0; + } = 3; +} = 100227; diff --git a/ccan/nfs/nfsclient-async.c b/ccan/nfs/nfsclient-async.c new file mode 100644 index 00000000..4abb901a --- /dev/null +++ b/ccan/nfs/nfsclient-async.c @@ -0,0 +1,233 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +/* Example program using the highlevel async interface. + */ + +#define SERVER "10.1.1.27" +#define EXPORT "/VIRTUAL" +#define NFSFILE "/BOOKS/Classics/Dracula.djvu" +#define NFSDIR "/BOOKS/Classics/" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libnfs.h" + +struct client { + char *server; + char *export; + uint32_t mount_port; + struct nfsfh *nfsfh; + int is_finished; +}; + +void nfs_opendir_cb(int status, struct nfs_context *nfs, void *data, void *private_data) +{ + struct client *client = private_data; + struct nfsdir *nfsdir = data; + struct nfsdirent *nfsdirent; + + if (status < 0) { + printf("opendir failed with \"%s\"\n", (char *)data); + exit(10); + } + + printf("opendir successful\n"); + while((nfsdirent = nfs_readdir(nfs, nfsdir)) != NULL) { + printf("Inode:%d Name:%s\n", (int)nfsdirent->inode, nfsdirent->name); + } + nfs_closedir(nfs, nfsdir); + + client->is_finished = 1; +} + +void nfs_close_cb(int status, struct nfs_context *nfs, void *data, void *private_data) +{ + struct client *client = private_data; + + if (status < 0) { + printf("close failed with \"%s\"\n", (char *)data); + exit(10); + } + + printf("close successful\n"); + printf("call opendir(%s)\n", NFSDIR); + if (nfs_opendir_async(nfs, NFSDIR, nfs_opendir_cb, client) != 0) { + printf("Failed to start async nfs close\n"); + exit(10); + } +} + +void nfs_fstat_cb(int status, struct nfs_context *nfs, void *data, void *private_data) +{ + struct client *client = private_data; + struct stat *st; + + if (status < 0) { + printf("fstat call failed with \"%s\"\n", (char *)data); + exit(10); + } + + printf("Got reply from server for fstat(%s).\n", NFSFILE); + st = (struct stat *)data; + printf("Mode %04o\n", st->st_mode); + printf("Size %d\n", (int)st->st_size); + printf("Inode %04o\n", (int)st->st_ino); + + printf("Close file\n"); + if (nfs_close_async(nfs, client->nfsfh, nfs_close_cb, client) != 0) { + printf("Failed to start async nfs close\n"); + exit(10); + } +} + +void nfs_read_cb(int status, struct nfs_context *nfs, void *data, void *private_data) +{ + struct client *client = private_data; + char *read_data; + int i; + + if (status < 0) { + printf("read failed with \"%s\"\n", (char *)data); + exit(10); + } + + printf("read successful with %d bytes of data\n", status); + read_data = data; + for (i=0;i<16;i++) { + printf("%02x ", read_data[i]&0xff); + } + printf("\n"); + printf("Fstat file :%s\n", NFSFILE); + if (nfs_fstat_async(nfs, client->nfsfh, nfs_fstat_cb, client) != 0) { + printf("Failed to start async nfs fstat\n"); + exit(10); + } +} + +void nfs_open_cb(int status, struct nfs_context *nfs, void *data, void *private_data) +{ + struct client *client = private_data; + struct nfsfh *nfsfh; + + if (status < 0) { + printf("open call failed with \"%s\"\n", (char *)data); + exit(10); + } + + nfsfh = data; + client->nfsfh = nfsfh; + printf("Got reply from server for open(%s). Handle:%p\n", NFSFILE, data); + printf("Read first 16 bytes\n"); + if (nfs_pread_async(nfs, nfsfh, 0, 16, nfs_read_cb, client) != 0) { + printf("Failed to start async nfs open\n"); + exit(10); + } +} + +void nfs_stat_cb(int status, struct nfs_context *nfs, void *data, void *private_data) +{ + struct client *client = private_data; + struct stat *st; + + if (status < 0) { + printf("stat call failed with \"%s\"\n", (char *)data); + exit(10); + } + + printf("Got reply from server for stat(%s).\n", NFSFILE); + st = (struct stat *)data; + printf("Mode %04o\n", st->st_mode); + printf("Size %d\n", (int)st->st_size); + printf("Inode %04o\n", (int)st->st_ino); + + printf("Open file for reading :%s\n", NFSFILE); + if (nfs_open_async(nfs, NFSFILE, O_RDONLY, nfs_open_cb, client) != 0) { + printf("Failed to start async nfs open\n"); + exit(10); + } +} + +void nfs_mount_cb(int status, struct nfs_context *nfs, void *data, void *private_data) +{ + struct client *client = private_data; + + if (status < 0) { + printf("mount/mnt call failed with \"%s\"\n", (char *)data); + exit(10); + } + + printf("Got reply from server for MOUNT/MNT procedure.\n"); + printf("Stat file :%s\n", NFSFILE); + if (nfs_stat_async(nfs, NFSFILE, nfs_stat_cb, client) != 0) { + printf("Failed to start async nfs stat\n"); + exit(10); + } +} + + + +int main(int argc, char *argv[]) +{ + struct nfs_context *nfs; + struct pollfd pfd; + int ret; + struct client client; + + client.server = SERVER; + client.export = EXPORT; + client.is_finished = 0; + + nfs = nfs_init_context(); + if (nfs == NULL) { + printf("failed to init context\n"); + exit(10); + } + + ret = nfs_mount_async(nfs, client.server, client.export, nfs_mount_cb, &client); + if (ret != 0) { + printf("Failed to start async nfs mount\n"); + exit(10); + } + + for (;;) { + pfd.fd = nfs_get_fd(nfs); + pfd.events = nfs_which_events(nfs); + + if (poll(&pfd, 1, -1) < 0) { + printf("Poll failed"); + exit(10); + } + if (nfs_service(nfs, pfd.revents) < 0) { + printf("nfs_service failed\n"); + break; + } + if (client.is_finished) { + break; + } + } + + nfs_destroy_context(nfs); + printf("nfsclient finished\n"); + return 0; +} diff --git a/ccan/nfs/nfsclient-raw.c b/ccan/nfs/nfsclient-raw.c new file mode 100644 index 00000000..4738bdf9 --- /dev/null +++ b/ccan/nfs/nfsclient-raw.c @@ -0,0 +1,220 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +/* Example program using the lowlevel raw interface. + * This allow accurate control of the exact commands that are being used. + */ + +#define SERVER "10.1.1.27" +#define EXPORT "/VIRTUAL" + +#include +#include +#include +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-raw-mount.h" + +struct client { + char *server; + char *export; + uint32_t mount_port; + int is_finished; +}; + +void mount_mnt_cb(struct rpc_context *rpc, int status, void *data, void *private_data) +{ + mountres3 *res; + struct client *client = private_data; + + if (status == RPC_STATUS_ERROR) { + printf("mount/mnt call failed with \"%s\"\n", (char *)data); + exit(10); + } + if (status != RPC_STATUS_SUCCESS) { + printf("mount/mnt call to server %s failed, status:%d\n", client->server, status); + exit(10); + } + + res = data; + if (res->fhs_status != MNT3_OK) { + printf("RPC error: Mount failed with error %s(%d) %s(%d)", mountstat3_to_str(res->fhs_status), res->fhs_status, strerror(-mountstat3_to_errno(res->fhs_status)), -mountstat3_to_errno(res->fhs_status)); + exit(10); + } + + printf("Got reply from server for MOUNT/MNT procedure.\n"); + client->is_finished = 1; +} + + +void mount_null_cb(struct rpc_context *rpc, int status, void *data, void *private_data) +{ + struct client *client = private_data; + + if (status == RPC_STATUS_ERROR) { + printf("mount null call failed with \"%s\"\n", (char *)data); + exit(10); + } + if (status != RPC_STATUS_SUCCESS) { + printf("mount null call to server %s failed, status:%d\n", client->server, status); + exit(10); + } + + printf("Got reply from server for MOUNT/NULL procedure.\n"); + printf("Send MOUNT/MNT command for %s\n", client->export); + if (rpc_mount_mnt_async(rpc, mount_mnt_cb, client->export, client) != 0) { + printf("Failed to send mnt request\n"); + exit(10); + } + +} + +void mount_connect_cb(struct rpc_context *rpc, int status, void *data, void *private_data) +{ + struct client *client = private_data; + + if (status != RPC_STATUS_SUCCESS) { + printf("connection to RPC.MOUNTD on server %s failed\n", client->server); + exit(10); + } + + printf("Connected to RPC.MOUNTD on %s:%d\n", client->server, client->mount_port); + printf("Send NULL request to check if RPC.MOUNTD is actually running\n"); + if (rpc_mount_null_async(rpc, mount_null_cb, client) != 0) { + printf("Failed to send null request\n"); + exit(10); + } +} + + +void pmap_getport_cb(struct rpc_context *rpc, int status, void *data, void *private_data) +{ + struct client *client = private_data; + uint32_t port; + + if (status == RPC_STATUS_ERROR) { + printf("portmapper getport call failed with \"%s\"\n", (char *)data); + exit(10); + } + if (status != RPC_STATUS_SUCCESS) { + printf("portmapper getport call to server %s failed, status:%d\n", client->server, status); + exit(10); + } + + client->mount_port = *(uint32_t *)data; + printf("GETPORT returned Port:%d\n", client->mount_port); + if (client->mount_port == 0) { + printf("RPC.MOUNTD is not available on server : %s\n", client->server, client->mount_port); + exit(10); + } + + printf("Disconnect socket from portmap server\n"); + if (rpc_disconnect(rpc, "normal disconnect") != 0) { + printf("Failed to disconnect socket to portmapper\n"); + exit(10); + } + + printf("Connect to RPC.MOUNTD on %s:%d\n", client->server, client->mount_port); + if (rpc_connect_async(rpc, client->server, client->mount_port, 1, mount_connect_cb, client) != 0) { + printf("Failed to start connection\n"); + exit(10); + } +} + + +void pmap_null_cb(struct rpc_context *rpc, int status, void *data, void *private_data) +{ + struct client *client = private_data; + + if (status == RPC_STATUS_ERROR) { + printf("portmapper null call failed with \"%s\"\n", (char *)data); + exit(10); + } + if (status != RPC_STATUS_SUCCESS) { + printf("portmapper null call to server %s failed, status:%d\n", client->server, status); + exit(10); + } + + printf("Got reply from server for PORTMAP/NULL procedure.\n"); + printf("Send getport request asking for MOUNT port\n"); + if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, pmap_getport_cb, client) != 0) { + printf("Failed to send getport request\n"); + exit(10); + } +} + +void pmap_connect_cb(struct rpc_context *rpc, int status, void *data, void *private_data) +{ + struct client *client = private_data; + + printf("pmap_connect_cb status:%d.\n", status); + if (status != RPC_STATUS_SUCCESS) { + printf("connection to portmapper on server %s failed\n", client->server); + exit(10); + } + + printf("Send NULL request to check if portmapper is actually running\n"); + if (rpc_pmap_null_async(rpc, pmap_null_cb, client) != 0) { + printf("Failed to send null request\n"); + exit(10); + } +} + + +int main(int argc, char *argv[]) +{ + struct rpc_context *rpc; + struct pollfd pfd; + int ret; + struct client client; + + rpc = rpc_init_context(); + if (rpc == NULL) { + printf("failed to init context\n"); + exit(10); + } + + client.server = SERVER; + client.export = EXPORT; + client.is_finished = 0; + if (rpc_connect_async(rpc, client.server, 111, 0, pmap_connect_cb, &client) != 0) { + printf("Failed to start connection\n"); + exit(10); + } + + for (;;) { + pfd.fd = rpc_get_fd(rpc); + pfd.events = rpc_which_events(rpc); + + if (poll(&pfd, 1, -1) < 0) { + printf("Poll failed"); + exit(10); + } + if (rpc_service(rpc, pfd.revents) < 0) { + printf("rpc_service failed\n"); + break; + } + if (client.is_finished) { + break; + } + } + + rpc_destroy_context(rpc); + rpc=NULL; + printf("nfsclient finished\n"); + return 0; +} diff --git a/ccan/nfs/nfsclient-sync.c b/ccan/nfs/nfsclient-sync.c new file mode 100644 index 00000000..a9f5265b --- /dev/null +++ b/ccan/nfs/nfsclient-sync.c @@ -0,0 +1,202 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +/* Example program using the highlevel sync interface + */ + +#define SERVER "10.1.1.27" +#define EXPORT "/VIRTUAL" +#define NFSFILE "/BOOKS/Classics/Dracula.djvu" +#define NFSFILER "/BOOKS/Classics/Dracula.djvu.renamed" +#define NFSFILEW "/BOOKS/Classics/foo" +#define NFSDIR "/BOOKS/Classics/" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libnfs.h" +#include /* for authunix_create() */ + +struct client { + char *server; + char *export; + uint32_t mount_port; + int is_finished; +}; + + +int main(int argc, char *argv[]) +{ + struct nfs_context *nfs; + int i, ret; + struct client client; + struct stat st; + struct nfsfh *nfsfh; + struct nfsdir *nfsdir; + struct nfsdirent *nfsdirent; + client.server = SERVER; + client.export = EXPORT; + client.is_finished = 0; + char buf[16]; + off_t offset; + struct statvfs svfs; + + nfs = nfs_init_context(); + if (nfs == NULL) { + printf("failed to init context\n"); + exit(10); + } + + ret = nfs_mount_sync(nfs, client.server, client.export); + if (ret != 0) { + printf("Failed to mount nfs share : %s\n", nfs_get_error(nfs)); + exit(10); + } + printf("mounted share successfully\n"); +exit(10); + + ret = nfs_stat_sync(nfs, NFSFILE, &st); + if (ret != 0) { + printf("Failed to stat(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + exit(10); + } + printf("Mode %04o\n", st.st_mode); + printf("Size %d\n", (int)st.st_size); + printf("Inode %04o\n", (int)st.st_ino); + + ret = nfs_open_sync(nfs, NFSFILE, O_RDONLY, &nfsfh); + if (ret != 0) { + printf("Failed to open(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + exit(10); + } + + ret = nfs_read_sync(nfs, nfsfh, 16, buf); + if (ret < 0) { + printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + exit(10); + } + printf("read %d bytes\n", ret); + for (i=0;i<16;i++) { + printf("%02x ", buf[i]&0xff); + } + printf("\n"); + ret = nfs_read_sync(nfs, nfsfh, 16, buf); + if (ret < 0) { + printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + exit(10); + } + printf("read %d bytes\n", ret); + for (i=0;i<16;i++) { + printf("%02x ", buf[i]&0xff); + } + printf("\n"); + + ret = (int)nfs_lseek_sync(nfs, nfsfh, 0, SEEK_CUR, &offset); + if (ret < 0) { + printf("Failed to lseek(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + exit(10); + } + printf("File position is %d\n", (int)offset); + + printf("seek to end of file\n"); + ret = (int)nfs_lseek_sync(nfs, nfsfh, 0, SEEK_END, &offset); + if (ret < 0) { + printf("Failed to lseek(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + exit(10); + } + printf("File position is %d\n", (int)offset); + + ret = nfs_fstat_sync(nfs, nfsfh, &st); + if (ret != 0) { + printf("Failed to stat(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + exit(10); + } + printf("Mode %04o\n", st.st_mode); + printf("Size %d\n", (int)st.st_size); + printf("Inode %04o\n", (int)st.st_ino); + + + ret = nfs_close_sync(nfs, nfsfh); + if (ret < 0) { + printf("Failed to close(%s)\n", NFSFILE, nfs_get_error(nfs)); + exit(10); + } + + ret = nfs_opendir_sync(nfs, NFSDIR, &nfsdir); + if (ret != 0) { + printf("Failed to open(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + exit(10); + } + while((nfsdirent = nfs_readdir(nfs, nfsdir)) != NULL) { + printf("Inode:%d Name:%s\n", (int)nfsdirent->inode, nfsdirent->name); + } + nfs_closedir(nfs, nfsdir); + + + ret = nfs_open_sync(nfs, NFSFILEW, O_WRONLY, &nfsfh); + if (ret != 0) { + printf("Failed to open(%s) %s\n", NFSFILEW, nfs_get_error(nfs)); + exit(10); + } + ret = nfs_pwrite_sync(nfs, nfsfh, 0, 16, buf); + if (ret < 0) { + printf("Failed to pwrite(%s) %s\n", NFSFILEW, nfs_get_error(nfs)); + exit(10); + } + ret = nfs_fsync_sync(nfs, nfsfh); + if (ret < 0) { + printf("Failed to fsync(%s) %s\n", NFSFILEW, nfs_get_error(nfs)); + exit(10); + } + ret = nfs_close_sync(nfs, nfsfh); + if (ret < 0) { + printf("Failed to close(%s)\n", NFSFILEW, nfs_get_error(nfs)); + exit(10); + } + + + ret = nfs_statvfs_sync(nfs, NFSDIR, &svfs); + if (ret < 0) { + printf("Failed to statvfs(%s)\n", NFSDIR, nfs_get_error(nfs)); + exit(10); + } + printf("files %d/%d/%d\n", (int)svfs.f_files, (int)svfs.f_ffree, (int)svfs.f_favail); + + + ret = nfs_access_sync(nfs, NFSFILE, R_OK); + if (ret != 0) { + printf("Failed to access(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + } + + /* become root */ + nfs_set_auth(nfs, authunix_create("Ronnies-Laptop", 0, 0, 0, NULL)); + + ret = nfs_link_sync(nfs, NFSFILE, NFSFILER); + if (ret != 0) { + printf("Failed to link(%s) %s\n", NFSFILE, nfs_get_error(nfs)); + } + + + nfs_destroy_context(nfs); + printf("nfsclient finished\n"); + return 0; +} diff --git a/ccan/nfs/pdu.c b/ccan/nfs/pdu.c new file mode 100644 index 00000000..0f32d7e0 --- /dev/null +++ b/ccan/nfs/pdu.c @@ -0,0 +1,226 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include +#include "dlinklist.h" +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-private.h" + +struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, xdrproc_t xdr_decode_fn, int xdr_decode_bufsize) +{ + struct rpc_pdu *pdu; + struct rpc_msg msg; + + if (rpc == NULL) { + printf("trying to allocate rpc pdu on NULL context\n"); + return NULL; + } + + pdu = malloc(sizeof(struct rpc_pdu)); + if (pdu == NULL) { + printf("Failed to allocate pdu structure\n"); + return NULL; + } + bzero(pdu, sizeof(struct rpc_pdu)); + pdu->xid = rpc->xid++; + pdu->cb = cb; + pdu->private_data = private_data; + pdu->xdr_decode_fn = xdr_decode_fn; + pdu->xdr_decode_bufsize = xdr_decode_bufsize; + + xdrmem_create(&pdu->xdr, rpc->encodebuf, rpc->encodebuflen, XDR_ENCODE); + xdr_setpos(&pdu->xdr, 4); /* skip past the record marker */ + + bzero(&msg, sizeof(struct rpc_msg)); + msg.rm_xid = pdu->xid; + msg.rm_direction = CALL; + msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + msg.rm_call.cb_prog = program; + msg.rm_call.cb_vers = version; + msg.rm_call.cb_proc = procedure; + msg.rm_call.cb_cred = rpc->auth->ah_cred; + msg.rm_call.cb_verf = rpc->auth->ah_verf; + + if (xdr_callmsg(&pdu->xdr, &msg) == 0) { + printf("xdr_callmsg failed\n"); + xdr_destroy(&pdu->xdr); + free(pdu); + return NULL; + } + + return pdu; +} + +void rpc_free_pdu(struct rpc_context *rpc _U_, struct rpc_pdu *pdu) +{ + if (pdu->outdata.data != NULL) { + free(pdu->outdata.data); + pdu->outdata.data = NULL; + } + + if (pdu->xdr_decode_buf != NULL) { + xdr_free(pdu->xdr_decode_fn, pdu->xdr_decode_buf); + free(pdu->xdr_decode_buf); + pdu->xdr_decode_buf = NULL; + } + + xdr_destroy(&pdu->xdr); + free(pdu); +} + + +int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu) +{ + int size, recordmarker; + + size = xdr_getpos(&pdu->xdr); + + /* write recordmarker */ + xdr_setpos(&pdu->xdr, 0); + recordmarker = (size - 4) | 0x80000000; + xdr_int(&pdu->xdr, &recordmarker); + + pdu->outdata.size = size; + pdu->outdata.data = malloc(pdu->outdata.size); + if (pdu->outdata.data == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate buffer for pdu\n"); + rpc_free_pdu(rpc, pdu); + return -1; + } + + memcpy(pdu->outdata.data, rpc->encodebuf, pdu->outdata.size); + DLIST_ADD_END(rpc->outqueue, pdu, NULL); + + return 0; +} + +int rpc_get_pdu_size(char *buf) +{ + uint32_t size; + + size = ntohl(*(uint32_t *)buf); + + if ((size & 0x80000000) == 0) { + printf("cant handle oncrpc fragments\n"); + return -1; + } + + return (size & 0x7fffffff) + 4; +} + +static int rpc_process_reply(struct rpc_context *rpc, struct rpc_pdu *pdu, XDR *xdr) +{ + struct rpc_msg msg; + + bzero(&msg, sizeof(struct rpc_msg)); + msg.acpted_rply.ar_verf = _null_auth; + if (pdu->xdr_decode_bufsize > 0) { + pdu->xdr_decode_buf = malloc(pdu->xdr_decode_bufsize); + if (pdu->xdr_decode_buf == NULL) { + printf("xdr_replymsg failed in portmap_getport_reply\n"); + pdu->cb(rpc, RPC_STATUS_ERROR, "Failed to allocate buffer for decoding of XDR reply", pdu->private_data); + return 0; + } + bzero(pdu->xdr_decode_buf, pdu->xdr_decode_bufsize); + } + msg.acpted_rply.ar_results.where = pdu->xdr_decode_buf; + msg.acpted_rply.ar_results.proc = pdu->xdr_decode_fn; + + if (xdr_replymsg(xdr, &msg) == 0) { + printf("xdr_replymsg failed in portmap_getport_reply\n"); + pdu->cb(rpc, RPC_STATUS_ERROR, "Message rejected by server", pdu->private_data); + if (pdu->xdr_decode_buf != NULL) { + free(pdu->xdr_decode_buf); + pdu->xdr_decode_buf = NULL; + } + return 0; + } + if (msg.rm_reply.rp_stat != MSG_ACCEPTED) { + pdu->cb(rpc, RPC_STATUS_ERROR, "RPC Packet not accepted by the server", pdu->private_data); + return 0; + } + switch (msg.rm_reply.rp_acpt.ar_stat) { + case SUCCESS: + pdu->cb(rpc, RPC_STATUS_SUCCESS, pdu->xdr_decode_buf, pdu->private_data); + break; + case PROG_UNAVAIL: + pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Program not available", pdu->private_data); + break; + case PROG_MISMATCH: + pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Program version mismatch", pdu->private_data); + break; + case PROC_UNAVAIL: + pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Procedure not available", pdu->private_data); + break; + case GARBAGE_ARGS: + pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Garbage arguments", pdu->private_data); + break; + case SYSTEM_ERR: + pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: System Error", pdu->private_data); + break; + default: + pdu->cb(rpc, RPC_STATUS_ERROR, "Unknown rpc response from server", pdu->private_data); + break; + } + + return 0; +} + +int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size) +{ + struct rpc_pdu *pdu; + XDR xdr; + int pos, recordmarker; + unsigned int xid; + + bzero(&xdr, sizeof(XDR)); + + xdrmem_create(&xdr, buf, size, XDR_DECODE); + if (xdr_int(&xdr, &recordmarker) == 0) { + printf("xdr_int reading recordmarker failed\n"); + xdr_destroy(&xdr); + return -1; + } + pos = xdr_getpos(&xdr); + if (xdr_int(&xdr, (int *)&xid) == 0) { + printf("xdr_int reading xid failed\n"); + xdr_destroy(&xdr); + return -1; + } + xdr_setpos(&xdr, pos); + + for (pdu=rpc->waitpdu; pdu; pdu=pdu->next) { + if (pdu->xid != xid) { + continue; + } + DLIST_REMOVE(rpc->waitpdu, pdu); + if (rpc_process_reply(rpc, pdu, &xdr) != 0) { + printf("rpc_procdess_reply failed\n"); + } + xdr_destroy(&xdr); + rpc_free_pdu(rpc, pdu); + return 0; + } + printf("No matching pdu found for xid:%d\n", xid); + xdr_destroy(&xdr); + return -1; +} + diff --git a/ccan/nfs/portmap.c b/ccan/nfs/portmap.c new file mode 100644 index 00000000..269aded6 --- /dev/null +++ b/ccan/nfs/portmap.c @@ -0,0 +1,73 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-private.h" +#include "libnfs-raw-portmap.h" + + +int rpc_pmap_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data) +{ + struct rpc_pdu *pdu; + + pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_NULL, cb, private_data, (xdrproc_t)xdr_void, 0); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for portmap/null call"); + return -1; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + rpc_set_error(rpc, "Out of memory. Failed to queue pdu for portmap/null call"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} + +int rpc_pmap_getport_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data) +{ + struct rpc_pdu *pdu; + struct mapping m; + + pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_GETPORT, cb, private_data, (xdrproc_t)xdr_int, sizeof(uint32_t)); + if (pdu == NULL) { + rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for portmap/getport call"); + return -1; + } + + m.prog = program; + m.vers = version; + m.prot = IPPROTO_TCP; + m.port = 0; + if (xdr_mapping(&pdu->xdr, &m) == 0) { + rpc_set_error(rpc, "XDR error: Failed to encode data for portmap/getport call"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + if (rpc_queue_pdu(rpc, pdu) != 0) { + printf("Failed to queue portmap/getport pdu\n"); + rpc_free_pdu(rpc, pdu); + return -2; + } + + return 0; +} diff --git a/ccan/nfs/portmap.x b/ccan/nfs/portmap.x new file mode 100644 index 00000000..8921b92d --- /dev/null +++ b/ccan/nfs/portmap.x @@ -0,0 +1,37 @@ +/* + * From RFC1833 + */ + +const PMAP_PORT = 111; /* portmapper port number */ + +struct mapping { + unsigned int prog; + unsigned int vers; + unsigned int prot; + unsigned int port; +}; + +struct call_args { + unsigned int prog; + unsigned int vers; + unsigned int proc; + opaque args<>; +}; + + +program PMAP_PROGRAM { + version PMAP_V2 { + void + PMAP_NULL(void) = 0; + + bool + PMAP_SET(mapping) = 1; + + bool + PMAP_UNSET(mapping) = 2; + + unsigned int + PMAP_GETPORT(mapping) = 3; + } = 2; +} = 100000; + diff --git a/ccan/nfs/socket.c b/ccan/nfs/socket.c new file mode 100644 index 00000000..b1d0f292 --- /dev/null +++ b/ccan/nfs/socket.c @@ -0,0 +1,300 @@ +/* + Copyright (C) by Ronnie Sahlberg 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libnfs.h" +#include "libnfs-raw.h" +#include "libnfs-private.h" +#include "dlinklist.h" + +static void set_nonblocking(int fd) +{ + unsigned v; + v = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, v | O_NONBLOCK); +} + +int rpc_get_fd(struct rpc_context *rpc) +{ + return rpc->fd; +} + +int rpc_which_events(struct rpc_context *rpc) +{ + int events = POLLIN; + + if (rpc->is_connected == 0) { + events |= POLLOUT; + } + + if (rpc->outqueue) { + events |= POLLOUT; + } + return events; +} + +static int rpc_write_to_socket(struct rpc_context *rpc) +{ + ssize_t count; + + if (rpc == NULL) { + printf("trying to write to socket for NULL context\n"); + return -1; + } + if (rpc->fd == -1) { + printf("trying to write but not connected\n"); + return -2; + } + + while (rpc->outqueue != NULL) { + ssize_t total; + + total = rpc->outqueue->outdata.size; + + count = write(rpc->fd, rpc->outqueue->outdata.data + rpc->outqueue->written, total - rpc->outqueue->written); + if (count == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + printf("socket would block, return from write to socket\n"); + return 0; + } + printf("Error when writing to socket :%s(%d)\n", strerror(errno), errno); + return -3; + } + + rpc->outqueue->written += count; + if (rpc->outqueue->written == total) { + struct rpc_pdu *pdu = rpc->outqueue; + + DLIST_REMOVE(rpc->outqueue, pdu); + DLIST_ADD_END(rpc->waitpdu, pdu, NULL); + } + } + return 0; +} + +static int rpc_read_from_socket(struct rpc_context *rpc) +{ + int available; + int size; + unsigned char *buf; + ssize_t count; + + if (ioctl(rpc->fd, FIONREAD, &available) != 0) { + rpc_set_error(rpc, "Ioctl FIONREAD returned error : %d. Closing socket.", errno); + return -1; + } + if (available == 0) { + rpc_set_error(rpc, "Socket has been closed"); + return -2; + } + size = rpc->insize - rpc->inpos + available; + buf = malloc(size); + if (buf == NULL) { + rpc_set_error(rpc, "Out of memory: failed to allocate %d bytes for input buffer. Closing socket.", size); + return -3; + } + if (rpc->insize > rpc->inpos) { + memcpy(buf, rpc->inbuf + rpc->inpos, rpc->insize - rpc->inpos); + rpc->insize -= rpc->inpos; + rpc->inpos = 0; + } + + count = read(rpc->fd, buf + rpc->insize, available); + if (count == -1) { + if (errno == EINTR) { + free(buf); + buf = NULL; + return 0; + } + rpc_set_error(rpc, "Read from socket failed, errno:%d. Closing socket.", errno); + free(buf); + buf = NULL; + return -4; + } + + if (rpc->inbuf != NULL) { + free(rpc->inbuf); + } + rpc->inbuf = (char *)buf; + rpc->insize += count; + + while (1) { + if (rpc->insize - rpc->inpos < 4) { + return 0; + } + count = rpc_get_pdu_size(rpc->inbuf + rpc->inpos); + if (rpc->insize + rpc->inpos < count) { + return 0; + } + if (rpc_process_pdu(rpc, rpc->inbuf + rpc->inpos, count) != 0) { + rpc_set_error(rpc, "Invalid/garbage pdu received from server. Closing socket"); + return -5; + } + rpc->inpos += count; + if (rpc->inpos == rpc->insize) { + free(rpc->inbuf); + rpc->inbuf = NULL; + rpc->insize = 0; + rpc->inpos = 0; + } + } + return 0; +} + + + +int rpc_service(struct rpc_context *rpc, int revents) +{ + if (revents & POLLERR) { + printf("rpc_service: POLLERR, socket error\n"); + if (rpc->is_connected == 0) { + rpc_set_error(rpc, "Failed to connect to server socket."); + } else { + rpc_set_error(rpc, "Socket closed with POLLERR"); + } + rpc->connect_cb(rpc, RPC_STATUS_ERROR, rpc->error_string, rpc->connect_data); + return -1; + } + if (revents & POLLHUP) { + printf("rpc_service: POLLHUP, socket error\n"); + rpc_set_error(rpc, "Socket failed with POLLHUP"); + rpc->connect_cb(rpc, RPC_STATUS_ERROR, rpc->error_string, rpc->connect_data); + return -2; + } + + if (rpc->is_connected == 0 && rpc->fd != -1 && revents&POLLOUT) { + rpc->is_connected = 1; + rpc->connect_cb(rpc, RPC_STATUS_SUCCESS, NULL, rpc->connect_data); + return 0; + } + + if (revents & POLLOUT && rpc->outqueue != NULL) { + if (rpc_write_to_socket(rpc) != 0) { + printf("write to socket failed\n"); + return -3; + } + } + + if (revents & POLLIN) { + if (rpc_read_from_socket(rpc) != 0) { + rpc_disconnect(rpc, rpc_get_error(rpc)); + return -4; + } + } + + return 0; +} + + +int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, int use_privileged_port, rpc_cb cb, void *private_data) +{ + struct sockaddr_storage s; + struct sockaddr_in *sin = (struct sockaddr_in *)&s; + int socksize; + + if (rpc->fd != -1) { + rpc_set_error(rpc, "Trying to connect while already connected"); + printf("%s\n", rpc->error_string); + return -1; + } + + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + if (inet_pton(AF_INET, server, &sin->sin_addr) != 1) { + rpc_set_error(rpc, "Not a valid server ip address"); + printf("%s\n", rpc->error_string); + return -2; + } + + switch (s.ss_family) { + case AF_INET: + rpc->fd = socket(AF_INET, SOCK_STREAM, 0); + socksize = sizeof(struct sockaddr_in); + break; + } + + if (rpc->fd == -1) { + rpc_set_error(rpc, "Failed to open socket"); + printf("%s\n", rpc->error_string); + return -3; + } + + /* if we are root, try to find a privileged port to use (512 - 1023) */ + if (geteuid() == 0 && use_privileged_port != 0) { + struct sockaddr_storage ls; + int ret, count = 0; + static int local_port = 0; + + if (local_port == 0) { + srandom(getpid() ^ time(NULL)); + local_port = random()%512 + 512; + } + + do { + count ++; + if (local_port >= 1024) { + local_port = 512; + } + switch (s.ss_family) { + case AF_INET: + bzero(&ls, socksize); + ((struct sockaddr_in *)&ls)->sin_family = AF_INET; + ((struct sockaddr_in *)&ls)->sin_addr.s_addr = INADDR_ANY; + ((struct sockaddr_in *)&ls)->sin_port = htons(local_port++); + break; + } + + ret = bind(rpc->fd, (struct sockaddr *)&ls, socksize); + } while (ret != 0 && count < 50); + } + + rpc->connect_cb = cb; + rpc->connect_data = private_data; + + set_nonblocking(rpc->fd); + + if (connect(rpc->fd, (struct sockaddr *)&s, socksize) != 0 && errno != EINPROGRESS) { + rpc_set_error(rpc, "connect() to server failed"); + printf("%s\n", rpc->error_string); + return -4; + } + + return 0; +} + +int rpc_disconnect(struct rpc_context *rpc, char *error) +{ + if (rpc->fd != -1) { + close(rpc->fd); + } + rpc->fd = -1; + + rpc->is_connected = 0; + + rpc_error_all_pdus(rpc, error); + + return 0; +} -- 2.39.2