initial libnfs checkin
authorRonnie Sahlberg <ronniesahlberg@gmail.com>
Sun, 6 Feb 2011 04:45:09 +0000 (15:45 +1100)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Sun, 6 Feb 2011 04:45:09 +0000 (15:45 +1100)
24 files changed:
COPYING [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
config.h.in [new file with mode: 0644]
configure.ac [new file with mode: 0644]
examples/nfsclient-async.c [new file with mode: 0644]
examples/nfsclient-raw.c [new file with mode: 0644]
examples/nfsclient-sync.c [new file with mode: 0644]
include/libnfs-private.h [new file with mode: 0644]
include/libnfs-raw.h [new file with mode: 0644]
include/libnfs.h [new file with mode: 0644]
include/slist.h [new file with mode: 0644]
lib/init.c [new file with mode: 0644]
lib/libnfs-sync.c [new file with mode: 0644]
lib/libnfs.c [new file with mode: 0644]
lib/pdu.c [new file with mode: 0644]
lib/socket.c [new file with mode: 0644]
mount/mount.c [new file with mode: 0644]
mount/mount.x [new file with mode: 0644]
nfs/nfs.c [new file with mode: 0644]
nfs/nfs.x [new file with mode: 0644]
portmap/portmap.c [new file with mode: 0644]
portmap/portmap.x [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..93c1e92
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,20 @@
+Libnfs components fall under two separate licences
+
+
+The lib and include directories
+===============================
+The nfs client library itself, i.e. the lib and include directories,
+is licenced under GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+The src and examples directories
+================================
+The utility and example applications using this library, i.e. the src and the
+examples directories, are licenced under 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.
+
+
+To avoid any confusion, every source file contains a licence boilerplate.
+
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..278873b
--- /dev/null
@@ -0,0 +1,111 @@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+libdir = @libdir@
+bindir = @bindir@
+LIBS=@LIBS@
+
+INSTALLCMD = @install@ -c
+
+CC=gcc
+CFLAGS=-g -O0 -fPIC -Wall -W -I. -Iinclude -Imount -Infs -Iportmap "-D_U_=__attribute__((unused))" -D_FILE_OFFSET_BITS=64
+
+LIBNFS_A=lib/libnfs.a
+LIBNFS_SO_NAME=libnfs.so.1
+VERSION=1.0.0
+LIBNFS_SO=lib/libnfs.so.$(VERSION)
+
+LIB_OBJS = lib/pdu.o lib/init.o lib/socket.o \
+lib/libnfs.o lib/libnfs-sync.o
+
+LIBNFS_OBJS = \
+mount/libnfs-raw-mount.o mount/mount.o \
+portmap/libnfs-raw-portmap.o portmap/portmap.o \
+nfs/libnfs-raw-nfs.o nfs/nfs.o
+
+EXAMPLES=bin/nfsclient-raw bin/nfsclient-async bin/nfsclient-sync
+
+all: $(LIBNFS_SO) $(EXAMPLES)
+
+bin/nfsclient-async: examples/nfsclient-async.c $(LIBNFS_A)
+       mkdir -p bin
+       $(CC) $(CFLAGS) -o $@ examples/nfsclient-async.c $(LIBNFS_A) $(LIBS)
+
+bin/nfsclient-sync: examples/nfsclient-sync.c $(LIBNFS_A)
+       mkdir -p bin
+       $(CC) $(CFLAGS) -o $@ examples/nfsclient-sync.c $(LIBNFS_A) $(LIBS)
+
+bin/nfsclient-raw: examples/nfsclient-raw.c $(LIBNFS_A)
+       mkdir -p bin
+       $(CC) $(CFLAGS) -o $@ examples/nfsclient-raw.c $(LIBNFS_A) $(LIBS)
+
+$(LIBNFS_A): $(LIBNFS_OBJS) $(LIB_OBJS)
+       @echo Creating library $@
+       ar r $(LIBNFS_A) $(LIBNFS_OBJS) $(LIB_OBJS)
+       ranlib $(LIBNFS_A)
+
+$(LIBNFS_SO): $(LIBNFS_OBJS) $(LIB_OBJS)
+       @echo Creating shared library $@
+       $(CC) -shared -Wl,-soname=$(LIBNFS_SO_NAME) -o $@ $(LIBNFS_OBJS) $(LIB_OBJS)
+
+portmap/libnfs-raw-portmap.h: portmap/portmap.x
+       @echo Generating $@
+       rpcgen -h portmap/portmap.x > portmap/libnfs-raw-portmap.h
+
+portmap/libnfs-raw-portmap.c: portmap/portmap.x
+       @echo Generating $@
+       rpcgen -c portmap/portmap.x | sed -e "s/#include \"portmap\/portmap.h\"/#include \"libnfs-raw-portmap.h\"/" > portmap/libnfs-raw-portmap.c
+
+portmap/libnfs-raw-portmap.o: portmap/libnfs-raw-portmap.c portmap/libnfs-raw-portmap.h
+       @echo Compiling $@
+       $(CC) $(CFLAGS) -c portmap/libnfs-raw-portmap.c -o $@
+
+mount/libnfs-raw-mount.h: mount/mount.x
+       @echo Generating $@
+       rpcgen -h mount/mount.x > mount/libnfs-raw-mount.h
+
+mount/libnfs-raw-mount.c: mount/mount.x
+       @echo Generating $@
+       rpcgen -c mount/mount.x | sed -e "s/#include \"mount\/mount.h\"/#include \"libnfs-raw-mount.h\"/" > mount/libnfs-raw-mount.c
+
+mount/libnfs-raw-mount.o: mount/libnfs-raw-mount.c mount/libnfs-raw-mount.h
+       @echo Compiling $@
+       $(CC) $(CFLAGS) -c mount/libnfs-raw-mount.c -o $@
+
+nfs/libnfs-raw-nfs.h: nfs/nfs.x
+       @echo Generating $@
+       rpcgen -h nfs/nfs.x > nfs/libnfs-raw-nfs.h
+
+nfs/libnfs-raw-nfs.c: nfs/nfs.x
+       @echo Generating $@
+       rpcgen -c nfs/nfs.x | sed -e "s/#include \"nfs\/nfs.h\"/#include \"libnfs-raw-nfs.h\"/" > nfs/libnfs-raw-nfs.c
+
+nfs/libnfs-raw-nfs.o: nfs/libnfs-raw-nfs.c nfs/libnfs-raw-nfs.h
+       @echo Compiling $@
+       $(CC) $(CFLAGS) -c nfs/libnfs-raw-nfs.c -o $@
+
+install: $(LIBNFS_A) $(LIBNFS_SO)
+ifeq ("$(LIBDIR)x","x")
+       $(INSTALLCMD) -m 755 $(LIBNFS_SO) $(libdir)
+       $(INSTALLCMD) -m 755 $(LIBNFS_A) $(libdir)
+       @ldconfig@
+else
+       $(INSTALLCMD) -m 755 $(LIBISCSI_SO) $(LIBDIR)
+       $(INSTALLCMD) -m 755 $(LIBNFS_A) $(LIBDIR)
+endif
+       mkdir -p $(DESTDIR)/usr/include/nfsc
+       $(INSTALLCMD) -m 644 include/libnfs.h $(DESTDIR)/usr/include/nfsc
+       $(INSTALLCMD) -m 644 include/libnfs-private.h $(DESTDIR)/usr/include/nfsc
+       $(INSTALLCMD) -m 644 mount/libnfs-raw-mount.h $(DESTDIR)/usr/include/nfsc
+       $(INSTALLCMD) -m 644 nfs/libnfs-raw-nfs.h $(DESTDIR)/usr/include/nfsc
+       $(INSTALLCMD) -m 644 portmap/libnfs-raw-portmap.h $(DESTDIR)/usr/include/nfsc
+
+distclean: clean
+       rm -f config.h config.log config.status configure Makefile
+
+clean:
+       rm -f bin/* lib/*.o lib/*.a $(LIBNFS_SO)
+       rm -f mount/*.o mount/libnfs-raw-mount.h mount/libnfs-raw-mount.c
+       rm -f nfs/*.o nfs/libnfs-raw-nfs.h nfs/libnfs-raw-nfs.c
+       rm -f portmap/*.o portmap/libnfs-raw-portmap.h portmap/libnfs-raw-portmap.c
+       rm -f nfsclient-raw nfsclient-async nfsclient-sync
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..c02b8ec
--- /dev/null
+++ b/README
@@ -0,0 +1,24 @@
+LIBNFS is a client library for accessing NFS shares over a network.
+
+LIBNFS offers three different APIs, for different use :
+1, RAW : A fully async low level rpc library for nfs protocols
+This API is described in include/libnfs-raw.h
+it offers a fully async interface to raw XDR encoded blobs.
+This api provides very flexible and precice control of the RPC issued.
+
+examples/nfsclient-raw.c provides examples on how to use the raw API
+
+2, NFS ASYNC : A fully asynchronous library for high level vfs functions
+This API is described by the *_async() fucntions in include/libnfs.h.
+This API provides a fully async access to posix vfs like functions such as 
+stat(), read(), ...
+
+examples/nfsclient-async.c provides examples on how to use this API
+
+
+3, NFS SYNC : A synchronous library for high level vfs functions
+This API is described by the *_sync() fucntions in include/libnfs.h.
+This API provides access to posix vfs like functions such as 
+stat(), read(), ...
+
+examples/nfsclient-sync.c provides examples on how to use this API
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..579675a
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+rm -rf autom4te.cache
+rm -f configure config.h.in libnfs.pc
+
+IPATHS="-I ./include -I ../include -I ./include -I ./mount -I ./nfs -I ./portmap"
+
+autoheader $IPATHS || exit 1
+autoconf $IPATHS || exit 1
+
+rm -rf autom4te.cache
+
+echo "Now run ./configure and then make."
+exit 0
+
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..fbe4d82
--- /dev/null
@@ -0,0 +1,22 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Whether the sockaddr_in struct has a sin_len property */
+#undef HAVE_SOCK_SIN_LEN
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..8b99796
--- /dev/null
@@ -0,0 +1,105 @@
+AC_PREREQ(2.50)
+AC_INIT(libnfs, m4_esyscmd([grep 'Version:' ./packaging/RPM/libnfs.spec.in 2>/dev/null | head -1 | sed -e 's/[ \t]*Version:[ \t]*\([^ \t]*\)[ \t]*.*/\1/' | tr -d '\n']))
+AC_CONFIG_SRCDIR([lib/init.c])
+
+if test "${libdir}" = '${exec_prefix}/lib'; then
+  case `uname -m` in
+    x86_64|ppc64|powerpc64)
+      libdir='${exec_prefix}/lib64'
+      ;;
+    *)
+      libdir='${exec_prefix}/lib'
+      ;;
+  esac
+fi
+
+is_solaris="no"
+install="/usr/bin/install"
+ldconfig="ldconfig"
+
+case `uname` in
+  Linux*)
+    ;;
+  AIX*)
+    ;;
+  SunOS)
+    is_solaris="yes"
+    install="ginstall"
+    ldconfig="echo no ldconfig on solaris"
+    LIBS="$LIBS -lsocket -lnsl"
+    ;;
+  *)
+    ;;
+esac
+
+if test "$ac_cv_prog_gcc" = yes; then
+   CFLAGS="$CFLAGS -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings"
+fi
+
+LOGDIR='${localstatedir}/log'
+AC_ARG_WITH([logdir],
+       [  --with-logdir=DIR       path to log directory [[LOCALSTATEDIR/log]]],
+       LOGDIR=$withval)
+if test ! -z "$LOGDIR"; then
+  if test "$LOGDIR" = "yes" -o "$LOGDIR" = "no"; then
+    AC_MSG_ERROR([--with-logdir must specify a path])
+  fi
+fi
+AC_SUBST(LOGDIR)
+
+AC_CONFIG_HEADER(config.h)
+
+EXTRA_OBJ=""
+
+#AC_CHECK_HEADERS(sched.h)
+#AC_CHECK_FUNCS(mlockall)
+
+AC_CACHE_CHECK([for sin_len in sock],libnfs_cv_HAVE_SOCK_SIN_LEN,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>],
+[struct sockaddr_in sock; sock.sin_len = sizeof(sock);],
+libnfs_cv_HAVE_SOCK_SIN_LEN=yes,libnfs_cv_HAVE_SOCK_SIN_LEN=no)])
+if test x"$libnfs_cv_HAVE_SOCK_SIN_LEN" = x"yes"; then
+    AC_DEFINE(HAVE_SOCK_SIN_LEN,1,[Whether the sockaddr_in struct has a sin_len property])
+fi
+
+AC_MSG_CHECKING(whether libpopt is available)
+ac_save_CFLAGS="$CFLAGS"
+ac_save_LIBS="$LIBS"
+CFLAGS="$CFLAGS $GLIB_CFLAGS"
+LIBS="$GLIB_LIBS $LIBS -lpopt"
+AC_TRY_RUN([
+/*
+ * Just see if we can compile/link with popt
+ */
+#include <popt.h>
+
+int main(int argc, const char *argv[])
+{
+       struct poptOption popt_options[] = {
+               POPT_TABLEEND
+       };
+       poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
+
+       return 0;
+}
+], ac_cv_have_popt=yes, ac_cv_have_popt=no,
+   [echo $ac_n "compile with POPT. Assuming OK... $ac_c"
+    ac_cv_have_popt=yes])
+CFLAGS="$ac_save_CFLAGS"
+LIBS="$ac_save_LIBS"
+if test "$ac_cv_have_popt" = yes ; then
+  AC_MSG_RESULT(yes)
+else
+  AC_MSG_RESULT(no)
+  AC_MSG_NOTICE(You need libpopt to compile libnfs. Install the libpopt-dev package.)
+  exit
+fi
+
+AC_SUBST(libdir)
+AC_SUBST(install)
+AC_SUBST(ldconfig)
+#AC_SUBST(LIBNFS_LDFLAGS)
+
+AC_OUTPUT(Makefile)
diff --git a/examples/nfsclient-async.c b/examples/nfsclient-async.c
new file mode 100644 (file)
index 0000000..4abb901
--- /dev/null
@@ -0,0 +1,233 @@
+/* 
+   Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#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/examples/nfsclient-raw.c b/examples/nfsclient-raw.c
new file mode 100644 (file)
index 0000000..9de1ea6
--- /dev/null
@@ -0,0 +1,213 @@
+/* 
+   Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <poll.h>
+#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)
+{
+       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);
+       }
+
+       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, 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, 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/examples/nfsclient-sync.c b/examples/nfsclient-sync.c
new file mode 100644 (file)
index 0000000..2f38395
--- /dev/null
@@ -0,0 +1,202 @@
+/* 
+   Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "libnfs.h"
+#include <rpc/rpc.h>            /* 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");
+
+
+       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/include/libnfs-private.h b/include/libnfs-private.h
new file mode 100644 (file)
index 0000000..d54e5a8
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include <rpc/auth.h>
+
+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 *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/include/libnfs-raw.h b/include/libnfs-raw.h
new file mode 100644 (file)
index 0000000..f3a413f
--- /dev/null
@@ -0,0 +1,536 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ * 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 <stdint.h>
+
+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, 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 union 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);
+
+
+
diff --git a/include/libnfs.h b/include/libnfs.h
new file mode 100644 (file)
index 0000000..2debb8b
--- /dev/null
@@ -0,0 +1,905 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ * This is the highlevel interface to access NFS resources using a posix-like interface
+ */
+#include <stdint.h>
+
+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 c, the context.
+ * Function returns
+ *  NULL : Failed to create a context.
+ *  *nfs : 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);
+
+
+struct nfsfh;
+
+
+/*
+ * MOUNT THE EXPORT
+ */
+/*
+ * Async nfs mount.
+ * 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(<filename>)
+ * 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(<filename>)
+ * 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(<filename>)
+ *
+ * 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 stat(<filename>)
+ * 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(<dirname>)
+ * 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(<dirname>)
+ * 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(<name>)
+ * 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(<name>)
+ * 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(<name>)
+ * 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(<name>)
+ * 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(<handle>)
+ * 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(<handle>)
+ * 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(<name>)
+ * 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(<name>)
+ * 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(<handle>)
+ * 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(<handle>)
+ * 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(<path>)
+ * 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(<path>)
+ * 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(<path>)
+ * 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(<path>)
+ * 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(<path>)
+ * 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(<path>)
+ * 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(<path>)
+ * 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(<path>)
+ * 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(<oldpath>, <newpath>)
+ * 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(<oldpath>, <newpath>)
+ * 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(<oldpath>, <newpath>)
+ * 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(<oldpath>, <newpath>)
+ * 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/include/slist.h b/include/slist.h
new file mode 100644 (file)
index 0000000..56c8364
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define SLIST_ADD(list, item) \
+       do {                                                    \
+               (item)->next = (*list);                         \
+               (*list) = (item);                               \
+       } while (0);
+
+#define SLIST_ADD_END(list, item)                              \
+       if ((*list) == NULL) {                                  \
+          SLIST_ADD((list), (item));                           \
+       } else {                                                \
+          void *head = (*list);                                \
+          while ((*list)->next)                                \
+            (*list) = (*list)->next;                           \
+          (*list)->next = (item);                              \
+          (item)->next = NULL;                                 \
+          (*list) = head;                                      \
+       }
+
+#define SLIST_REMOVE(list, item) \
+       if ((*list) == (item)) {                                \
+          (*list) = (item)->next;                              \
+       } else {                                                \
+          void *head = (*list);                                \
+          while ((*list)->next && (*list)->next != (item))     \
+            (*list) = (*list)->next;                           \
+          if ((*list)->next != NULL) {                         \
+             (*list)->next = (*list)->next->next;              \
+          }                                                    \
+          (*list) = head;                                      \
+       }
+
+
+
+
diff --git a/lib/init.c b/lib/init.c
new file mode 100644 (file)
index 0000000..fb2ae83
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <rpc/xdr.h>
+#include "slist.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);
+               SLIST_REMOVE(&rpc->outqueue, pdu);
+               rpc_free_pdu(rpc, pdu);
+       }
+       while((pdu = rpc->waitpdu) != NULL) {
+               pdu->cb(rpc, RPC_STATUS_ERROR, error, pdu->private_data);
+               SLIST_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);
+               SLIST_REMOVE(&rpc->outqueue, pdu);
+               rpc_free_pdu(rpc, pdu);
+       }
+       while((pdu = rpc->waitpdu) != NULL) {
+               pdu->cb(rpc, RPC_STATUS_CANCEL, NULL, pdu->private_data);
+               SLIST_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/lib/libnfs-sync.c b/lib/libnfs-sync.c
new file mode 100644 (file)
index 0000000..0dc6253
--- /dev/null
@@ -0,0 +1,1069 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ * High level api to nfs filesystems
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <poll.h>
+#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/lib/libnfs.c b/lib/libnfs.c
new file mode 100644 (file)
index 0000000..f4e8b01
--- /dev/null
@@ -0,0 +1,2705 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ * High level api to nfs filesystems
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <utime.h>
+#include <unistd.h>
+#include <fcntl.h>
+#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;
+};
+
+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_9_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_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_nfs_getattr_async(rpc, nfs_mount_9_cb, &nfs->rootfh, 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, 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, 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, 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/lib/pdu.c b/lib/pdu.c
new file mode 100644 (file)
index 0000000..238d71d
--- /dev/null
+++ b/lib/pdu.c
@@ -0,0 +1,226 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <strings.h>
+#include <rpc/xdr.h>
+#include <rpc/rpc_msg.h>
+#include "slist.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);
+       SLIST_ADD_END(&rpc->outqueue, pdu);
+
+       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;
+               }
+               SLIST_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/lib/socket.c b/lib/socket.c
new file mode 100644 (file)
index 0000000..dd4ffc4
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <errno.h>
+#include <rpc/xdr.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include "libnfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-private.h"
+#include "slist.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;
+
+                       SLIST_REMOVE(&rpc->outqueue, pdu);
+                       SLIST_ADD_END(&rpc->waitpdu, pdu);
+               }
+       }
+       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, 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;
+       }
+
+       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;
+}
diff --git a/mount/mount.c b/mount/mount.c
new file mode 100644 (file)
index 0000000..447052d
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <errno.h>
+#include <rpc/xdr.h>
+#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/mount/mount.x b/mount/mount.x
new file mode 100644 (file)
index 0000000..6c2f071
--- /dev/null
@@ -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<FHSIZE3>;
+typedef string dirpath<MNTPATHLEN>;
+typedef string name<MNTNAMLEN>;
+
+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/nfs/nfs.c b/nfs/nfs.c
new file mode 100644 (file)
index 0000000..34c0859
--- /dev/null
+++ b/nfs/nfs.c
@@ -0,0 +1,680 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <rpc/xdr.h>
+#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/nfs/nfs.x b/nfs/nfs.x
new file mode 100644 (file)
index 0000000..58fc34e
--- /dev/null
+++ b/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<NFS3_FHSIZE>;
+};
+
+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/portmap/portmap.c b/portmap/portmap.c
new file mode 100644 (file)
index 0000000..4c10bd8
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <rpc/xdr.h>
+#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/portmap/portmap.x b/portmap/portmap.x
new file mode 100644 (file)
index 0000000..8921b92
--- /dev/null
@@ -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;
+