Merge branch 'ronnie'
authorRusty Russell <rusty@rustcorp.com.au>
Wed, 10 Nov 2010 05:17:12 +0000 (15:47 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Wed, 10 Nov 2010 05:17:12 +0000 (15:47 +1030)
69 files changed:
ccan/alloc/alloc.c
ccan/compiler/_info
ccan/compiler/compiler.h
ccan/compiler/test/compile_fail-printf.c
ccan/htable/htable.c
ccan/ilog/ilog.c
ccan/ilog/ilog.h
ccan/iscsi/LICENCE [new symlink]
ccan/iscsi/Makefile [new file with mode: 0644]
ccan/iscsi/_info [new file with mode: 0644]
ccan/iscsi/discovery.c [new file with mode: 0644]
ccan/iscsi/dlinklist.h [new file with mode: 0644]
ccan/iscsi/init.c [new file with mode: 0644]
ccan/iscsi/iscsi-private.h [new file with mode: 0644]
ccan/iscsi/iscsi.h [new file with mode: 0644]
ccan/iscsi/login.c [new file with mode: 0644]
ccan/iscsi/nop.c [new file with mode: 0644]
ccan/iscsi/pdu.c [new file with mode: 0644]
ccan/iscsi/scsi-command.c [new file with mode: 0644]
ccan/iscsi/scsi-lowlevel.c [new file with mode: 0644]
ccan/iscsi/scsi-lowlevel.h [new file with mode: 0644]
ccan/iscsi/socket.c [new file with mode: 0644]
ccan/iscsi/test/run.c [new file with mode: 0644]
ccan/iscsi/tools/iscsiclient.c [new file with mode: 0644]
ccan/jbitset/jbitset.h
ccan/jmap/jmap.h
ccan/likely/likely.h
ccan/nfs/Makefile [new file with mode: 0644]
ccan/nfs/dlinklist.h [new file with mode: 0644]
ccan/nfs/init.c [new file with mode: 0644]
ccan/nfs/libnfs-private.h [new file with mode: 0644]
ccan/nfs/libnfs-raw-mount.c [new file with mode: 0644]
ccan/nfs/libnfs-raw-nfs.c [new file with mode: 0644]
ccan/nfs/libnfs-raw-nfsacl.c [new file with mode: 0644]
ccan/nfs/libnfs-raw-portmap.c [new file with mode: 0644]
ccan/nfs/libnfs-raw.h [new file with mode: 0644]
ccan/nfs/libnfs-sync.c [new file with mode: 0644]
ccan/nfs/libnfs.c [new file with mode: 0644]
ccan/nfs/mount.c [new file with mode: 0644]
ccan/nfs/nfs.c [new file with mode: 0644]
ccan/nfs/nfs.h [new file with mode: 0644]
ccan/nfs/nfsacl.c [new file with mode: 0644]
ccan/nfs/pdu.c [new file with mode: 0644]
ccan/nfs/portmap.c [new file with mode: 0644]
ccan/nfs/rpc/mount.h [new file with mode: 0644]
ccan/nfs/rpc/mount.x [new file with mode: 0644]
ccan/nfs/rpc/nfs.h [new file with mode: 0644]
ccan/nfs/rpc/nfs.x [new file with mode: 0755]
ccan/nfs/rpc/nfsacl.h [new file with mode: 0644]
ccan/nfs/rpc/nfsacl.x [new file with mode: 0644]
ccan/nfs/rpc/portmap.h [new file with mode: 0644]
ccan/nfs/rpc/portmap.x [new file with mode: 0644]
ccan/nfs/socket.c [new file with mode: 0644]
ccan/nfs/tools/nfsclient-async.c [new file with mode: 0644]
ccan/nfs/tools/nfsclient-raw.c [new file with mode: 0644]
ccan/nfs/tools/nfsclient-sync.c [new file with mode: 0644]
ccan/rbtree/_info [new file with mode: 0644]
ccan/rbtree/rbtree.c [new file with mode: 0644]
ccan/rbtree/rbtree.h [new file with mode: 0644]
ccan/rbtree/test/run.c [new file with mode: 0644]
ccan/talloc/talloc.c
ccan/talloc/talloc.h
ccan/tap/tap.h
ccan/tdb/open.c
ccan/tdb/tdb.h
ccan/tdb/tools/tdbtorture.c
ccan/tdb2/tdb.c
ccan/tdb2/tdb2.h
ccan/tdb2/tools/tdbtorture.c

index 643efedc85d1be3212e175051e3558def8021545..64475078c0a6811f88b5dcf9861492a117a2ff41 100644 (file)
@@ -527,9 +527,8 @@ static bool huge_allocated(struct header *head, unsigned long offset)
 }
 
 /* They want something really big.  Aim for contiguous pages (slow). */
-static COLD_ATTRIBUTE
-void *huge_alloc(void *pool, unsigned long poolsize,
-                unsigned long size, unsigned long align)
+static COLD void *huge_alloc(void *pool, unsigned long poolsize,
+                            unsigned long size, unsigned long align)
 {
        struct header *head = pool;
        struct huge_alloc *ha;
@@ -647,7 +646,7 @@ done:
        return (char *)pool + ha->off;
 }
 
-static COLD_ATTRIBUTE void
+static COLD void
 huge_free(struct header *head, unsigned long poolsize, void *free)
 {
        unsigned long i, off, pgnum, free_off = (char *)free - (char *)head;
@@ -687,8 +686,7 @@ huge_free(struct header *head, unsigned long poolsize, void *free)
        alloc_free(head, poolsize, ha);
 }
 
-static COLD_ATTRIBUTE unsigned long
-huge_size(struct header *head, void *p)
+static COLD unsigned long huge_size(struct header *head, void *p)
 {
        unsigned long i, off = (char *)p - (char *)head;
        struct huge_alloc *ha;
index 31f6f0c7ce482cadbc6644e0f60af742da23bd37..c55ba22f086c320de70b4c5d21a0b56da825e16f 100644 (file)
@@ -6,16 +6,18 @@
  * compiler - macros for common compiler extensions
  *
  * Abstracts away some compiler hints.  Currently these include:
- * - COLD_ATTRIBUTE
+ * - COLD
  *     For functions not called in fast paths (aka. cold functions)
- * - PRINTF_ATTRIBUTE
+ * - PRINTF_FMT
  *     For functions which take printf-style parameters.
- * - IDEMPOTENT_ATTRIBUTE
+ * - IDEMPOTENT
  *     For functions which return the same value for same parameters.
- * - NEEDED_ATTRIBUTE
+ * - NEEDED
  *     For functions and variables which must be emitted even if unused.
- * - UNNEEDED_ATTRIBUTE
+ * - UNNEEDED
  *     For functions and variables which need not be emitted if unused.
+ * - UNUSED
+ *     For parameters which are not used.
  * - IS_COMPILE_CONSTANT
  *     For using different tradeoffs for compiletime vs runtime evaluation.
  *
@@ -29,7 +31,7 @@
  *
  *     // Example of a (slow-path) logging function.
  *     static int log_threshold = 2;
- *     static void COLD_ATTRIBUTE PRINTF_ATTRIBUTE(2,3)
+ *     static void COLD PRINTF_FMT(2,3)
  *             logger(int level, const char *fmt, ...)
  *     {
  *             va_list ap;
index 49088ac799e6f0ba9772afde55d8a9fdd421f71d..d57d814b3711213c978602cd46c7c6e2e67ccf7d 100644 (file)
 
 #if HAVE_ATTRIBUTE_COLD
 /**
- * COLD_ATTRIBUTE - a function is unlikely to be called.
+ * COLD - a function is unlikely to be called.
  *
  * Used to mark an unlikely code path and optimize appropriately.
  * It is usually used on logging or error routines.
  *
  * Example:
- * static void COLD_ATTRIBUTE moan(const char *reason)
+ * static void COLD moan(const char *reason)
  * {
  *     fprintf(stderr, "Error: %s (%s)\n", reason, strerror(errno));
  * }
  */
-#define COLD_ATTRIBUTE __attribute__((cold))
+#define COLD __attribute__((cold))
 #else
-#define COLD_ATTRIBUTE
+#define COLD
 #endif
 
 #if HAVE_ATTRIBUTE_PRINTF
 /**
- * PRINTF_ATTRIBUTE - a function takes printf-style arguments
+ * PRINTF_FMT - a function takes printf-style arguments
  * @nfmt: the 1-based number of the function's format argument.
  * @narg: the 1-based number of the function's first variable argument.
  *
  * This allows the compiler to check your parameters as it does for printf().
  *
  * Example:
- * void PRINTF_ATTRIBUTE(2,3) my_printf(const char *prefix,
- *                                     const char *fmt, ...);
+ * void PRINTF_FMT(2,3) my_printf(const char *prefix, const char *fmt, ...);
  */
-#define PRINTF_ATTRIBUTE(nfmt, narg) \
+#define PRINTF_FMT(nfmt, narg) \
        __attribute__((format(__printf__, nfmt, narg)))
 #else
-#define PRINTF_ATTRIBUTE(nfmt, narg)
+#define PRINTF_FMT(nfmt, narg)
 #endif
 
 #if HAVE_ATTRIBUTE_CONST
 /**
- * IDEMPOTENT_ATTRIBUTE - a function's return depends only on its argument
+ * IDEMPOTENT - a function's return depends only on its argument
  *
  * This allows the compiler to assume that the function will return the exact
  * same value for the exact same arguments.  This implies that the function
  * must not use global variables, or dereference pointer arguments.
  */
-#define IDEMPOTENT_ATTRIBUTE __attribute__((const))
+#define IDEMPOTENT __attribute__((const))
 #else
-#define IDEMPOTENT_ATTRIBUTE
+#define IDEMPOTENT
 #endif
 
 #if HAVE_ATTRIBUTE_UNUSED
 /**
- * UNNEEDED_ATTRIBUTE - a parameter/variable/function may not be needed
+ * UNNEEDED - a variable/function may not be needed
  *
- * This suppresses warnings about unused variables or parameters, but tells
+ * This suppresses warnings about unused variables or functions, but tells
  * the compiler that if it is unused it need not emit it into the source code.
  *
  * Example:
  * // With some preprocessor options, this is unnecessary.
- * static UNNEEDED_ATTRIBUTE int counter;
+ * static UNNEEDED int counter;
  *
  * // With some preprocessor options, this is unnecessary.
- * static UNNEEDED_ATTRIBUTE void add_to_counter(int add)
+ * static UNNEEDED void add_to_counter(int add)
  * {
  *     counter += add;
  * }
  */
-#define UNNEEDED_ATTRIBUTE __attribute__((unused))
+#define UNNEEDED __attribute__((unused))
 
 #if HAVE_ATTRIBUTE_USED
 /**
- * NEEDED_ATTRIBUTE - a parameter/variable/function is needed
+ * NEEDED - a variable/function is needed
  *
- * This suppresses warnings about unused variables or parameters, but tells
+ * This suppresses warnings about unused variables or functions, but tells
  * the compiler that it must exist even if it (seems) unused.
  *
  * Example:
  *     // Even if this is unused, these are vital for debugging.
- *     static UNNEEDED_ATTRIBUTE int counter;
- *     static UNNEEDED_ATTRIBUTE void dump_counter(void)
+ *     static NEEDED int counter;
+ *     static NEEDED void dump_counter(void)
  *     {
  *             printf("Counter is %i\n", counter);
  *     }
  */
-#define NEEDED_ATTRIBUTE __attribute__((used))
+#define NEEDED __attribute__((used))
 #else
 /* Before used, unused functions and vars were always emitted. */
-#define NEEDED_ATTRIBUTE __attribute__((unused))
+#define NEEDED __attribute__((unused))
 #endif
+
+/**
+ * UNUSED - a parameter is unused
+ *
+ * Some compilers (eg. gcc with -W or -Wunused) warn about unused
+ * function parameters.  This suppresses such warnings and indicates
+ * to the reader that it's deliberate.
+ *
+ * Example:
+ *     // This is used as a callback, so needs to have this prototype.
+ *     static int some_callback(void *unused UNUSED)
+ *     {
+ *             return 0;
+ *     }
+ */
+#define UNUSED __attribute__((unused))
 #else
-#define UNNEEDED_ATTRIBUTE
-#define NEEDED_ATTRIBUTE
+#define UNNEEDED
+#define NEEDED
+#define UNUSED
 #endif
 
 #if HAVE_BUILTIN_CONSTANT_P
index 670126bba1b7ee7093e6ee925fe827073be1e533..8f34ae5a1255b25f89aa0ae3424c9c47ef750cee 100644 (file)
@@ -1,6 +1,6 @@
 #include <ccan/compiler/compiler.h>
 
-static void PRINTF_ATTRIBUTE(2,3) my_printf(int x, const char *fmt, ...)
+static void PRINTF_FMT(2,3) my_printf(int x, const char *fmt, ...)
 {
 }
 
index 788e71895b183db45768fd0327572dcf5ccf4fd5..a15c54d7958a6b779c7e3b7c7b951eb360ef0409 100644 (file)
@@ -155,7 +155,7 @@ static void ht_add(struct htable *ht, const void *new, size_t h)
        ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect);
 }
 
-static COLD_ATTRIBUTE bool double_table(struct htable *ht)
+static COLD bool double_table(struct htable *ht)
 {
        unsigned int i;
        size_t oldnum = (size_t)1 << ht->bits;
@@ -192,7 +192,7 @@ static COLD_ATTRIBUTE bool double_table(struct htable *ht)
        return true;
 }
 
-static COLD_ATTRIBUTE void rehash_table(struct htable *ht)
+static COLD void rehash_table(struct htable *ht)
 {
        size_t start, i;
        uintptr_t e;
@@ -217,7 +217,7 @@ static COLD_ATTRIBUTE void rehash_table(struct htable *ht)
 }
 
 /* We stole some bits, now we need to put them back... */
-static COLD_ATTRIBUTE void update_common(struct htable *ht, const void *p)
+static COLD void update_common(struct htable *ht, const void *p)
 {
        unsigned int i;
        uintptr_t maskdiff, bitsdiff;
index 17fc14d23bcb136138e253c474a96f61cee54e62..7030b79a24d292168cbe0b8437aa4affa1ccc57d 100644 (file)
@@ -16,7 +16,7 @@
     year=1998,
     note="\url{http://supertech.csail.mit.edu/papers/debruijn.pdf}"
   }*/
-static UNNEEDED_ATTRIBUTE const unsigned char DEBRUIJN_IDX32[32]={
+static UNNEEDED const unsigned char DEBRUIJN_IDX32[32]={
    0, 1,28, 2,29,14,24, 3,30,22,20,15,25,17, 4, 8,
   31,27,13,23,21,19,16, 7,26,12,18, 6,11, 5,10, 9
 };
index fdb9ebab8d70c427d275d28077f3b7a180f8bbb2..55dd009885a73c488cb41280f64a319e84ed6233 100644 (file)
@@ -24,7 +24,7 @@
  *             return 1U << ilog32(i-1);
  *     }
  */
-int ilog32(uint32_t _v) IDEMPOTENT_ATTRIBUTE;
+int ilog32(uint32_t _v) IDEMPOTENT;
 
 /**
  * ilog32_nz - Integer binary logarithm of a non-zero 32-bit value.
@@ -43,7 +43,7 @@ int ilog32(uint32_t _v) IDEMPOTENT_ATTRIBUTE;
  *             return ilog32_nz(i) - 1;
  *     }
  */
-int ilog32_nz(uint32_t _v) IDEMPOTENT_ATTRIBUTE;
+int ilog32_nz(uint32_t _v) IDEMPOTENT;
 
 /**
  * ilog64 - Integer binary logarithm of a 64-bit value.
@@ -55,7 +55,7 @@ int ilog32_nz(uint32_t _v) IDEMPOTENT_ATTRIBUTE;
  * See Also:
  *     ilog64_nz(), ilog32()
  */
-int ilog64(uint64_t _v) IDEMPOTENT_ATTRIBUTE;
+int ilog64(uint64_t _v) IDEMPOTENT;
 
 /**
  * ilog64_nz - Integer binary logarithm of a non-zero 64-bit value.
@@ -67,7 +67,7 @@ int ilog64(uint64_t _v) IDEMPOTENT_ATTRIBUTE;
  * See Also:
  *     ilog64(), ilog32_nz()
  */
-int ilog64_nz(uint64_t _v) IDEMPOTENT_ATTRIBUTE;
+int ilog64_nz(uint64_t _v) IDEMPOTENT;
 
 /**
  * STATIC_ILOG_32 - The integer logarithm of an (unsigned, 32-bit) constant.
diff --git a/ccan/iscsi/LICENCE b/ccan/iscsi/LICENCE
new file mode 120000 (symlink)
index 0000000..190cfd5
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/GPL-3
\ No newline at end of file
diff --git a/ccan/iscsi/Makefile b/ccan/iscsi/Makefile
new file mode 100644 (file)
index 0000000..278a236
--- /dev/null
@@ -0,0 +1,39 @@
+LIBS=
+CC=gcc
+CFLAGS=-g -O0 -Wall -W -I../..
+LIBISCSI_OBJ = socket.o init.o login.o nop.o pdu.o discovery.o scsi-command.o scsi-lowlevel.o
+
+all: tools/iscsiclient
+
+tools/iscsiclient: tools/iscsiclient.o libiscsi.a
+       $(CC) $(CFLAGS) -o $@ tools/iscsiclient.o libiscsi.a $(LIBS)
+
+libiscsi.a: $(LIBISCSI_OBJ)
+       @echo Creating library $@
+       ar r libiscsi.a $(LIBISCSI_OBJ) 
+       ranlib libiscsi.a
+
+tools/iscsiclient.o: tools/iscsiclient.c
+       @echo Compiling $@
+       $(CC) $(CFLAGS) -c tools/iscsiclient.c -o $@
+
+socket.o: socket.c iscsi.h iscsi-private.h
+
+init.o: init.c iscsi.h iscsi-private.h
+
+login.o: login.c iscsi.h iscsi-private.h
+
+pdu.o: pdu.c iscsi.h iscsi-private.h
+
+nop.o: nop.c iscsi.h iscsi-private.h
+
+discovery.o: discovery.c iscsi.h iscsi-private.h
+
+scsi-command.o: scsi-command.c iscsi.h iscsi-private.h
+
+scsi-lowlevel.o: scsi-lowlevel.c scsi-lowlevel.h
+
+clean:
+       rm -f tools/iscsiclient
+       rm -f *.o
+       rm -f libiscsi.a
diff --git a/ccan/iscsi/_info b/ccan/iscsi/_info
new file mode 100644 (file)
index 0000000..33ae4e4
--- /dev/null
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * iscsi - async library for iscsi functionality
+ *
+ * The iscsi module is a work in progress.
+ *
+ * It aims to become a full async library for iscsi functionality,
+ * including all features required to establish and maintain a iscsi
+ * session, as well as a low level scsi library to create scsi cdb's
+ * and parse/unmarshall data-in structures.
+ *
+ * License: GPL (3 or any later version)
+ * Author: Ronnie Sahlberg <ronniesahlberg@gmail.com>
+ */
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/compiler\n");
+               return 0;
+       }
+
+       return 1;
+}
+
diff --git a/ccan/iscsi/discovery.c b/ccan/iscsi/discovery.c
new file mode 100644 (file)
index 0000000..3f29755
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+   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 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/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data)
+{
+       struct iscsi_pdu *pdu;
+       char *str;
+
+       if (iscsi == NULL) {
+               printf("trying to send text on NULL context\n");
+               return -1;
+       }
+
+       if (iscsi->session_type != ISCSI_SESSION_DISCOVERY) {
+               printf("Trying to do discovery on non-discovery session\n");
+               return -2;
+       }
+
+       pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_TEXT_REQUEST, ISCSI_PDU_TEXT_RESPONSE);
+       if (pdu == NULL) {
+               printf("Failed to allocate text pdu\n");
+               return -3;
+       }
+
+       /* immediate */
+       iscsi_pdu_set_immediate(pdu);
+
+       /* flags */
+       iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_TEXT_FINAL);
+
+       /* target transfer tag */
+       iscsi_pdu_set_ttt(pdu, 0xffffffff);
+
+       /* sendtargets */
+       str = "SendTargets=All";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -4;
+       }
+
+       pdu->callback     = cb;
+       pdu->private_data = private_data;
+
+       if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+               printf("failed to queue iscsi text pdu\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -5;
+       }
+
+       return 0;
+}
+
+static void iscsi_free_discovery_addresses(struct iscsi_discovery_address *addresses)
+{
+       while (addresses != NULL) {
+               struct iscsi_discovery_address *next = addresses->next;
+
+               if (addresses->target_name != NULL) {
+                       free(discard_const(addresses->target_name));
+                       addresses->target_name = NULL;
+               }
+               if (addresses->target_address != NULL) {
+                       free(discard_const(addresses->target_address));
+                       addresses->target_address = NULL;
+               }
+               addresses->next = NULL;
+               free(addresses);
+               addresses = next;
+       }
+}
+
+int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
+{
+       struct iscsi_discovery_address *targets = NULL;
+
+       /* verify the response looks sane */
+       if (hdr[1] != ISCSI_PDU_TEXT_FINAL) {
+               printf("unsupported flags in text reply %02x\n", hdr[1]);
+               pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+               return -1;
+       }
+
+       /* skip past the header */
+       hdr  += ISCSI_HEADER_SIZE;
+       size -= ISCSI_HEADER_SIZE;
+
+       while (size > 0) {
+               int len;
+
+               len = strlen((char *)hdr);
+
+               if (len == 0) {
+                       break;
+               }
+
+               if (len > size) {
+                       printf("len > size when parsing discovery data %d>%d\n", len, size);
+                       pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+                       iscsi_free_discovery_addresses(targets);
+                       return -1;
+               }
+
+               /* parse the strings */
+               if (!strncmp((char *)hdr, "TargetName=", 11)) {
+                       struct iscsi_discovery_address *target;
+
+                       target = malloc(sizeof(struct iscsi_discovery_address));
+                       if (target == NULL) {
+                               printf("Failed to allocate data for new discovered target\n");
+                               pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+                               iscsi_free_discovery_addresses(targets);
+                               return -1;
+                       }
+                       bzero(target, sizeof(struct iscsi_discovery_address));
+                       target->target_name = strdup((char *)hdr+11);
+                       if (target->target_name == NULL) {
+                               printf("Failed to allocate data for new discovered target name\n");
+                               pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+                               free(target);
+                               target = NULL;
+                               iscsi_free_discovery_addresses(targets);
+                               return -1;
+                       }
+                       target->next = targets;
+                       targets = target;
+               } else if (!strncmp((char *)hdr, "TargetAddress=", 14)) {
+                       targets->target_address = strdup((char *)hdr+14);
+                       if (targets->target_address == NULL) {
+                               printf("Failed to allocate data for new discovered target address\n");
+                               pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+                               iscsi_free_discovery_addresses(targets);
+                               return -1;
+                       }
+               } else {
+                       printf("Dont know how to handle discovery string : %s\n", hdr);
+                       pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+                       iscsi_free_discovery_addresses(targets);
+                       return -1;
+               }
+
+               hdr  += len + 1;
+               size -= len + 1;
+       }
+
+       pdu->callback(iscsi, ISCSI_STATUS_GOOD, targets, pdu->private_data);
+       iscsi_free_discovery_addresses(targets);
+
+       return 0;
+}
diff --git a/ccan/iscsi/dlinklist.h b/ccan/iscsi/dlinklist.h
new file mode 100644 (file)
index 0000000..acbb986
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+   Unix SMB/CIFS implementation.
+   some simple double linked list macros
+
+   Copyright (C) Andrew Tridgell 1998-2010
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* To use these macros you must have a structure containing a next and
+   prev pointer */
+
+#ifndef _DLINKLIST_H
+#define _DLINKLIST_H
+
+/*
+  February 2010 - changed list format to have a prev pointer from the
+  list head. This makes DLIST_ADD_END() O(1) even though we only have
+  one list pointer.
+
+  The scheme is as follows:
+
+     1) with no entries in the list:
+          list_head == NULL
+
+     2) with 1 entry in the list:
+          list_head->next == NULL
+          list_head->prev == list_head
+
+     3) with 2 entries in the list:
+          list_head->next == element2
+          list_head->prev == element2
+         element2->prev == list_head
+         element2->next == NULL
+
+     4) with N entries in the list:
+          list_head->next == element2
+          list_head->prev == elementN
+         elementN->prev == element{N-1}
+         elementN->next == NULL
+
+  This allows us to find the tail of the list by using
+  list_head->prev, which means we can add to the end of the list in
+  O(1) time
+
+
+  Note that the 'type' arguments below are no longer needed, but
+  are kept for now to prevent an incompatible argument change
+ */
+
+
+/*
+   add an element at the front of a list
+*/
+#define DLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+               (p)->prev = (list) = (p);  \
+               (p)->next = NULL; \
+       } else { \
+               (p)->prev = (list)->prev; \
+               (list)->prev = (p); \
+               (p)->next = (list); \
+               (list) = (p); \
+       } \
+} while (0)
+
+/*
+   remove an element from a list
+   Note that the element doesn't have to be in the list. If it
+   isn't then this is a no-op
+*/
+#define DLIST_REMOVE(list, p) \
+do { \
+       if ((p) == (list)) { \
+               if ((p)->next) (p)->next->prev = (p)->prev; \
+               (list) = (p)->next; \
+       } else if ((list) && (p) == (list)->prev) {     \
+               (p)->prev->next = NULL; \
+               (list)->prev = (p)->prev; \
+       } else { \
+               if ((p)->prev) (p)->prev->next = (p)->next; \
+               if ((p)->next) (p)->next->prev = (p)->prev; \
+       } \
+       if ((p) != (list)) (p)->next = (p)->prev = NULL;        \
+} while (0)
+
+/*
+   find the head of the list given any element in it.
+   Note that this costs O(N), so you should avoid this macro
+   if at all possible!
+*/
+#define DLIST_HEAD(p, result_head) \
+do { \
+       (result_head) = (p); \
+       while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \
+} while(0)
+
+/* return the last element in the list */
+#define DLIST_TAIL(list) ((list)?(list)->prev:NULL)
+
+/* return the previous element in the list. */
+#define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL)
+
+/* insert 'p' after the given element 'el' in a list. If el is NULL then
+   this is the same as a DLIST_ADD() */
+#define DLIST_ADD_AFTER(list, p, el) \
+do { \
+        if (!(list) || !(el)) { \
+               DLIST_ADD(list, p); \
+       } else { \
+               (p)->prev = (el);   \
+               (p)->next = (el)->next;         \
+               (el)->next = (p);               \
+               if ((p)->next) (p)->next->prev = (p);   \
+               if ((list)->prev == (el)) (list)->prev = (p); \
+       }\
+} while (0)
+
+
+/*
+   add to the end of a list.
+   Note that 'type' is ignored
+*/
+#define DLIST_ADD_END(list, p, type)                   \
+do { \
+       if (!(list)) { \
+               DLIST_ADD(list, p); \
+       } else { \
+               DLIST_ADD_AFTER(list, p, (list)->prev); \
+       } \
+} while (0)
+
+/* promote an element to the from of a list */
+#define DLIST_PROMOTE(list, p) \
+do { \
+          DLIST_REMOVE(list, p); \
+          DLIST_ADD(list, p); \
+} while (0)
+
+/*
+   demote an element to the end of a list.
+   Note that 'type' is ignored
+*/
+#define DLIST_DEMOTE(list, p, type)                    \
+do { \
+       DLIST_REMOVE(list, p); \
+       DLIST_ADD_END(list, p, NULL);           \
+} while (0)
+
+/*
+   concatenate two lists - putting all elements of the 2nd list at the
+   end of the first list.
+   Note that 'type' is ignored
+*/
+#define DLIST_CONCATENATE(list1, list2, type)  \
+do { \
+       if (!(list1)) { \
+               (list1) = (list2); \
+       } else { \
+               (list1)->prev->next = (list2); \
+               if (list2) { \
+                       void *_tmplist = (void *)(list1)->prev; \
+                       (list1)->prev = (list2)->prev; \
+                       (list2)->prev = _tmplist; \
+               } \
+       } \
+} while (0)
+
+#endif /* _DLINKLIST_H */
diff --git a/ccan/iscsi/init.c b/ccan/iscsi/init.c
new file mode 100644 (file)
index 0000000..e428c94
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+   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 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/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "dlinklist.h"
+
+
+struct iscsi_context *iscsi_create_context(const char *initiator_name)
+{
+       struct iscsi_context *iscsi;
+
+       iscsi = malloc(sizeof(struct iscsi_context));
+       if (iscsi == NULL) {
+               printf("Failed to allocate iscsi context\n");
+               return NULL;
+       }
+
+       bzero(iscsi, sizeof(struct iscsi_context));
+
+       iscsi->initiator_name = strdup(initiator_name);
+       if (iscsi->initiator_name == NULL) {
+               printf("Failed to allocate initiator name context\n");
+               free(iscsi);
+               return NULL;
+       }
+
+       iscsi->fd = -1;
+
+       /* use a "random" isid */
+       srandom(getpid() ^ time(NULL));
+       iscsi_set_random_isid(iscsi);
+
+       return iscsi;
+}
+
+int iscsi_set_random_isid(struct iscsi_context *iscsi)
+{
+       iscsi->isid[0] = 0x80;
+       iscsi->isid[1] = random()&0xff;
+       iscsi->isid[2] = random()&0xff;
+       iscsi->isid[3] = random()&0xff;
+       iscsi->isid[4] = 0;
+       iscsi->isid[5] = 0;
+
+       return 0;
+}
+
+int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias)
+{
+       if (iscsi == NULL) {
+               printf("Context is NULL when adding alias\n");
+               return -1;
+       }
+       if (iscsi->is_loggedin != 0) {
+               printf("Already logged in when adding alias\n");
+               return -2;
+       }
+
+       if (iscsi->alias != NULL) {
+               free(discard_const(iscsi->alias));
+               iscsi->alias = NULL;
+       }
+
+       iscsi->alias = strdup(alias);
+       if (iscsi->alias == NULL) {
+               printf("Failed to allocate alias name\n");
+               return -3;
+       }
+
+       return 0;
+}
+
+int iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name)
+{
+       if (iscsi == NULL) {
+               printf("Context is NULL when adding targetname\n");
+               return -1;
+       }
+       if (iscsi->is_loggedin != 0) {
+               printf("Already logged in when adding targetname\n");
+               return -2;
+       }
+
+       if (iscsi->target_name != NULL) {
+               free(discard_const(iscsi->target_name));
+               iscsi->target_name= NULL;
+       }
+
+       iscsi->target_name = strdup(target_name);
+       if (iscsi->target_name == NULL) {
+               printf("Failed to allocate target name\n");
+               return -3;
+       }
+
+       return 0;
+}
+
+int iscsi_destroy_context(struct iscsi_context *iscsi)
+{
+       struct iscsi_pdu *pdu;
+
+       if (iscsi == NULL) {
+               return 0;
+       }
+       if (iscsi->initiator_name != NULL) {
+               free(discard_const(iscsi->initiator_name));
+               iscsi->initiator_name = NULL;
+       }
+       if (iscsi->alias != NULL) {
+               free(discard_const(iscsi->alias));
+               iscsi->alias = NULL;
+       }
+       if (iscsi->is_loggedin != 0) {
+               printf("deswtroying context while logged in\n");
+       }
+       if (iscsi->fd != -1) {
+               iscsi_disconnect(iscsi);
+       }
+
+       if (iscsi->inbuf != NULL) {
+               free(iscsi->inbuf);
+               iscsi->inbuf = NULL;
+               iscsi->insize = 0;
+               iscsi->inpos = 0;
+       }
+
+       while ((pdu = iscsi->outqueue)) {
+               DLIST_REMOVE(iscsi->outqueue, pdu);
+               pdu->callback(iscsi, ISCSI_STATUS_CANCELLED, NULL, pdu->private_data);
+               iscsi_free_pdu(iscsi, pdu);
+       }
+       while ((pdu = iscsi->waitpdu)) {
+               DLIST_REMOVE(iscsi->waitpdu, pdu);
+               pdu->callback(iscsi, ISCSI_STATUS_CANCELLED, NULL, pdu->private_data);
+               iscsi_free_pdu(iscsi, pdu);
+       }
+
+       free(iscsi);
+
+       return 0;
+}
diff --git a/ccan/iscsi/iscsi-private.h b/ccan/iscsi/iscsi-private.h
new file mode 100644 (file)
index 0000000..090d3c4
--- /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 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/>.
+*/
+#ifndef CCAN_ISCSI_PRIVATE_H
+#define CCAN_ISCSI_PRIVATE_H
+
+#include <stdint.h>
+
+#ifndef discard_const
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+#endif
+
+struct iscsi_context {
+       const char *initiator_name;
+       const char *target_name;
+       const char *alias;
+       enum iscsi_session_type session_type;
+       unsigned char isid[6];
+       uint32_t itt;
+       uint32_t cmdsn;
+       uint32_t statsn;
+
+       int fd;
+       int is_connected;
+       int is_loggedin;
+
+       iscsi_command_cb connect_cb;
+       void *connect_data;
+
+       struct iscsi_pdu *outqueue;
+       struct iscsi_pdu *waitpdu;
+
+       int insize;
+       int inpos;
+       unsigned char *inbuf;
+};
+
+#define ISCSI_HEADER_SIZE                      48
+
+#define ISCSI_PDU_IMMEDIATE                   0x40
+
+#define ISCSI_PDU_TEXT_FINAL                  0x80
+#define ISCSI_PDU_TEXT_CONTINUE                       0x40
+
+#define ISCSI_PDU_LOGIN_TRANSIT                       0x80
+#define ISCSI_PDU_LOGIN_CONTINUE              0x40
+#define ISCSI_PDU_LOGIN_CSG_SECNEG            0x00
+#define ISCSI_PDU_LOGIN_CSG_OPNEG             0x04
+#define ISCSI_PDU_LOGIN_CSG_FF                0x0c
+#define ISCSI_PDU_LOGIN_NSG_SECNEG            0x00
+#define ISCSI_PDU_LOGIN_NSG_OPNEG             0x01
+#define ISCSI_PDU_LOGIN_NSG_FF                0x03
+
+
+#define ISCSI_PDU_SCSI_FINAL                  0x80
+#define ISCSI_PDU_SCSI_READ                   0x40
+#define ISCSI_PDU_SCSI_WRITE                  0x20
+#define ISCSI_PDU_SCSI_ATTR_UNTAGGED          0x00
+#define ISCSI_PDU_SCSI_ATTR_SIMPLE            0x01
+#define ISCSI_PDU_SCSI_ATTR_ORDERED           0x02
+#define ISCSI_PDU_SCSI_ATTR_HEADOFQUEUE               0x03
+#define ISCSI_PDU_SCSI_ATTR_ACA                       0x04
+
+#define ISCSI_PDU_DATA_FINAL                  0x80
+#define ISCSI_PDU_DATA_ACK_REQUESTED          0x40
+#define ISCSI_PDU_DATA_BIDIR_OVERFLOW                 0x10
+#define ISCSI_PDU_DATA_BIDIR_UNDERFLOW         0x08
+#define ISCSI_PDU_DATA_RESIDUAL_OVERFLOW       0x04
+#define ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW      0x02
+#define ISCSI_PDU_DATA_CONTAINS_STATUS        0x01
+
+enum iscsi_opcode {ISCSI_PDU_NOP_OUT=0x00,
+                 ISCSI_PDU_SCSI_REQUEST=0x01,
+                 ISCSI_PDU_LOGIN_REQUEST=0x03,
+                 ISCSI_PDU_TEXT_REQUEST=0x04,
+                 ISCSI_PDU_LOGOUT_REQUEST=0x06,
+                 ISCSI_PDU_NOP_IN=0x20,
+                 ISCSI_PDU_SCSI_RESPONSE=0x21,
+                 ISCSI_PDU_LOGIN_RESPONSE=0x23,
+                 ISCSI_PDU_TEXT_RESPONSE=0x24,
+                 ISCSI_PDU_DATA_IN=0x25,
+                 ISCSI_PDU_LOGOUT_RESPONSE=0x26};
+
+struct iscsi_pdu {
+       struct iscsi_pdu *prev, *next;
+
+       uint32_t itt;
+       uint32_t cmdsn;
+       enum iscsi_opcode response_opcode;
+
+       iscsi_command_cb callback;
+       void *private_data;
+
+       int written;
+       struct iscsi_data outdata;
+       struct iscsi_data indata;
+
+       struct iscsi_scsi_cbdata *scsi_cbdata;
+};
+
+void iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata);
+
+struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, enum iscsi_opcode response_opcode);
+void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
+void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags);
+void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu);
+void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt);
+void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn);
+void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun);
+void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn);
+void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen);
+int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, unsigned char *dptr, int dsize);
+int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
+int iscsi_add_data(struct iscsi_data *data, unsigned char *dptr, int dsize, int pdualignment);
+int iscsi_set_random_isid(struct iscsi_context *iscsi);
+
+struct scsi_task;
+void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task);
+
+int iscsi_get_pdu_size(const unsigned char *hdr);
+int iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr, int size);
+
+int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
+int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
+int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
+int iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
+int iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size, int *is_finished);
+int iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
+
+#endif /* CCAN_ISCSI_PRIVATE_H */
diff --git a/ccan/iscsi/iscsi.h b/ccan/iscsi/iscsi.h
new file mode 100644 (file)
index 0000000..4aa4670
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+   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 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/>.
+*/
+#ifndef CCAN_ISCSI_H
+#define CCAN_ISCSI_H
+
+struct iscsi_context;
+struct sockaddr;
+
+
+/*
+ * Returns the file descriptor that libiscsi uses.
+ */
+int iscsi_get_fd(struct iscsi_context *iscsi);
+
+/*
+ * Returns which events that we need to poll for for the iscsi file descriptor.
+ */
+int iscsi_which_events(struct iscsi_context *iscsi);
+
+/*
+ * Called to process the events when events become available for the iscsi file descriptor.
+ */
+int iscsi_service(struct iscsi_context *iscsi, int revents);
+
+
+
+/*
+ * Create a context for an ISCSI session.
+ * Initiator_name is the iqn name we want to identify to the target as.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+struct iscsi_context *iscsi_create_context(const char *initiator_name);
+
+/*
+ * Destroy an existing ISCSI context and tear down any existing connection.
+ * Callbacks for any command in flight will be invoked with ISCSI_STATUS_CANCELLED.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_destroy_context(struct iscsi_context *iscsi);
+
+/*
+ * Set an optional alias name to identify with when connecting to the target
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias);
+
+/*
+ * Set the iqn name of the taqget to login to.
+ * The target name must be set before a normal-login can be initiated.
+ * Only discovery-logins are possible without setting the target iqn name.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_targetname(struct iscsi_context *iscsi, const char *targetname);
+
+
+/* Types of icsi sessions. Discovery sessions are used to query for what targets exist behind
+ * the portal connected to. Normal sessions are used to log in and do I/O to the SCSI LUNs
+ */
+enum iscsi_session_type {ISCSI_SESSION_DISCOVERY=1, ISCSI_SESSION_NORMAL=2};
+
+/*
+ * Set the session type for a scsi context.
+ * Session type can only be set/changed while the iscsi context is not logged in to
+ * a target.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_session_type(struct iscsi_context *iscsi, enum iscsi_session_type session_type);
+
+
+/* ISCSI_STATUS_GOOD must map to SCSI_STATUS_GOOD
+ * and ISCSI_STATUS_CHECK_CONDITION must map to SCSI_STATUS_CHECK_CONDITION
+ */
+enum icsi_status { ISCSI_STATUS_GOOD           =0,
+                  ISCSI_STATUS_CHECK_CONDITION =2,
+                  ISCSI_STATUS_CANCELLED       =0x0f000000,
+                  ISCSI_STATUS_ERROR           =0x0f000001 };
+
+
+/*
+ * Generic callback for completion of iscsi_*_async().
+ * command_data depends on status.
+ */
+typedef void (*iscsi_command_cb)(struct iscsi_context *iscsi, int status, void *command_data, void *private_data);
+
+
+/*
+ * Asynchronous call to connect a TCP connection to the target-host/port
+ *
+ * Returns:
+ *  0 if the call was initiated and a connection will be attempted. Result of the connection will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * This command is unique in that the callback can be invoked twice.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : Connection was successful. Command_data is NULL.
+ *                            In this case the callback will be invoked a second time once the connection
+ *                            is torn down.
+ *
+ *    ISCSI_STATUS_ERROR    : Either failed to establish the connection, or an already established connection
+ *                            has failed with an error.
+ *
+ * The callback will NOT be invoked if the session is explicitely torn down through a call to
+ * iscsi_disconnect() or iscsi_destroy_context().
+ */
+int iscsi_connect_async(struct iscsi_context *iscsi, const char *target, iscsi_command_cb cb, void *private_data);
+
+/*
+ * Disconnect a connection to a target.
+ * You can not disconnect while being logged in to a target.
+ *
+ * Returns:
+ *  0 disconnect was successful
+ * <0 error
+ */
+int iscsi_disconnect(struct iscsi_context *iscsi);
+
+/*
+ * Asynchronous call to perform an ISCSI login.
+ *
+ * Returns:
+ *  0 if the call was initiated and a login will be attempted. Result of the login will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : login was successful. Command_data is always NULL.
+ *    ISCSI_STATUS_CANCELLED: login was aborted. Command_data is NULL.
+ *    ISCSI_STATUS_ERROR    : login failed. Command_data is NULL.
+ */
+int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data);
+
+
+/*
+ * Asynchronous call to perform an ISCSI logout.
+ *
+ * Returns:
+ *  0 if the call was initiated and a logout will be attempted. Result of the logout will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : logout was successful. Command_data is always NULL.
+ *    ISCSI_STATUS_CANCELLED: logout was aborted. Command_data is NULL.
+ */
+int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data);
+
+
+/*
+ * Asynchronous call to perform an ISCSI discovery.
+ *
+ * discoveries can only be done on connected and logged in discovery sessions.
+ *
+ * Returns:
+ *  0 if the call was initiated and a discovery  will be attempted. Result of the logout will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : Discovery was successful. Command_data is a pointer to a
+ *                            iscsi_discovery_address list of structures.
+ *                            This list of structures is only valid for the duration of the
+ *                            callback and all data will be freed once the callback returns.
+ *    ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
+ */
+int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data);
+
+struct iscsi_discovery_address {
+       struct iscsi_discovery_address *next;
+       const char *target_name;
+       const char *target_address;
+};
+
+/*
+ * Asynchronous call to perform an ISCSI NOP-OUT call
+ *
+ * Returns:
+ *  0 if the call was initiated and a nop-out will be attempted. Result will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : NOP-OUT was successful and the server responded with a NOP-IN
+ *                            callback_data is a iscsi_data structure containing the data returned from
+ *                            the server.
+ *    ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
+ */
+int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb, unsigned char *data, int len, void *private_data);
+
+
+/* These are the possible status values for the callbacks for scsi commands.
+ * The content of command_data depends on the status type.
+ *
+ * status :
+ *   ISCSI_STATUS_GOOD the scsi command completed successfullt on the target.
+ *   If this scsi command returns DATA-IN, that data is stored in an scsi_task structure
+ *   returned in the command_data parameter. This buffer will be automatically freed once the callback
+ *   returns.
+ *
+ *   ISCSI_STATUS_CHECK_CONDITION the scsi command failed with a scsi sense.
+ *   Command_data contains a struct scsi_task. When the callback returns, this buffer
+ *   will automatically become freed.
+ *
+ *   ISCSI_STATUS_CANCELLED the scsi command was aborted. Command_data is NULL.
+ *
+ *   ISCSI_STATUS_ERROR the command failed. Command_data is NULL.
+ */
+
+
+
+struct iscsi_data {
+       int size;
+       unsigned char *data;
+};
+
+
+/*
+ * Async commands for SCSI
+ */
+int iscsi_reportluns_async(struct iscsi_context *iscsi, iscsi_command_cb cb, int report_type, int alloc_len, void *private_data);
+int iscsi_testunitready_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data);
+int iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int evpd, int page_code, int maxsize, void *private_data);
+int iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int pmi, void *private_data);
+int iscsi_read10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int datalen, int blocksize, void *private_data);
+int iscsi_write10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, unsigned char *data, int datalen, int lba, int fua, int fuanv, int blocksize, void *private_data);
+int iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, void *private_data);
+
+
+#endif /* CCAN_ISCSI_H */
diff --git a/ccan/iscsi/login.c b/ccan/iscsi/login.c
new file mode 100644 (file)
index 0000000..bc0d6ff
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+   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 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/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <ccan/compiler/compiler.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data)
+{
+       struct iscsi_pdu *pdu;
+       char *str;
+       int ret;
+
+       if (iscsi == NULL) {
+               printf("trying to login on NULL context\n");
+               return -1;
+       }
+
+       if (iscsi->is_loggedin != 0) {
+               printf("trying to login while already logged in\n");
+               return -2;
+       }
+
+       switch (iscsi->session_type) {
+       case ISCSI_SESSION_DISCOVERY:
+       case ISCSI_SESSION_NORMAL:
+               break;
+       default:
+               printf("trying to login without setting session type\n");
+               return -3;
+       }
+
+       pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGIN_REQUEST, ISCSI_PDU_LOGIN_RESPONSE);
+       if (pdu == NULL) {
+               printf("Failed to allocate login pdu\n");
+               return -4;
+       }
+
+       /* login request */
+       iscsi_pdu_set_immediate(pdu);
+
+       /* flags */
+       iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_LOGIN_TRANSIT|ISCSI_PDU_LOGIN_CSG_OPNEG|ISCSI_PDU_LOGIN_NSG_FF);
+
+
+       /* initiator name */
+       if (asprintf(&str, "InitiatorName=%s", iscsi->initiator_name) == -1) {
+               printf("asprintf failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -5;
+       }
+       ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1);
+       free(str);
+       if (ret != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -6;
+       }
+
+       /* optional alias */
+       if (iscsi->alias) {
+               if (asprintf(&str, "InitiatorAlias=%s", iscsi->alias) == -1) {
+                       printf("asprintf failed\n");
+                       iscsi_free_pdu(iscsi, pdu);
+                       return -7;
+               }
+               ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1);
+               free(str);
+               if (ret != 0) {
+                       printf("pdu add data failed\n");
+                       iscsi_free_pdu(iscsi, pdu);
+                       return -8;
+               }
+       }
+
+       /* target name */
+       if (iscsi->session_type == ISCSI_SESSION_NORMAL) {
+               if (iscsi->target_name == NULL) {
+                       printf("trying normal connect but target name not set\n");
+                       iscsi_free_pdu(iscsi, pdu);
+                       return -9;
+               }
+
+               if (asprintf(&str, "TargetName=%s", iscsi->target_name) == -1) {
+                       printf("asprintf failed\n");
+                       iscsi_free_pdu(iscsi, pdu);
+                       return -10;
+               }
+               ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1);
+               free(str);
+               if (ret != 0) {
+                       printf("pdu add data failed\n");
+                       iscsi_free_pdu(iscsi, pdu);
+                       return -11;
+               }
+       }
+
+       /* session type */
+       switch (iscsi->session_type) {
+       case ISCSI_SESSION_DISCOVERY:
+               str = "SessionType=Discovery";
+               break;
+       case ISCSI_SESSION_NORMAL:
+               str = "SessionType=Normal";
+               break;
+       default:
+               printf("can not handle sessions %d yet\n", iscsi->session_type);
+               return -12;
+       }
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -13;
+       }
+
+       str = "HeaderDigest=None";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -14;
+       }
+       str = "DataDigest=None";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -15;
+       }
+       str = "InitialR2T=Yes";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -16;
+       }
+       str = "ImmediateData=Yes";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -17;
+       }
+       str = "MaxBurstLength=262144";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -18;
+       }
+       str = "FirstBurstLength=262144";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -19;
+       }
+       str = "MaxRecvDataSegmentLength=262144";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -20;
+       }
+       str = "DataPDUInOrder=Yes";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -21;
+       }
+       str = "DataSequenceInOrder=Yes";
+       if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+               printf("pdu add data failed\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -22;
+       }
+
+
+       pdu->callback     = cb;
+       pdu->private_data = private_data;
+
+       if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+               printf("failed to queue iscsi login pdu\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -23;
+       }
+
+       return 0;
+}
+
+int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
+{
+       int status;
+
+       if (size < ISCSI_HEADER_SIZE) {
+               printf("dont have enough data to read status from login reply\n");
+               return -1;
+       }
+
+       /* XXX here we should parse the data returned in case the target renegotiated some
+        *     some parameters.
+        *     we should also do proper handshaking if the target is not yet prepared to transition
+        *     to the next stage
+        */
+       status = ntohs(*(uint16_t *)&hdr[36]);
+       if (status != 0) {
+               pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+               return 0;
+       }
+
+       iscsi->statsn = ntohs(*(uint16_t *)&hdr[24]);
+
+       iscsi->is_loggedin = 1;
+       pdu->callback(iscsi, ISCSI_STATUS_GOOD, NULL, pdu->private_data);
+
+       return 0;
+}
+
+
+int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data)
+{
+       struct iscsi_pdu *pdu;
+
+       if (iscsi == NULL) {
+               printf("trying to logout on NULL context\n");
+               return -1;
+       }
+
+       if (iscsi->is_loggedin == 0) {
+               printf("trying to logout while not logged in\n");
+               return -2;
+       }
+
+       pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGOUT_REQUEST, ISCSI_PDU_LOGOUT_RESPONSE);
+       if (pdu == NULL) {
+               printf("Failed to allocate logout pdu\n");
+               return -3;
+       }
+
+       /* logout request has the immediate flag set */
+       iscsi_pdu_set_immediate(pdu);
+
+       /* flags : close the session */
+       iscsi_pdu_set_pduflags(pdu, 0x80);
+
+
+       pdu->callback     = cb;
+       pdu->private_data = private_data;
+
+       if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+               printf("failed to queue iscsi logout pdu\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -4;
+       }
+
+       return 0;
+}
+
+int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size UNUSED)
+{
+       iscsi->is_loggedin = 0;
+       pdu->callback(iscsi, ISCSI_STATUS_GOOD, NULL, pdu->private_data);
+
+       return 0;
+}
+
+int iscsi_set_session_type(struct iscsi_context *iscsi, enum iscsi_session_type session_type)
+{
+       if (iscsi == NULL) {
+               printf("Trying to set sesssion type on NULL context\n");
+               return -1;
+       }
+       if (iscsi->is_loggedin) {
+               printf("trying to set session type while logged in\n");
+               return -2;
+       }
+       
+       iscsi->session_type = session_type;
+
+       return 0;
+}
diff --git a/ccan/iscsi/nop.c b/ccan/iscsi/nop.c
new file mode 100644 (file)
index 0000000..5c288b0
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+   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 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/>.
+*/
+
+#include <stdio.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb, unsigned char *data, int len, void *private_data)
+{
+       struct iscsi_pdu *pdu;
+
+       if (iscsi == NULL) {
+               printf("trying to send nop-out on NULL context\n");
+               return -1;
+       }
+
+       if (iscsi->is_loggedin == 0) {
+               printf("trying send nop-out while not logged in\n");
+               return -2;
+       }
+
+       pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_NOP_OUT, ISCSI_PDU_NOP_IN);
+       if (pdu == NULL) {
+               printf("Failed to allocate nop-out pdu\n");
+               return -3;
+       }
+
+       /* immediate flag */
+       iscsi_pdu_set_immediate(pdu);
+
+       /* flags */
+       iscsi_pdu_set_pduflags(pdu, 0x80);
+
+       /* ttt */
+       iscsi_pdu_set_ttt(pdu, 0xffffffff);
+
+       /* lun */
+       iscsi_pdu_set_lun(pdu, 2);
+
+       /* cmdsn is not increased if Immediate delivery*/
+       iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
+       pdu->cmdsn = iscsi->cmdsn;
+//     iscsi->cmdsn++;
+
+       pdu->callback     = cb;
+       pdu->private_data = private_data;
+
+       if (iscsi_pdu_add_data(iscsi, pdu, data, len) != 0) {
+               printf("Failed to add outdata to nop-out\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -4;
+       }
+
+
+       if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+               printf("failed to queue iscsi nop-out pdu\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -5;
+       }
+
+       return 0;
+}
+
+int iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
+{
+       struct iscsi_data data;
+
+       data.data = NULL;
+       data.size = 0;
+
+       if (size > ISCSI_HEADER_SIZE) {
+               data.data = discard_const(&hdr[ISCSI_HEADER_SIZE]);
+               data.size = size - ISCSI_HEADER_SIZE;
+       }
+       pdu->callback(iscsi, ISCSI_STATUS_GOOD, &data, pdu->private_data);
+
+       return 0;
+}
diff --git a/ccan/iscsi/pdu.c b/ccan/iscsi/pdu.c
new file mode 100644 (file)
index 0000000..0b290d9
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+   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 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/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "scsi-lowlevel.h"
+#include "dlinklist.h"
+
+struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, enum iscsi_opcode response_opcode)
+{
+       struct iscsi_pdu *pdu;
+
+       pdu = malloc(sizeof(struct iscsi_pdu));
+       if (pdu == NULL) {
+               printf("failed to allocate pdu\n");
+               return NULL;
+       }
+       bzero(pdu, sizeof(struct iscsi_pdu));
+
+       pdu->outdata.size = ISCSI_HEADER_SIZE;
+       pdu->outdata.data = malloc(pdu->outdata.size);
+
+       if (pdu->outdata.data == NULL) {
+               printf("failed to allocate pdu header\n");
+               free(pdu);
+               pdu = NULL;
+               return NULL;
+       }
+       bzero(pdu->outdata.data, pdu->outdata.size);
+
+       /* opcode */
+       pdu->outdata.data[0] = opcode;
+       pdu->response_opcode = response_opcode;
+
+       /* isid */
+       if (opcode ==ISCSI_PDU_LOGIN_REQUEST) {
+               memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6);
+       }
+
+       /* itt */
+       *(uint32_t *)&pdu->outdata.data[16] = htonl(iscsi->itt);
+       pdu->itt = iscsi->itt;
+
+       iscsi->itt++;
+
+       return pdu;
+}
+
+void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
+{
+       if (pdu == NULL) {
+               printf("trying to free NULL pdu\n");
+               return;
+       }
+       if (pdu->outdata.data) {
+               free(pdu->outdata.data);
+               pdu->outdata.data = NULL;
+       }
+       if (pdu->indata.data) {
+               free(pdu->indata.data);
+               pdu->indata.data = NULL;
+       }
+       if (pdu->scsi_cbdata) {
+               iscsi_free_scsi_cbdata(pdu->scsi_cbdata);
+               pdu->scsi_cbdata = NULL;
+       }
+
+       free(pdu);
+}
+
+
+int iscsi_add_data(struct iscsi_data *data, unsigned char *dptr, int dsize, int pdualignment)
+{
+       int len, aligned;
+       unsigned char *buf;
+
+       if (dsize == 0) {
+               printf("Trying to append zero size data to iscsi_data\n");
+               return -1;
+       }
+
+       len = data->size + dsize;
+       aligned = len;
+       if (pdualignment) {
+               aligned = (aligned+3)&0xfffffffc;
+       }
+       buf = malloc(aligned);
+       if (buf == NULL) {
+               printf("failed to allocate buffer for %d bytes\n", len);
+               return -2;
+       }
+
+       memcpy(buf, data->data, data->size);
+       memcpy(buf + data->size, dptr, dsize);
+       if (len != aligned) {
+               /* zero out any padding at the end */
+              bzero(buf+len, aligned-len);
+       }
+
+       free(data->data);
+       data->data  = buf;
+       data->size = len;
+
+       return 0;
+}
+
+int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, unsigned char *dptr, int dsize)
+{
+       if (pdu == NULL) {
+               printf("trying to add data to NULL pdu\n");
+               return -1;
+       }
+       if (dsize == 0) {
+               printf("Trying to append zero size data to pdu\n");
+               return -2;
+       }
+
+       if (iscsi_add_data(&pdu->outdata, dptr, dsize, 1) != 0) {
+               printf("failed to add data to pdu buffer\n");
+               return -3;
+       }
+
+       /* update data segment length */
+       *(uint32_t *)&pdu->outdata.data[4] = htonl(pdu->outdata.size-ISCSI_HEADER_SIZE);
+
+       return 0;
+}
+
+int iscsi_get_pdu_size(const unsigned char *hdr)
+{
+       int size;
+
+       size = (ntohl(*(uint32_t *)&hdr[4])&0x00ffffff) + ISCSI_HEADER_SIZE;
+       size = (size+3)&0xfffffffc;
+
+       return size;
+}
+
+
+int iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr, int size)
+{
+       uint32_t itt;
+       enum iscsi_opcode opcode;
+       struct iscsi_pdu *pdu;
+       uint8_t ahslen;
+
+       opcode = hdr[0] & 0x3f;
+       ahslen = hdr[4];
+       itt = ntohl(*(uint32_t *)&hdr[16]);
+
+       if (ahslen != 0) {
+               printf("cant handle expanded headers yet\n");
+               return -1;
+       }
+
+       for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
+               enum iscsi_opcode expected_response = pdu->response_opcode;
+               int is_finished = 1;
+
+               if (pdu->itt != itt) {
+                       continue;
+               }
+
+               /* we have a special case with scsi-command opcodes, the are replied to by either a scsi-response
+                * or a data-in, or a combination of both.
+                */
+               if (opcode == ISCSI_PDU_DATA_IN && expected_response == ISCSI_PDU_SCSI_RESPONSE) {
+                       expected_response = ISCSI_PDU_DATA_IN;
+               }
+                               
+               if (opcode != expected_response) {
+                       printf("Got wrong opcode back for itt:%d  got:%d expected %d\n", itt, opcode, pdu->response_opcode);
+                       return -1;
+               }
+               switch (opcode) {
+               case ISCSI_PDU_LOGIN_RESPONSE:
+                       if (iscsi_process_login_reply(iscsi, pdu, hdr, size) != 0) {
+                               DLIST_REMOVE(iscsi->waitpdu, pdu);
+                               iscsi_free_pdu(iscsi, pdu);
+                               printf("iscsi login reply failed\n");
+                               return -2;
+                       }
+                       break;
+               case ISCSI_PDU_TEXT_RESPONSE:
+                       if (iscsi_process_text_reply(iscsi, pdu, hdr, size) != 0) {
+                               DLIST_REMOVE(iscsi->waitpdu, pdu);
+                               iscsi_free_pdu(iscsi, pdu);
+                               printf("iscsi text reply failed\n");
+                               return -2;
+                       }
+                       break;
+               case ISCSI_PDU_LOGOUT_RESPONSE:
+                       if (iscsi_process_logout_reply(iscsi, pdu, hdr, size) != 0) {
+                               DLIST_REMOVE(iscsi->waitpdu, pdu);
+                               iscsi_free_pdu(iscsi, pdu);
+                               printf("iscsi logout reply failed\n");
+                               return -3;
+                       }
+                       break;
+               case ISCSI_PDU_SCSI_RESPONSE:
+                       if (iscsi_process_scsi_reply(iscsi, pdu, hdr, size) != 0) {
+                               DLIST_REMOVE(iscsi->waitpdu, pdu);
+                               iscsi_free_pdu(iscsi, pdu);
+                               printf("iscsi response reply failed\n");
+                               return -4;
+                       }
+                       break;
+               case ISCSI_PDU_DATA_IN:
+                       if (iscsi_process_scsi_data_in(iscsi, pdu, hdr, size, &is_finished) != 0) {
+                               DLIST_REMOVE(iscsi->waitpdu, pdu);
+                               iscsi_free_pdu(iscsi, pdu);
+                               printf("iscsi data in failed\n");
+                               return -4;
+                       }
+                       break;
+               case ISCSI_PDU_NOP_IN:
+                       if (iscsi_process_nop_out_reply(iscsi, pdu, hdr, size) != 0) {
+                               DLIST_REMOVE(iscsi->waitpdu, pdu);
+                               iscsi_free_pdu(iscsi, pdu);
+                               printf("iscsi nop-in failed\n");
+                               return -5;
+                       }
+                       break;
+               default:
+                       printf("Dont know how to handle opcode %d\n", opcode);
+                       return -2;
+               }
+
+               if (is_finished) {
+                       DLIST_REMOVE(iscsi->waitpdu, pdu);
+                       iscsi_free_pdu(iscsi, pdu);
+               } else {
+                       printf("pdu is not yet finished, let it remain\n");
+               }
+               return 0;
+       }
+
+       return 0;
+}
+
+void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags)
+{
+       pdu->outdata.data[1] = flags;
+}
+
+void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu)
+{
+       pdu->outdata.data[0] |= ISCSI_PDU_IMMEDIATE;
+}
+
+void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt)
+{
+       *(uint32_t *)&pdu->outdata.data[20] = htonl(ttt);
+}
+
+void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn)
+{
+       *(uint32_t *)&pdu->outdata.data[24] = htonl(cmdsn);
+}
+
+void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn)
+{
+       *(uint32_t *)&pdu->outdata.data[28] = htonl(expstatsnsn);
+}
+
+void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task)
+{
+       bzero(&pdu->outdata.data[32], 16);
+       memcpy(&pdu->outdata.data[32], task->cdb, task->cdb_size);
+}
+
+void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun)
+{
+       pdu->outdata.data[9] = lun;
+}
+
+void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen)
+{
+       *(uint32_t *)&pdu->outdata.data[20] = htonl(expxferlen);
+}
diff --git a/ccan/iscsi/scsi-command.c b/ccan/iscsi/scsi-command.c
new file mode 100644 (file)
index 0000000..eb3ae5b
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+   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 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/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "scsi-lowlevel.h"
+#include "dlinklist.h"
+
+struct iscsi_scsi_cbdata {
+       struct iscsi_scsi_cbdata *prev, *next;
+       iscsi_command_cb  callback;
+       void             *private_data;
+       struct scsi_task *task;
+};
+
+void iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata)
+{
+       if (scsi_cbdata == NULL) {
+               return;
+       }
+       if (scsi_cbdata->task == NULL) {
+               scsi_free_scsi_task(scsi_cbdata->task);
+               scsi_cbdata->task = NULL;
+       }
+       free(scsi_cbdata);
+}
+
+static void iscsi_scsi_response_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct iscsi_scsi_cbdata *scsi_cbdata = (struct iscsi_scsi_cbdata *)private_data;
+       struct scsi_task *task = command_data;
+
+       switch (status) {
+       case ISCSI_STATUS_CHECK_CONDITION:
+               scsi_cbdata->callback(iscsi, ISCSI_STATUS_CHECK_CONDITION, task, scsi_cbdata->private_data);
+               return;
+       
+
+       case ISCSI_STATUS_GOOD:
+               scsi_cbdata->callback(iscsi, ISCSI_STATUS_GOOD, task, scsi_cbdata->private_data);
+               return;
+       default:
+               printf("Cant handle  scsi status %d yet\n", status);
+               scsi_cbdata->callback(iscsi, ISCSI_STATUS_ERROR, task, scsi_cbdata->private_data);
+       }
+}
+
+
+static int iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, struct scsi_task *task, iscsi_command_cb cb, struct iscsi_data *data, void *private_data)
+{
+       struct iscsi_pdu *pdu;
+       struct iscsi_scsi_cbdata *scsi_cbdata;
+       int flags;
+
+       if (iscsi == NULL) {
+               printf("trying to send command on NULL context\n");
+               scsi_free_scsi_task(task);
+               return -1;
+       }
+
+       if (iscsi->session_type != ISCSI_SESSION_NORMAL) {
+               printf("Trying to send command on discovery session\n");
+               scsi_free_scsi_task(task);
+               return -2;
+       }
+
+       if (iscsi->is_loggedin == 0) {
+               printf("Trying to send command while not logged in\n");
+               scsi_free_scsi_task(task);
+               return -3;
+       }
+
+       scsi_cbdata = malloc(sizeof(struct iscsi_scsi_cbdata));
+       if (scsi_cbdata == NULL) {
+               printf("failed to allocate scsi cbdata\n");
+               scsi_free_scsi_task(task);
+               return -4;
+       }
+       bzero(scsi_cbdata, sizeof(struct iscsi_scsi_cbdata));
+       scsi_cbdata->task         = task;
+       scsi_cbdata->callback     = cb;
+       scsi_cbdata->private_data = private_data;
+
+       pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_SCSI_REQUEST, ISCSI_PDU_SCSI_RESPONSE);
+       if (pdu == NULL) {
+               printf("Failed to allocate text pdu\n");
+               iscsi_free_scsi_cbdata(scsi_cbdata);
+               return -5;
+       }
+       pdu->scsi_cbdata = scsi_cbdata;
+
+       /* flags */
+       flags = ISCSI_PDU_SCSI_FINAL|ISCSI_PDU_SCSI_ATTR_SIMPLE;
+       switch (task->xfer_dir) {
+       case SCSI_XFER_NONE:
+               break;
+       case SCSI_XFER_READ:
+               flags |= ISCSI_PDU_SCSI_READ;
+               break;
+       case SCSI_XFER_WRITE:
+               flags |= ISCSI_PDU_SCSI_WRITE;
+               if (data == NULL) {
+                       printf("DATA-OUT command but data == NULL\n");
+                       iscsi_free_pdu(iscsi, pdu);
+                       return -5;
+               }
+               if (data->size != task->expxferlen) {
+                       printf("data size:%d is not same as expected data transfer length:%d\n", data->size, task->expxferlen);
+                       iscsi_free_pdu(iscsi, pdu);
+                       return -7;
+               }
+               if (iscsi_pdu_add_data(iscsi, pdu, data->data, data->size) != 0) {
+                       printf("Failed to add outdata to the pdu\n");
+                       iscsi_free_pdu(iscsi, pdu);
+                       return -6;
+               }
+
+               break;
+       }
+       iscsi_pdu_set_pduflags(pdu, flags);
+
+       /* lun */
+       iscsi_pdu_set_lun(pdu, lun);
+
+       /* expxferlen */
+       iscsi_pdu_set_expxferlen(pdu, task->expxferlen);
+
+       /* cmdsn */
+       iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
+       pdu->cmdsn = iscsi->cmdsn;
+       iscsi->cmdsn++;
+
+       /* exp statsn */
+       iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1);
+               
+       /* cdb */
+       iscsi_pdu_set_cdb(pdu, task);
+
+       pdu->callback     = iscsi_scsi_response_cb;
+       pdu->private_data = scsi_cbdata;
+
+       if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+               printf("failed to queue iscsi scsi pdu\n");
+               iscsi_free_pdu(iscsi, pdu);
+               return -6;
+       }
+
+       return 0;
+}
+
+
+int iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
+{
+       int statsn, flags, response, status;
+       struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
+       struct scsi_task *task = scsi_cbdata->task;
+
+       statsn = ntohl(*(uint32_t *)&hdr[24]);
+       if (statsn > (int)iscsi->statsn) {
+               iscsi->statsn = statsn;
+       }
+
+       flags = hdr[1];
+       if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
+               printf("scsi response pdu but Final bit is not set: 0x%02x\n", flags);
+               pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
+               return -1;
+       }
+       if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
+               printf("scsi response asked for ACK 0x%02x\n", flags);
+               pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
+               return -1;
+       }
+       /* for now, we ignore all overflow/underflow flags. We just print/log them so we can tweak
+        * libiscsi to not generate under/over flows
+        */
+       if ((flags&ISCSI_PDU_DATA_BIDIR_OVERFLOW) != 0) {
+               printf("scsi response contains bidir overflow 0x%02x\n", flags);
+       }
+       if ((flags&ISCSI_PDU_DATA_BIDIR_UNDERFLOW) != 0) {
+               printf("scsi response contains bidir underflow 0x%02x\n", flags);
+       }
+       if ((flags&ISCSI_PDU_DATA_RESIDUAL_OVERFLOW) != 0) {
+               printf("scsi response contains residual overflow 0x%02x\n", flags);
+       }
+       if ((flags&ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW) != 0) {
+               printf("scsi response contains residual underflow 0x%02x\n", flags);
+       }
+
+
+       response = hdr[2];
+       if (response != 0x00) {
+               printf("scsi reply response field:%d\n", response);
+       }
+
+       status = hdr[3];
+
+       switch (status) {
+       case ISCSI_STATUS_GOOD:
+               task->datain.data = pdu->indata.data;
+               task->datain.size = pdu->indata.size;
+
+               pdu->callback(iscsi, ISCSI_STATUS_GOOD, &pdu->indata, pdu->private_data);
+               break;
+       case ISCSI_STATUS_CHECK_CONDITION:
+               task->datain.data = discard_const(hdr + ISCSI_HEADER_SIZE);
+               task->datain.size = size - ISCSI_HEADER_SIZE;
+
+               task->sense.error_type = task->datain.data[2] & 0x7f;
+               task->sense.key        = task->datain.data[4] & 0x0f;
+               task->sense.ascq       = ntohs(*(uint16_t *)&(task->datain.data[14]));
+
+               pdu->callback(iscsi, ISCSI_STATUS_CHECK_CONDITION, task, pdu->private_data);
+               break;
+       default:
+               printf("Unknown status :%d\n", status);
+
+               pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
+               return -1;
+       }
+
+       return 0;
+}
+
+int iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size, int *is_finished)
+{
+       int statsn, flags, status;
+       struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
+       struct scsi_task *task = scsi_cbdata->task;
+       int dsl;
+
+       statsn = ntohl(*(uint32_t *)&hdr[24]);
+       if (statsn > (int)iscsi->statsn) {
+               iscsi->statsn = statsn;
+       }
+
+       flags = hdr[1];
+       if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
+               printf("scsi response asked for ACK 0x%02x\n", flags);
+               pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
+               return -1;
+       }
+       /* for now, we ignore all overflow/underflow flags. We just print/log them so we can tweak
+        * libiscsi to not generate under/over flows
+        */
+       if ((flags&ISCSI_PDU_DATA_RESIDUAL_OVERFLOW) != 0) {
+               printf("scsi response contains residual overflow 0x%02x\n", flags);
+       }
+       if ((flags&ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW) != 0) {
+               printf("scsi response contains residual underflow 0x%02x\n", flags);
+       }
+
+       dsl = ntohl(*(uint32_t *)&hdr[4])&0x00ffffff;
+
+       if (dsl > size - ISCSI_HEADER_SIZE) {
+               printf ("dsl is :%d, while buffser size if %d\n", dsl, size - ISCSI_HEADER_SIZE);
+       }
+
+       if (iscsi_add_data(&pdu->indata, discard_const(hdr + ISCSI_HEADER_SIZE), dsl, 0) != 0) {
+               printf("failed to add data to pdu in buffer\n");
+               return -3;
+       }
+
+
+       if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
+               printf("scsi data-in without Final bit: 0x%02x\n", flags);
+               *is_finished = 0;
+       }
+       if ((flags&ISCSI_PDU_DATA_CONTAINS_STATUS) == 0) {
+               printf("scsi data-in without Status bit: 0x%02x\n", flags);
+               *is_finished = 0;
+       }
+
+       if (*is_finished == 0) {
+               return 0;
+       }
+
+
+       /* this was the final data-in packet in the sequence and it has the s-bit set, so invoke the
+        * callback.
+        */
+       status = hdr[3];
+       task->datain.data = pdu->indata.data;
+       task->datain.size = pdu->indata.size;
+
+       pdu->callback(iscsi, status, task, pdu->private_data);
+
+       return 0;
+}
+
+
+
+
+/*
+ * SCSI commands
+ */
+
+int iscsi_testunitready_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data)
+{
+       struct scsi_task *task;
+       int ret;
+
+       if ((task = scsi_cdb_testunitready()) == NULL) {
+               printf("Failed to create testunitready cdb\n");
+               return -1;
+       }
+       ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+       return ret;
+}
+
+
+int iscsi_reportluns_async(struct iscsi_context *iscsi, iscsi_command_cb cb, int report_type, int alloc_len, void *private_data)
+{
+       struct scsi_task *task;
+       int ret;
+
+       if (alloc_len < 16) {
+               printf("Minimum allowed alloc len for reportluns is 16. You specified %d\n", alloc_len);
+               return -1;
+       }
+
+       if ((task = scsi_reportluns_cdb(report_type, alloc_len)) == NULL) {
+               printf("Failed to create reportluns cdb\n");
+               return -2;
+       }
+       /* report luns are always sent to lun 0 */
+       ret = iscsi_scsi_command_async(iscsi, 0, task, cb, NULL, private_data);
+
+       return ret;
+}
+
+int iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int evpd, int page_code, int maxsize, void *private_data)
+{
+       struct scsi_task *task;
+       int ret;
+
+       if ((task = scsi_cdb_inquiry(evpd, page_code, maxsize)) == NULL) {
+               printf("Failed to create inquiry cdb\n");
+               return -1;
+       }
+       ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+       return ret;
+}
+
+int iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int pmi, void *private_data)
+{
+       struct scsi_task *task;
+       int ret;
+
+       if ((task = scsi_cdb_readcapacity10(lba, pmi)) == NULL) {
+               printf("Failed to create readcapacity10 cdb\n");
+               return -1;
+       }
+       ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+       return ret;
+}
+
+int iscsi_read10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int datalen, int blocksize, void *private_data)
+{
+       struct scsi_task *task;
+       int ret;
+
+       if (datalen % blocksize != 0) {
+               printf("datalen:%d is not a multiple of the blocksize:%d\n", datalen, blocksize);
+               return -1;
+       }
+
+       if ((task = scsi_cdb_read10(lba, datalen, blocksize)) == NULL) {
+               printf("Failed to create read10 cdb\n");
+               return -2;
+       }
+       ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+       return ret;
+}
+
+
+int iscsi_write10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, unsigned char *data, int datalen, int lba, int fua, int fuanv, int blocksize, void *private_data)
+{
+       struct scsi_task *task;
+       struct iscsi_data outdata;
+       int ret;
+
+       if (datalen % blocksize != 0) {
+               printf("datalen:%d is not a multiple of the blocksize:%d\n", datalen, blocksize);
+               return -1;
+       }
+
+       if ((task = scsi_cdb_write10(lba, datalen, fua, fuanv, blocksize)) == NULL) {
+               printf("Failed to create read10 cdb\n");
+               return -2;
+       }
+
+       outdata.data = data;
+       outdata.size = datalen;
+
+       ret = iscsi_scsi_command_async(iscsi, lun, task, cb, &outdata, private_data);
+
+       return ret;
+}
+
+int iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, void *private_data)
+{
+       struct scsi_task *task;
+       int ret;
+
+       if ((task = scsi_cdb_modesense6(dbd, pc, page_code, sub_page_code, alloc_len)) == NULL) {
+               printf("Failed to create modesense6 cdb\n");
+               return -2;
+       }
+       ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+       return ret;
+}
+
diff --git a/ccan/iscsi/scsi-lowlevel.c b/ccan/iscsi/scsi-lowlevel.c
new file mode 100644 (file)
index 0000000..7090401
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+   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 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/>.
+*/
+/*
+ * would be nice if this could grow into a full blown library for scsi to
+ * 1, build a CDB
+ * 2, check how big a complete data-in structure needs to be
+ * 3, unmarshall data-in into a real structure
+ * 4, marshall a real structure into a data-out blob
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <strings.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <ccan/compiler/compiler.h>
+#include "scsi-lowlevel.h"
+#include "dlinklist.h"
+
+
+void scsi_free_scsi_task(struct scsi_task *task)
+{
+       struct scsi_allocated_memory *mem;
+
+       while((mem = task->mem)) {
+                  DLIST_REMOVE(task->mem, mem);
+                  free(mem);
+       }
+       free(task);
+}
+
+static void *scsi_malloc(struct scsi_task *task, size_t size)
+{
+       struct scsi_allocated_memory *mem;
+
+       mem = malloc(sizeof(struct scsi_allocated_memory));
+       if (mem == NULL) {
+               printf("Failed to allocate memory to scsi task\n");
+               return NULL;
+       }
+       bzero(mem, sizeof(struct scsi_allocated_memory));
+       mem->ptr = malloc(size);
+       if (mem->ptr == NULL) {
+               printf("Failed to allocate memory buffer for scsi task\n");
+               free(mem);
+               return NULL;
+       }
+       bzero(mem->ptr, size);
+       DLIST_ADD(task->mem, mem);
+       return mem->ptr;
+}
+
+/*
+ * TESTUNITREADY
+ */
+struct scsi_task *scsi_cdb_testunitready(void)
+{
+       struct scsi_task *task;
+
+       task = malloc(sizeof(struct scsi_task));
+       if (task == NULL) {
+               printf("Failed to allocate scsi task structure\n");
+               return NULL;
+       }
+
+       bzero(task, sizeof(struct scsi_task));
+       task->cdb[0]   = SCSI_OPCODE_TESTUNITREADY;
+
+       task->cdb_size   = 6;
+       task->xfer_dir   = SCSI_XFER_NONE;
+       task->expxferlen = 0;
+
+       return task;
+}
+
+
+/*
+ * REPORTLUNS
+ */
+struct scsi_task *scsi_reportluns_cdb(int report_type, int alloc_len)
+{
+       struct scsi_task *task;
+
+       task = malloc(sizeof(struct scsi_task));
+       if (task == NULL) {
+               printf("Failed to allocate scsi task structure\n");
+               return NULL;
+       }
+
+       bzero(task, sizeof(struct scsi_task));
+       task->cdb[0]   = SCSI_OPCODE_REPORTLUNS;
+       task->cdb[2]   = report_type;
+       *(uint32_t *)&task->cdb[6] = htonl(alloc_len);
+
+       task->cdb_size = 12;
+       task->xfer_dir = SCSI_XFER_READ;
+       task->expxferlen = alloc_len;
+
+       task->params.reportluns.report_type = report_type;
+
+       return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full report luns datain structure
+ */
+static int scsi_reportluns_datain_getfullsize(struct scsi_task *task)
+{
+       uint32_t list_size;
+
+       list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
+       
+       return list_size;
+}
+
+/*
+ * unmarshall the data in blob for reportluns into a structure
+ */
+static struct scsi_reportluns_list *scsi_reportluns_datain_unmarshall(struct scsi_task *task)
+{
+       struct scsi_reportluns_list *list;
+       int list_size;
+       int i, num_luns;
+
+       if (task->datain.size < 4) {
+               printf("not enough data for reportluns list length\n");
+               return NULL;
+       }
+
+       list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
+       if (list_size < task->datain.size) {
+               printf("not enough data to unmarshall reportluns data\n");
+               return NULL;
+       }
+
+       num_luns = list_size / 8 - 1;
+       list = scsi_malloc(task, offsetof(struct scsi_reportluns_list, luns) + sizeof(uint16_t) * num_luns);
+       if (list == NULL) {
+               printf("Failed to allocate reportluns structure\n");
+               return NULL;
+       }
+
+       list->num = num_luns;
+       for (i=0; i<num_luns; i++) {
+               list->luns[i] = htons(*(uint16_t *)&(task->datain.data[i*8+8]));
+       }
+       
+       return list;
+}
+
+
+/*
+ * READCAPACITY10
+ */
+struct scsi_task *scsi_cdb_readcapacity10(int lba, int pmi)
+{
+       struct scsi_task *task;
+
+       task = malloc(sizeof(struct scsi_task));
+       if (task == NULL) {
+               printf("Failed to allocate scsi task structure\n");
+               return NULL;
+       }
+
+       bzero(task, sizeof(struct scsi_task));
+       task->cdb[0]   = SCSI_OPCODE_READCAPACITY10;
+
+       *(uint32_t *)&task->cdb[2] = htonl(lba);
+
+       if (pmi) {
+               task->cdb[8] |= 0x01;
+       }
+
+       task->cdb_size = 10;
+       task->xfer_dir = SCSI_XFER_READ;
+       task->expxferlen = 8;
+
+       task->params.readcapacity10.lba = lba;
+       task->params.readcapacity10.pmi = pmi;
+
+       return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full readcapacity10 datain structure
+ */
+static int scsi_readcapacity10_datain_getfullsize(struct scsi_task *task
+                                                 UNUSED)
+{
+       return 8;
+}
+
+/*
+ * unmarshall the data in blob for readcapacity10 into a structure
+ */
+static struct scsi_readcapacity10 *scsi_readcapacity10_datain_unmarshall(struct scsi_task *task)
+{
+       struct scsi_readcapacity10 *rc10;
+
+       if (task->datain.size < 8) {
+               printf("Not enough data to unmarshall readcapacity10\n");
+               return NULL;
+       }
+       rc10 = malloc(sizeof(struct scsi_readcapacity10));
+       if (rc10 == NULL) {
+               printf("Failed to allocate readcapacity10 structure\n");
+               return NULL;
+       }
+
+       rc10->lba        = htonl(*(uint32_t *)&(task->datain.data[0]));
+       rc10->block_size = htonl(*(uint32_t *)&(task->datain.data[4]));
+
+       return rc10;
+}
+
+
+
+
+
+/*
+ * INQUIRY
+ */
+struct scsi_task *scsi_cdb_inquiry(int evpd, int page_code, int alloc_len)
+{
+       struct scsi_task *task;
+
+       task = malloc(sizeof(struct scsi_task));
+       if (task == NULL) {
+               printf("Failed to allocate scsi task structure\n");
+               return NULL;
+       }
+
+       bzero(task, sizeof(struct scsi_task));
+       task->cdb[0]   = SCSI_OPCODE_INQUIRY;
+
+       if (evpd) {
+               task->cdb[1] |= 0x01;
+       }
+
+       task->cdb[2] = page_code;
+
+       *(uint16_t *)&task->cdb[3] = htons(alloc_len);
+
+       task->cdb_size = 6;
+       task->xfer_dir = SCSI_XFER_READ;
+       task->expxferlen = alloc_len;
+
+       task->params.inquiry.evpd      = evpd;
+       task->params.inquiry.page_code = page_code;
+
+       return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full inquiry datain structure
+ */
+static int scsi_inquiry_datain_getfullsize(struct scsi_task *task)
+{
+       if (task->params.inquiry.evpd != 0) {
+               printf("Can not handle extended inquiry yet\n");
+               return -1;
+       }
+
+       /* standard inquiry*/
+       return task->datain.data[4] + 3;
+}
+
+/*
+ * unmarshall the data in blob for inquiry into a structure
+ */
+static void *scsi_inquiry_datain_unmarshall(struct scsi_task *task)
+{
+       struct scsi_inquiry_standard *inq;
+
+       if (task->params.inquiry.evpd != 0) {
+               printf("Can not handle extended inquiry yet\n");
+               return NULL;
+       }
+
+       /* standard inquiry */
+       inq = scsi_malloc(task, sizeof(struct scsi_inquiry_standard));
+       if (inq == NULL) {
+               printf("Failed to allocate standard inquiry structure\n");
+               return NULL;
+       }
+
+       inq->periperal_qualifier    = (task->datain.data[0]>>5)&0x07;
+       inq->periperal_device_type  = task->datain.data[0]&0x1f;
+       inq->rmb                    = task->datain.data[1]&0x80;
+       inq->version                = task->datain.data[2];
+       inq->normaca                = task->datain.data[3]&0x20;
+       inq->hisup                  = task->datain.data[3]&0x10;
+       inq->response_data_format   = task->datain.data[3]&0x0f;
+
+       memcpy(&inq->vendor_identification[0], &task->datain.data[8], 8);
+       memcpy(&inq->product_identification[0], &task->datain.data[16], 16);
+       memcpy(&inq->product_revision_level[0], &task->datain.data[32], 4);
+
+       return inq;
+}
+
+/*
+ * READ10
+ */
+struct scsi_task *scsi_cdb_read10(int lba, int xferlen, int blocksize)
+{
+       struct scsi_task *task;
+
+       task = malloc(sizeof(struct scsi_task));
+       if (task == NULL) {
+               printf("Failed to allocate scsi task structure\n");
+               return NULL;
+       }
+
+       bzero(task, sizeof(struct scsi_task));
+       task->cdb[0]   = SCSI_OPCODE_READ10;
+
+       *(uint32_t *)&task->cdb[2] = htonl(lba);
+       *(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
+
+       task->cdb_size = 10;
+       task->xfer_dir = SCSI_XFER_READ;
+       task->expxferlen = xferlen;
+
+       return task;
+}
+
+/*
+ * WRITE10
+ */
+struct scsi_task *scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv, int blocksize)
+{
+       struct scsi_task *task;
+
+       task = malloc(sizeof(struct scsi_task));
+       if (task == NULL) {
+               printf("Failed to allocate scsi task structure\n");
+               return NULL;
+       }
+
+       bzero(task, sizeof(struct scsi_task));
+       task->cdb[0]   = SCSI_OPCODE_WRITE10;
+
+       if (fua) {
+               task->cdb[1] |= 0x08;
+       }
+       if (fuanv) {
+               task->cdb[1] |= 0x02;
+       }
+
+       *(uint32_t *)&task->cdb[2] = htonl(lba);
+       *(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
+
+       task->cdb_size = 10;
+       task->xfer_dir = SCSI_XFER_WRITE;
+       task->expxferlen = xferlen;
+
+       return task;
+}
+
+
+
+/*
+ * MODESENSE6
+ */
+struct scsi_task *scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len)
+{
+       struct scsi_task *task;
+
+       task = malloc(sizeof(struct scsi_task));
+       if (task == NULL) {
+               printf("Failed to allocate scsi task structure\n");
+               return NULL;
+       }
+
+       bzero(task, sizeof(struct scsi_task));
+       task->cdb[0]   = SCSI_OPCODE_MODESENSE6;
+
+       if (dbd) {
+               task->cdb[1] |= 0x08;
+       }
+       task->cdb[2] = pc<<6 | page_code;
+       task->cdb[3] = sub_page_code;
+       task->cdb[4] = alloc_len;
+
+       task->cdb_size = 6;
+       task->xfer_dir = SCSI_XFER_READ;
+       task->expxferlen = alloc_len;
+
+       task->params.modesense6.dbd           = dbd;
+       task->params.modesense6.pc            = pc;
+       task->params.modesense6.page_code     = page_code;
+       task->params.modesense6.sub_page_code = sub_page_code;
+       return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full report luns datain structure
+ */
+static int scsi_modesense6_datain_getfullsize(struct scsi_task *task)
+{
+       int len;
+
+       len = task->datain.data[0] + 1;
+
+       return len;
+}
+
+
+
+int scsi_datain_getfullsize(struct scsi_task *task)
+{
+       switch (task->cdb[0]) {
+       case SCSI_OPCODE_TESTUNITREADY:
+               return 0;
+       case SCSI_OPCODE_INQUIRY:
+               return scsi_inquiry_datain_getfullsize(task);
+       case SCSI_OPCODE_MODESENSE6:
+               return scsi_modesense6_datain_getfullsize(task);
+       case SCSI_OPCODE_READCAPACITY10:
+               return scsi_readcapacity10_datain_getfullsize(task);
+//     case SCSI_OPCODE_READ10:
+//     case SCSI_OPCODE_WRITE10:
+       case SCSI_OPCODE_REPORTLUNS:
+               return scsi_reportluns_datain_getfullsize(task);
+       }
+       printf("Unknown opcode:%d for datain get full size\n", task->cdb[0]);
+       return -1;
+}
+
+void *scsi_datain_unmarshall(struct scsi_task *task)
+{
+       switch (task->cdb[0]) {
+       case SCSI_OPCODE_TESTUNITREADY:
+               return NULL;
+       case SCSI_OPCODE_INQUIRY:
+               return scsi_inquiry_datain_unmarshall(task);
+       case SCSI_OPCODE_READCAPACITY10:
+               return scsi_readcapacity10_datain_unmarshall(task);
+//     case SCSI_OPCODE_READ10:
+//     case SCSI_OPCODE_WRITE10:
+       case SCSI_OPCODE_REPORTLUNS:
+               return scsi_reportluns_datain_unmarshall(task);
+       }
+       printf("Unknown opcode:%d for datain unmarshall\n", task->cdb[0]);
+       return NULL;
+}
+
diff --git a/ccan/iscsi/scsi-lowlevel.h b/ccan/iscsi/scsi-lowlevel.h
new file mode 100644 (file)
index 0000000..3d477fc
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+   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 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/>.
+*/
+#ifndef CCAN_ISCSI_SCSI_LOWLEVEL_H
+#define CCAN_ISCSI_SCSI_LOWLEVEL_H
+
+#define SCSI_CDB_MAX_SIZE                      16
+
+enum scsi_opcode {SCSI_OPCODE_TESTUNITREADY=0x00,
+                 SCSI_OPCODE_INQUIRY=0x12,
+                 SCSI_OPCODE_MODESENSE6=0x1a,
+                 SCSI_OPCODE_READCAPACITY10=0x25,
+                 SCSI_OPCODE_READ10=0x28,
+                 SCSI_OPCODE_WRITE10=0x2A,
+                 SCSI_OPCODE_REPORTLUNS=0xA0};
+
+/* sense keys */
+#define SCSI_SENSE_KEY_ILLEGAL_REQUEST                 0x05
+#define SCSI_SENSE_KEY_UNIT_ATTENTION                  0x06
+
+/* ascq */
+#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB           0x2400
+#define SCSI_SENSE_ASCQ_BUS_RESET                      0x2900
+
+enum scsi_xfer_dir {SCSI_XFER_NONE=0,
+                   SCSI_XFER_READ=1,
+                   SCSI_XFER_WRITE=2};
+
+struct scsi_reportluns_params {
+       int report_type;
+};
+struct scsi_readcapacity10_params {
+       int lba;
+       int pmi;
+};
+struct scsi_inquiry_params {
+       int evpd;
+       int page_code;
+};
+struct scsi_modesense6_params {
+       int dbd;
+       int pc;
+       int page_code;
+       int sub_page_code;
+};
+
+struct scsi_sense {
+       unsigned char error_type;
+       unsigned char key;
+       int           ascq;
+};
+
+struct scsi_data {
+       int size;
+       unsigned char *data;
+};
+
+struct scsi_allocated_memory {
+       struct scsi_allocated_memory *prev, *next;
+       void *ptr;
+};
+
+struct scsi_task {
+       int cdb_size;
+       int xfer_dir;
+       int expxferlen;
+       unsigned char cdb[SCSI_CDB_MAX_SIZE];
+       union {
+                    struct scsi_readcapacity10_params readcapacity10;
+                    struct scsi_reportluns_params     reportluns;
+                    struct scsi_inquiry_params        inquiry;
+            struct scsi_modesense6_params     modesense6;
+       } params;
+
+       struct scsi_sense sense;
+       struct scsi_data datain;
+       struct scsi_allocated_memory *mem;
+};
+
+void scsi_free_scsi_task(struct scsi_task *task);
+
+
+/*
+ * TESTUNITREADY
+ */
+struct scsi_task *scsi_cdb_testunitready(void);
+
+
+/*
+ * REPORTLUNS
+ */
+#define SCSI_REPORTLUNS_REPORT_ALL_LUNS                                0x00
+#define SCSI_REPORTLUNS_REPORT_WELL_KNOWN_ONLY                 0x01
+#define SCSI_REPORTLUNS_REPORT_AVAILABLE_LUNS_ONLY             0x02
+
+struct scsi_reportluns_list {
+       uint32_t num;
+       uint16_t luns[0];
+};
+
+struct scsi_task *scsi_reportluns_cdb(int report_type, int alloc_len);
+
+/*
+ * READCAPACITY10
+ */
+struct scsi_readcapacity10 {
+       uint32_t lba;
+       uint32_t block_size;
+};
+struct scsi_task *scsi_cdb_readcapacity10(int lba, int pmi);
+
+
+/*
+ * INQUIRY
+ */
+enum scsi_inquiry_peripheral_qualifier {SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED=0x00,
+                                       SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED=0x01,
+                                       SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED=0x03};
+
+enum scsi_inquiry_peripheral_device_type {SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS=0x00,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS=0x01,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER=0x02,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR=0x03,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE=0x04,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC=0x05,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER=0x06,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY=0x07,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER=0x08,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS=0x09,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER=0x0c,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES=0x0d,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS=0x0e,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER=0x0f,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER=0x10,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD=0x11,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION=0x12,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER=0x13,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN=0x1e,
+                                         SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN=0x1f};
+
+struct scsi_inquiry_standard {
+       enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
+       enum scsi_inquiry_peripheral_device_type periperal_device_type;
+       int rmb;
+       int version;
+       int normaca;
+       int hisup;
+       int response_data_format;
+
+       char vendor_identification[8+1];
+       char product_identification[16+1];
+       char product_revision_level[4+1];
+};
+
+struct scsi_task *scsi_cdb_inquiry(int evpd, int page_code, int alloc_len);
+
+
+
+/*
+ * MODESENSE6
+ */
+enum scsi_modesense_page_control {SCSI_MODESENSE_PC_CURRENT=0x00,
+                                 SCSI_MODESENSE_PC_CHANGEABLE=0x01,
+                                 SCSI_MODESENSE_PC_DEFAULT=0x02,
+                                 SCSI_MODESENSE_PC_SAVED=0x03};
+
+enum scsi_modesense_page_code {SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES=0x3f};
+
+struct scsi_task *scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len);
+
+
+
+
+int scsi_datain_getfullsize(struct scsi_task *task);
+void *scsi_datain_unmarshall(struct scsi_task *task);
+
+struct scsi_task *scsi_cdb_read10(int lba, int xferlen, int blocksize);
+struct scsi_task *scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv, int blocksize);
+
+#endif /* CCAN_ISCSI_SCSI_LOWLEVEL_H */
diff --git a/ccan/iscsi/socket.c b/ccan/iscsi/socket.c
new file mode 100644 (file)
index 0000000..ce3526e
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+   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 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/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "dlinklist.h"
+
+static void set_nonblocking(int fd)
+{
+       unsigned v;
+       v = fcntl(fd, F_GETFL, 0);
+        fcntl(fd, F_SETFL, v | O_NONBLOCK);
+}
+
+int iscsi_connect_async(struct iscsi_context *iscsi, const char *target, iscsi_command_cb cb, void *private_data)
+{
+       int tpgt = -1;
+       int port = 3260;
+       char *str;
+       char *addr;
+       union {
+               struct sockaddr sa;
+               struct sockaddr_storage ss;
+               struct sockaddr_in sin;
+       } s;
+       int socksize;
+
+       if (iscsi == NULL) {
+               printf("Trying to connect NULL context\n");
+               return -1;
+       }
+       if (iscsi->fd != -1) {
+               printf("Trying to connect but already connected\n");
+               return -2;
+       }
+
+       addr = strdup(target);
+       if (addr == NULL) {
+               printf("failed to strdup target address\n");
+               return -3;
+       }
+       
+       /* check if we have a target portal group tag */
+       if ((str = rindex(addr, ',')) != NULL) {
+               tpgt = atoi(str+1);
+               str[0] = 0;
+       }
+
+       /* XXX need handling for {ipv6 addresses} */
+       /* for now, assume all is ipv4 */
+       if ((str = rindex(addr, ':')) != NULL) {
+               port = atoi(str+1);
+               str[0] = 0;
+       }
+
+       s.sin.sin_family = AF_INET;
+       s.sin.sin_port   = htons(port);
+       if (inet_pton(AF_INET, addr, &s.sin.sin_addr) != 1) {
+               printf("failed to convert to ip address\n");
+               free(addr);
+               return -4;
+       }
+       free(addr);
+
+       switch (s.ss.ss_family) {
+       case AF_INET:
+               iscsi->fd = socket(AF_INET, SOCK_STREAM, 0);
+               socksize = sizeof(struct sockaddr_in);
+               break;
+       default:
+               printf("Unknown family :%d\n", s.ss.ss_family);
+               return -5;
+
+       }
+
+       if (iscsi->fd == -1) {
+               printf("Failed to open socket\n");
+               return -6;
+
+       }
+
+       iscsi->connect_cb  = cb;
+       iscsi->connect_data = private_data;
+
+       set_nonblocking(iscsi->fd);
+
+       if (connect(iscsi->fd, &s.sa, socksize) != 0 && errno != EINPROGRESS) {
+               printf("Connect failed errno : %s (%d)\n", strerror(errno), errno);
+               return -7;
+       }
+
+       return 0;
+}
+
+int iscsi_disconnect(struct iscsi_context *iscsi)
+{
+       if (iscsi == NULL) {
+               printf("Trying to disconnect NULL context\n");
+               return -1;
+       }
+       if (iscsi->is_loggedin != 0) {
+               printf("Trying to disconnect while logged in\n");
+               return -2;
+       }
+       if (iscsi->fd == -1) {
+               printf("Trying to disconnect but not connected\n");
+               return -3;
+       }
+
+       close(iscsi->fd);
+       iscsi->fd  = -1;
+
+       iscsi->is_connected = 0;
+
+       return 0;
+}
+
+int iscsi_get_fd(struct iscsi_context *iscsi)
+{
+       if (iscsi == NULL) {
+               printf("Trying to get fd for NULL context\n");
+               return -1;
+       }
+
+       return iscsi->fd;
+}
+
+int iscsi_which_events(struct iscsi_context *iscsi)
+{
+       int events = POLLIN;
+
+       if (iscsi->is_connected == 0) {
+               events |= POLLOUT;
+       }
+
+       if (iscsi->outqueue) {
+               events |= POLLOUT;
+       }
+       return events;
+}
+
+static int iscsi_read_from_socket(struct iscsi_context *iscsi)
+{
+       int available;
+       int size;
+       unsigned char *buf;
+       ssize_t count;
+
+       if (ioctl(iscsi->fd, FIONREAD, &available) != 0) {
+               printf("ioctl FIONREAD returned error : %d\n", errno);
+               return -1;
+       }
+       if (available == 0) {
+               printf("no data readable in socket, socket is closed\n");
+               return -2;
+       }
+       size = iscsi->insize - iscsi->inpos + available;
+       buf = malloc(size);
+       if (buf == NULL) {
+               printf("failed to allocate %d bytes for input buffer\n", size);
+               return -3;
+       }
+       if (iscsi->insize > iscsi->inpos) {
+               memcpy(buf, iscsi->inbuf + iscsi->inpos, iscsi->insize - iscsi->inpos);
+               iscsi->insize -= iscsi->inpos;
+               iscsi->inpos   = 0;
+       }
+
+       count = read(iscsi->fd, buf + iscsi->insize, available);
+       if (count == -1) {
+               if (errno == EINTR) {
+                       free(buf);
+                       buf = NULL;
+                       return 0;
+               }
+               printf("read from socket failed, errno:%d\n", errno);
+               free(buf);
+               buf = NULL;
+               return -4;
+       }
+
+       if (iscsi->inbuf != NULL) {
+               free(iscsi->inbuf);
+       }
+       iscsi->inbuf   = buf;
+       iscsi->insize += count;
+
+       while (1) {
+               if (iscsi->insize - iscsi->inpos < 48) {
+                       return 0;
+               }
+               count = iscsi_get_pdu_size(iscsi->inbuf + iscsi->inpos);
+               if (iscsi->insize + iscsi->inpos < count) {
+                       return 0;
+               }
+               if (iscsi_process_pdu(iscsi, iscsi->inbuf + iscsi->inpos, count) != 0) {
+                       printf("failed to process pdu\n");
+                       return -5;
+               }
+               iscsi->inpos += count;
+               if (iscsi->inpos == iscsi->insize) {
+                       free(iscsi->inbuf);
+                       iscsi->inbuf = NULL;
+                       iscsi->insize = 0;
+                       iscsi->inpos = 0;
+               }
+               if (iscsi->inpos > iscsi->insize) {
+                       printf("inpos > insize. bug!\n");
+                       return -6;
+               }
+       }
+
+       return 0;
+}
+
+static int iscsi_write_to_socket(struct iscsi_context *iscsi)
+{
+       ssize_t count;
+
+       if (iscsi == NULL) {
+               printf("trying to write to socket for NULL context\n");
+               return -1;
+       }
+       if (iscsi->fd == -1) {
+               printf("trying to write but not connected\n");
+               return -2;
+       }
+
+       while (iscsi->outqueue != NULL) {
+               ssize_t total;
+
+               total = iscsi->outqueue->outdata.size;
+               total = (total +3) & 0xfffffffc;
+
+               count = write(iscsi->fd, iscsi->outqueue->outdata.data + iscsi->outqueue->written, total - iscsi->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 :%d\n", errno);
+                       return -3;
+               }
+
+               iscsi->outqueue->written += count;
+               if (iscsi->outqueue->written == total) {
+                       struct iscsi_pdu *pdu = iscsi->outqueue;
+
+                       DLIST_REMOVE(iscsi->outqueue, pdu);
+                       DLIST_ADD_END(iscsi->waitpdu, pdu, NULL);
+               }
+       }
+       return 0;
+}
+
+int iscsi_service(struct iscsi_context *iscsi, int revents)
+{
+       if (revents & POLLERR) {
+               printf("iscsi_service: POLLERR, socket error\n");
+               iscsi->connect_cb(iscsi, ISCSI_STATUS_ERROR, NULL, iscsi->connect_data);
+               return -1;
+       }
+       if (revents & POLLHUP) {
+               printf("iscsi_service: POLLHUP, socket error\n");
+               iscsi->connect_cb(iscsi, ISCSI_STATUS_ERROR, NULL, iscsi->connect_data);
+               return -2;
+       }
+
+       if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
+               iscsi->is_connected = 1;
+               iscsi->connect_cb(iscsi, ISCSI_STATUS_GOOD, NULL, iscsi->connect_data);
+               return 0;
+       }
+
+       if (revents & POLLOUT && iscsi->outqueue != NULL) {
+               if (iscsi_write_to_socket(iscsi) != 0) {
+                       printf("write to socket failed\n");
+                       return -3;
+               }
+       }
+       if (revents & POLLIN) {
+               if (iscsi_read_from_socket(iscsi) != 0) {
+                       printf("read from socket failed\n");
+                       return -4;
+               }
+       }
+
+       return 0;
+}
+
+int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
+{
+       if (iscsi == NULL) {
+               printf("trying to queue to NULL context\n");
+               return -1;
+       }
+       if (pdu == NULL) {
+               printf("trying to queue NULL pdu\n");
+               return -2;
+       }
+       DLIST_ADD_END(iscsi->outqueue, pdu, NULL);
+
+       return 0;
+}
+
+
+
diff --git a/ccan/iscsi/test/run.c b/ccan/iscsi/test/run.c
new file mode 100644 (file)
index 0000000..5a37c91
--- /dev/null
@@ -0,0 +1,24 @@
+#include <ccan/iscsi/iscsi.h>
+#include <ccan/iscsi/discovery.c>
+#include <ccan/iscsi/socket.c>
+#include <ccan/iscsi/init.c>
+#include <ccan/iscsi/pdu.c>
+#include <ccan/iscsi/scsi-lowlevel.c>
+#include <ccan/iscsi/nop.c>
+#include <ccan/iscsi/login.c>
+#include <ccan/iscsi/scsi-command.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+       struct iscsi_context *iscsi;
+
+       plan_tests(2);
+
+       iscsi = iscsi_create_context("some name");
+       ok1(iscsi);
+       ok1(iscsi_destroy_context(iscsi) == 0);
+
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/iscsi/tools/iscsiclient.c b/ccan/iscsi/tools/iscsiclient.c
new file mode 100644 (file)
index 0000000..28df8c2
--- /dev/null
@@ -0,0 +1,430 @@
+/* This is an example of using libiscsi.
+ * It basically logs in to the the target and performs a discovery.
+ * It then selects the last target in the returned list and
+ * starts a normal login to that target.
+ * Once logged in it issues a REPORTLUNS call and selects the last returned lun in the list.
+ * This LUN is then used to send INQUIRY, READCAPACITY10 and READ10 test calls to.
+ */
+/* The reason why we have to specify an allocation length and sometimes probe, starting with a small value, probing how big the buffer 
+ * should be, and asking again with a bigger buffer.
+ * Why not just always ask with a buffer that is big enough?
+ * The reason is that a lot of scsi targets are "sensitive" and ""buggy""
+ * many targets will just fail the operation completely if they thing alloc len is unreasonably big.
+ */
+
+/* This is the host/port we connect to.*/
+#define TARGET "10.1.1.27:3260"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <poll.h>
+#include <ccan/iscsi/iscsi.h>
+#include <ccan/iscsi/scsi-lowlevel.h>
+
+struct client_state {
+       char *message;
+       int has_discovered_target;
+       char *target_name;
+       char *target_address;
+       int lun;
+       int block_size;
+};
+
+void nop_out_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       struct iscsi_data *data = command_data;
+
+       printf("NOP-IN status:%d\n", status);
+       if (data->size > 0) {
+               printf("NOP-IN data:%s\n", data->data);
+       }
+       exit(10);
+}
+
+
+void write10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       struct scsi_task *task = command_data;
+       int i;
+
+       if (status == ISCSI_STATUS_CHECK_CONDITION) {
+
+               printf("Write10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+               exit(10);
+       }
+
+       printf("Write successful\n");
+       exit(10);
+}
+
+
+void read10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       struct scsi_task *task = command_data;
+       int i;
+
+       if (status == ISCSI_STATUS_CHECK_CONDITION) {
+               printf("Read10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+               exit(10);
+       }
+
+       printf("READ10 successful. Block content:\n");
+       for (i=0;i<task->datain.size;i++) {
+               printf("%02x ", task->datain.data[i]);
+               if (i%16==15)
+                       printf("\n");
+               if (i==69)
+                       break;
+       }
+       printf("...\n");
+
+       printf("Finished,   wont try to write data since that will likely destroy your LUN :-(\n");
+       printf("Send NOP-OUT\n");
+       if (iscsi_nop_out_async(iscsi, nop_out_cb, "Ping!", 6, private_data) != 0) {
+               printf("failed to send nop-out\n");
+               exit(10);
+       }
+//     printf("write the block back\n");
+//     if (iscsi_write10_async(iscsi, clnt->lun, write10_cb, task->data.datain, task->datain.size, 0, 0, 0, clnt->block_size, private_data) != 0) {
+//             printf("failed to send write10 command\n");
+//             exit(10);
+//     }
+}
+
+void readcapacity10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       struct scsi_task *task = command_data;
+       struct scsi_readcapacity10 *rc10;
+       int full_size;
+
+       if (status == ISCSI_STATUS_CHECK_CONDITION) {
+               printf("Readcapacity10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+               exit(10);
+       }
+
+       full_size = scsi_datain_getfullsize(task);
+       if (full_size < task->datain.size) {
+               printf("not enough data for full size readcapacity10\n");
+               exit(10);
+       }
+
+       rc10 = scsi_datain_unmarshall(task);
+       if (rc10 == NULL) {
+               printf("failed to unmarshall readcapacity10 data\n");
+               exit(10);
+       }
+       clnt->block_size = rc10->block_size;
+       printf("READCAPACITY10 successful. Size:%d blocks  blocksize:%d. Read first block\n", rc10->lba, rc10->block_size);
+       free(rc10);
+
+       if (iscsi_read10_async(iscsi, clnt->lun, read10_cb, 0, clnt->block_size, clnt->block_size, private_data) != 0) {
+               printf("failed to send read10 command\n");
+               exit(10);
+       }
+}
+
+void modesense6_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       struct scsi_task *task = command_data;
+       int full_size;
+
+       if (status == ISCSI_STATUS_CHECK_CONDITION) {
+               printf("Modesense6 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+               exit(10);
+       }
+
+       full_size = scsi_datain_getfullsize(task);
+       if (full_size > task->datain.size) {
+               printf("did not get enough data for mode sense, sening modesense again asking for bigger buffer\n");
+               if (iscsi_modesense6_async(iscsi, clnt->lun, modesense6_cb, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES, 0, full_size, private_data) != 0) {
+                       printf("failed to send modesense6 command\n");
+                       exit(10);
+               }
+               return;
+       }
+
+       printf("MODESENSE6 successful.\n");
+       printf("Send READCAPACITY10\n");
+       if (iscsi_readcapacity10_async(iscsi, clnt->lun, readcapacity10_cb, 0, 0, private_data) != 0) {
+               printf("failed to send readcapacity command\n");
+               exit(10);
+       }
+}
+
+void inquiry_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       struct scsi_task *task = command_data;
+       struct scsi_inquiry_standard *inq;
+
+       if (status == ISCSI_STATUS_CHECK_CONDITION) {
+               printf("Inquiry failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+               exit(10);
+       }
+
+       printf("INQUIRY successful for standard data.\n");
+       inq = scsi_datain_unmarshall(task);
+       if (inq == NULL) {
+               printf("failed to unmarshall inquiry datain blob\n");
+               exit(10);
+       }
+
+       printf("Device Type is %d. VendorId:%s ProductId:%s\n", inq->periperal_device_type, inq->vendor_identification, inq->product_identification);
+       printf("Send MODESENSE6\n");
+       if (iscsi_modesense6_async(iscsi, clnt->lun, modesense6_cb, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES, 0, 4, private_data) != 0) {
+               printf("failed to send modesense6 command\n");
+               exit(10);
+       }
+
+}
+
+void testunitready_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       struct scsi_task *task = command_data;
+
+       if (status == ISCSI_STATUS_CHECK_CONDITION) {
+               printf("First testunitready failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+               if (task->sense.key == SCSI_SENSE_KEY_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) {
+                       printf("target device just came online, try again\n");
+
+                       if (iscsi_testunitready_async(iscsi, clnt->lun, testunitready_cb, private_data) != 0) {
+                               printf("failed to send testunitready command\n");
+                               exit(10);
+                       }
+               }
+               return;
+       }
+
+       printf("TESTUNITREADY successful, do an inquiry on lun:%d\n", clnt->lun);
+       if (iscsi_inquiry_async(iscsi, clnt->lun, inquiry_cb, 0, 0, 64, private_data) != 0) {
+               printf("failed to send inquiry command\n");
+               exit(10);
+       }
+}
+
+
+void reportluns_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       struct scsi_task *task = command_data;
+       struct scsi_reportluns_list *list;
+       uint32_t full_report_size;
+       int i;
+
+       if (status != ISCSI_STATUS_GOOD) {
+               printf("Reportluns failed with unknown status code :%d\n", status);
+               return;
+       }
+
+       full_report_size = scsi_datain_getfullsize(task);
+
+       printf("REPORTLUNS status:%d   data size:%d,   full reports luns data size:%d\n", status, task->datain.size, full_report_size);
+       if (full_report_size > task->datain.size) {
+               printf("We did not get all the data we need in reportluns, ask again\n");
+               if (iscsi_reportluns_async(iscsi, reportluns_cb, 0, full_report_size, private_data) != 0) {
+                       printf("failed to send reportluns command\n");
+                       exit(10);
+               }
+               return;
+       }
+
+       
+       list = scsi_datain_unmarshall(task);
+       if (list == NULL) {
+               printf("failed to unmarshall reportluns datain blob\n");
+               exit(10);
+       }
+       for (i=0; i < list->num; i++) {
+               printf("LUN:%d found\n", list->luns[i]);
+               clnt->lun = list->luns[i];
+       }
+
+       printf("Will use LUN:%d\n", clnt->lun);
+       printf("Send testunitready to lun %d\n", clnt->lun);
+       if (iscsi_testunitready_async(iscsi, clnt->lun, testunitready_cb, private_data) != 0) {
+               printf("failed to send testunitready command\n");
+               exit(10);
+       }
+}
+
+
+void normallogin_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       if (status != 0) {
+               printf("Failed to log in to target. status :0x%04x\n", status);
+               exit(10);
+       }
+
+       printf("Logged in normal session, send reportluns\n");
+       if (iscsi_reportluns_async(iscsi, reportluns_cb, 0, 16, private_data) != 0) {
+               printf("failed to send reportluns command\n");
+               exit(10);
+       }
+}
+
+
+void normalconnect_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       printf("Connected to iscsi socket\n");
+
+       if (status != 0) {
+               printf("normalconnect_cb: connection  failed status:%d\n", status);
+               exit(10);
+       }
+
+       printf("connected, send login command\n");
+       iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
+       if (iscsi_login_async(iscsi, normallogin_cb, private_data) != 0) {
+               printf("iscsi_login_async failed\n");
+               exit(10);
+       }
+}
+
+
+
+void discoverylogout_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       
+       printf("discovery session logged out, Message from main() was:[%s]\n", clnt->message);
+
+       printf("disconnect socket\n");
+       if (iscsi_disconnect(iscsi) != 0) {
+               printf("Failed to disconnect old socket\n");
+               exit(10);
+       }
+
+       printf("reconnect with normal login to [%s]\n", clnt->target_address);
+       printf("Use targetname [%s] when connecting\n", clnt->target_name);
+       if (iscsi_set_targetname(iscsi, clnt->target_name)) {
+               printf("Failed to set target name\n");
+               exit(10);
+       }
+       if (iscsi_set_alias(iscsi, "ronnie") != 0) {
+               printf("Failed to add alias\n");
+               exit(10);
+       }
+       if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
+               printf("Failed to set settion type to normal\n");
+               exit(10);
+       }
+
+       if (iscsi_connect_async(iscsi, clnt->target_address, normalconnect_cb, clnt) != 0) {
+               printf("iscsi_connect failed\n");
+               exit(10);
+       }
+}
+
+void discovery_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       struct client_state *clnt = (struct client_state *)private_data;
+       struct iscsi_discovery_address *addr;
+
+       printf("discovery callback   status:%04x\n", status);
+       for(addr=command_data; addr; addr=addr->next) { 
+               printf("Target:%s Address:%s\n", addr->target_name, addr->target_address);
+       }
+
+       addr=command_data;
+       clnt->has_discovered_target = 1;
+       clnt->target_name    = strdup(addr->target_name);
+       clnt->target_address = strdup(addr->target_address);
+
+
+       printf("discovery complete, send logout command\n");
+
+       if (iscsi_logout_async(iscsi, discoverylogout_cb, private_data) != 0) {
+               printf("iscsi_logout_async failed\n");
+               exit(10);
+       }
+}
+
+
+void discoverylogin_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       if (status != 0) {
+               printf("Failed to log in to target. status :0x%04x\n", status);
+               exit(10);
+       }
+
+       printf("Logged in to target, send discovery command\n");
+       if (iscsi_discovery_async(iscsi, discovery_cb, private_data) != 0) {
+               printf("failed to send discovery command\n");
+               exit(10);
+       }
+
+}
+
+void discoveryconnect_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+       printf("Connected to iscsi socket status:0x%08x\n", status);
+
+       if (status != 0) {
+               printf("discoveryconnect_cb: connection  failed status:%d\n", status);
+               exit(10);
+       }
+
+       printf("connected, send login command\n");
+       iscsi_set_session_type(iscsi, ISCSI_SESSION_DISCOVERY);
+       if (iscsi_login_async(iscsi, discoverylogin_cb, private_data) != 0) {
+               printf("iscsi_login_async failed\n");
+               exit(10);
+       }
+}
+
+
+int main(int argc, char *argv[])
+{
+       struct iscsi_context *iscsi;
+       struct pollfd pfd;
+       struct client_state clnt;
+
+       printf("iscsi client\n");
+
+       iscsi = iscsi_create_context("iqn.2002-10.com.ronnie:client");
+       if (iscsi == NULL) {
+               printf("Failed to create context\n");
+               exit(10);
+       }
+
+       if (iscsi_set_alias(iscsi, "ronnie") != 0) {
+               printf("Failed to add alias\n");
+               exit(10);
+       }
+
+       clnt.message = "Hello iSCSI";
+       clnt.has_discovered_target = 0;
+       if (iscsi_connect_async(iscsi, TARGET, discoveryconnect_cb, &clnt) != 0) {
+               printf("iscsi_connect failed\n");
+               exit(10);
+       }
+
+       for (;;) {
+               pfd.fd = iscsi_get_fd(iscsi);
+               pfd.events = iscsi_which_events(iscsi);
+
+               if (poll(&pfd, 1, -1) < 0) {
+                       printf("Poll failed");
+                       exit(10);
+               }
+               if (iscsi_service(iscsi, pfd.revents) < 0) {
+                       printf("iscsi_service failed\n");
+                       break;
+               }
+       }
+
+printf("STOP\n");
+exit(10);
+
+       printf("ok\n");
+       return 0;
+}
+
index 4489a8f50021a1080fb4ccd5ccdc442c710804e7..77a158c9c8f482177e0d93956b8f33bc8f00779c 100644 (file)
@@ -33,7 +33,7 @@ struct jbitset {
        JError_t err;
        const char *errstr;
 };
-const char *COLD_ATTRIBUTE jbit_error_(struct jbitset *set);
+const char *COLD jbit_error_(struct jbitset *set);
 
 /**
  * jbit_error - test for an error in the a previous jbit_ operation.
index 0b389b6079300fb6872b5ea67889acaf141821dc..cfa2e26c61512fa07951f5e3101cc3be843354b7 100644 (file)
@@ -44,7 +44,7 @@ struct jmap {
        unsigned long acc_index;
        const char *funcname;
 };
-const char *COLD_ATTRIBUTE jmap_error_(struct jmap *map);
+const char *COLD jmap_error_(struct jmap *map);
 
 /* Debugging checks. */
 static inline void jmap_debug_add_access(const struct jmap *map,
index eed1f209f7535c6d1d13f7e8abb1a580f10c267c..137cb86131884d28710fa4f5e668d5bfe0f99f74 100644 (file)
@@ -39,7 +39,7 @@
  * code path and optimize appropriately; see likely() above.
  *
  * See Also:
- *     likely(), likely_stats(), UNLIKELY_FUNCTION_ATTRIBUTE (compiler.h)
+ *     likely(), likely_stats(), COLD (compiler.h)
  *
  * Example:
  *     // Prints a warning if we overflow.
diff --git a/ccan/nfs/Makefile b/ccan/nfs/Makefile
new file mode 100644 (file)
index 0000000..93f325b
--- /dev/null
@@ -0,0 +1,79 @@
+CC=gcc
+CFLAGS=-g -O0 -Wall -W -I../.. "-D_U_=__attribute__((unused))" -D_FILE_OFFSET_BITS=64
+LIBS=
+
+LIBNFS_OBJ = libnfs-raw-mount.o libnfs-raw-portmap.o libnfs-raw-nfs.o libnfs-raw-nfsacl.o mount.o nfs.o nfsacl.o portmap.o pdu.o init.o socket.o libnfs.o libnfs-sync.o
+
+all: tools/nfsclient-raw tools/nfsclient-async tools/nfsclient-sync
+
+tools/nfsclient-async: tools/nfsclient-async.c libnfs.a
+       $(CC) $(CFLAGS) -o $@ tools/nfsclient-async.c libnfs.a $(LIBS)
+
+tools/nfsclient-sync: tools/nfsclient-sync.c libnfs.a
+       $(CC) $(CFLAGS) -o $@ tools/nfsclient-sync.c libnfs.a $(LIBS)
+
+tools/nfsclient-raw: tools/nfsclient-raw.c libnfs.a
+       $(CC) $(CFLAGS) -o $@ tools/nfsclient-raw.c libnfs.a $(LIBS)
+
+libnfs.a: $(LIBNFS_OBJ)
+       @echo Creating library $@
+       ar r libnfs.a $(LIBNFS_OBJ) 
+       ranlib libnfs.a
+
+rpc/mount.h: rpc/mount.x
+       @echo Generating $@
+       rpcgen -h rpc/mount.x > $@
+
+libnfs-raw-mount.c: rpc/mount.x
+       @echo Generating $@
+       rpcgen -c rpc/mount.x > libnfs-raw-mount.c
+
+libnfs-raw-mount.o: libnfs-raw-mount.c rpc/mount.h
+       @echo Compiling $@
+       gcc -g -c libnfs-raw-mount.c -o $@
+
+rpc/nfs.h: rpc/nfs.x
+       @echo Generating $@
+       rpcgen -h rpc/nfs.x > $@
+
+libnfs-raw-nfs.c: rpc/nfs.x
+       @echo Generating $@
+       rpcgen -c rpc/nfs.x > $@
+
+libnfs-raw-nfs.o: libnfs-raw-nfs.c rpc/nfs.h
+       @echo Compiling $@
+       gcc -g -c libnfs-raw-nfs.c -o $@
+
+rpc/nfsacl.h: rpc/nfsacl.x
+       @echo Generating $@
+       rpcgen -h rpc/nfsacl.x > $@
+
+libnfs-raw-nfsacl.c: rpc/nfsacl.x
+       @echo Generating $@
+       rpcgen -c rpc/nfsacl.x > $@
+
+libnfs-raw-nfsacl.o: libnfs-raw-nfsacl.c rpc/nfsacl.h
+       @echo Compiling $@
+       gcc -g -c libnfs-raw-nfsacl.c -o $@
+
+rpc/portmap.h: rpc/portmap.x
+       @echo Generating $@
+       rpcgen -h rpc/portmap.x > $@
+
+libnfs-raw-portmap.c: rpc/portmap.x
+       @echo Generating $@
+       rpcgen -c rpc/portmap.x > $@
+
+libnfs-raw-portmap.o: libnfs-raw-portmap.c rpc/portmap.h
+       @echo Compiling $@
+       gcc -g -c libnfs-raw-portmap.c -o $@
+
+clean:
+       rm -f *.o
+       rm -f *.a
+       rm -f rpc/mount.h libnfs-raw-mount.c
+       rm -f rpc/nfs.h libnfs-raw-nfs.c
+       rm -f rpc/nfsacl.h libnfs-raw-nfsacl.c
+       rm -f rpc/portmap.h libnfs-raw-portmap.c
+       rm -f tools/nfsclient-raw tools/nfsclient-async tools/nfsclient-sync
+
diff --git a/ccan/nfs/dlinklist.h b/ccan/nfs/dlinklist.h
new file mode 100644 (file)
index 0000000..6d525f9
--- /dev/null
@@ -0,0 +1,181 @@
+/* 
+   Unix SMB/CIFS implementation.
+   some simple double linked list macros
+
+   Copyright (C) Andrew Tridgell 1998-2010
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* To use these macros you must have a structure containing a next and
+   prev pointer */
+
+#ifndef _DLINKLIST_H
+#define _DLINKLIST_H
+
+/*
+  February 2010 - changed list format to have a prev pointer from the
+  list head. This makes DLIST_ADD_END() O(1) even though we only have
+  one list pointer.
+
+  The scheme is as follows:
+
+     1) with no entries in the list:
+          list_head == NULL
+
+     2) with 1 entry in the list:
+          list_head->next == NULL
+          list_head->prev == list_head
+
+     3) with 2 entries in the list:
+          list_head->next == element2
+          list_head->prev == element2
+         element2->prev == list_head
+         element2->next == NULL
+
+     4) with N entries in the list:
+          list_head->next == element2
+          list_head->prev == elementN
+         elementN->prev == element{N-1}
+         elementN->next == NULL
+
+  This allows us to find the tail of the list by using
+  list_head->prev, which means we can add to the end of the list in
+  O(1) time
+
+
+  Note that the 'type' arguments below are no longer needed, but
+  are kept for now to prevent an incompatible argument change
+ */
+
+
+/*
+   add an element at the front of a list
+*/
+#define DLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+               (p)->prev = (list) = (p);  \
+               (p)->next = NULL; \
+       } else { \
+               (p)->prev = (list)->prev; \
+               (list)->prev = (p); \
+               (p)->next = (list); \
+               (list) = (p); \
+       } \
+} while (0)
+
+/*
+   remove an element from a list
+   Note that the element doesn't have to be in the list. If it
+   isn't then this is a no-op
+*/
+#define DLIST_REMOVE(list, p) \
+do { \
+       if ((p) == (list)) { \
+               if ((p)->next) (p)->next->prev = (p)->prev; \
+               (list) = (p)->next; \
+       } else if ((list) && (p) == (list)->prev) {     \
+               (p)->prev->next = NULL; \
+               (list)->prev = (p)->prev; \
+       } else { \
+               if ((p)->prev) (p)->prev->next = (p)->next; \
+               if ((p)->next) (p)->next->prev = (p)->prev; \
+       } \
+       if ((p) != (list)) (p)->next = (p)->prev = NULL;        \
+} while (0)
+
+/*
+   find the head of the list given any element in it.
+   Note that this costs O(N), so you should avoid this macro
+   if at all possible!
+*/
+#define DLIST_HEAD(p, result_head) \
+do { \
+       (result_head) = (p); \
+       while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \
+} while(0)
+
+/* return the last element in the list */
+#define DLIST_TAIL(list) ((list)?(list)->prev:NULL)
+
+/* return the previous element in the list. */
+#define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL)
+
+/* insert 'p' after the given element 'el' in a list. If el is NULL then
+   this is the same as a DLIST_ADD() */
+#define DLIST_ADD_AFTER(list, p, el) \
+do { \
+        if (!(list) || !(el)) { \
+               DLIST_ADD(list, p); \
+       } else { \
+               (p)->prev = (el);   \
+               (p)->next = (el)->next;         \
+               (el)->next = (p);               \
+               if ((p)->next) (p)->next->prev = (p);   \
+               if ((list)->prev == (el)) (list)->prev = (p); \
+       }\
+} while (0)
+
+
+/*
+   add to the end of a list.
+   Note that 'type' is ignored
+*/
+#define DLIST_ADD_END(list, p, type)                   \
+do { \
+       if (!(list)) { \
+               DLIST_ADD(list, p); \
+       } else { \
+               DLIST_ADD_AFTER(list, p, (list)->prev); \
+       } \
+} while (0)
+
+/* promote an element to the from of a list */
+#define DLIST_PROMOTE(list, p) \
+do { \
+          DLIST_REMOVE(list, p); \
+          DLIST_ADD(list, p); \
+} while (0)
+
+/*
+   demote an element to the end of a list.
+   Note that 'type' is ignored
+*/
+#define DLIST_DEMOTE(list, p, type)                    \
+do { \
+       DLIST_REMOVE(list, p); \
+       DLIST_ADD_END(list, p, NULL);           \
+} while (0)
+
+/*
+   concatenate two lists - putting all elements of the 2nd list at the
+   end of the first list.
+   Note that 'type' is ignored
+*/
+#define DLIST_CONCATENATE(list1, list2, type)  \
+do { \
+       if (!(list1)) { \
+               (list1) = (list2); \
+       } else { \
+               (list1)->prev->next = (list2); \
+               if (list2) { \
+                       void *_tmplist = (void *)(list1)->prev; \
+                       (list1)->prev = (list2)->prev; \
+                       (list2)->prev = _tmplist; \
+               } \
+       } \
+} while (0)
+
+#endif /* _DLINKLIST_H */
diff --git a/ccan/nfs/init.c b/ccan/nfs/init.c
new file mode 100644 (file)
index 0000000..9bc3c7e
--- /dev/null
@@ -0,0 +1,143 @@
+/* 
+   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/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <rpc/xdr.h>
+#include "dlinklist.h"
+#include "nfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-private.h"
+
+struct rpc_context *rpc_init_context(void)
+{
+       struct rpc_context *rpc;
+
+       rpc = malloc(sizeof(struct rpc_context));
+       if (rpc == NULL) {
+               printf("Failed to allocate rpc context\n");
+               return NULL;
+       }
+       bzero(rpc, sizeof(struct rpc_context));
+
+       rpc->encodebuflen = 65536;
+       rpc->encodebuf = malloc(rpc->encodebuflen);
+       if (rpc->encodebuf == NULL) {
+               printf("Failed to allocate a buffer for rpc encoding\n");
+               free(rpc);
+               return NULL;
+       }
+
+       rpc->auth = authunix_create_default();
+       if (rpc->auth == NULL) {
+               printf("failed to create authunix\n");
+               free(rpc->encodebuf);
+               free(rpc);
+               return NULL;
+       }
+       rpc->xid = 1;
+       rpc->fd = -1;
+
+       return rpc;
+}
+
+
+void rpc_set_auth(struct rpc_context *rpc, struct AUTH *auth)
+{
+       if (rpc->auth != NULL) {
+               auth_destroy(rpc->auth);
+       }
+       rpc->auth = auth;
+}
+
+
+void rpc_set_error(struct rpc_context *rpc, char *error_string, ...)
+{
+        va_list ap;
+       char *str;
+
+       if (rpc->error_string != NULL) {
+               free(rpc->error_string);
+       }
+        va_start(ap, error_string);
+       vasprintf(&str, error_string, ap);
+       rpc->error_string = str;
+        va_end(ap);
+}
+
+char *rpc_get_error(struct rpc_context *rpc)
+{
+       return rpc->error_string;
+}
+
+void rpc_error_all_pdus(struct rpc_context *rpc, char *error)
+{
+       struct rpc_pdu *pdu;
+
+       while((pdu = rpc->outqueue) != NULL) {
+               pdu->cb(rpc, RPC_STATUS_ERROR, error, pdu->private_data);
+               DLIST_REMOVE(rpc->outqueue, pdu);
+               rpc_free_pdu(rpc, pdu);
+       }
+       while((pdu = rpc->waitpdu) != NULL) {
+               pdu->cb(rpc, RPC_STATUS_ERROR, error, pdu->private_data);
+               DLIST_REMOVE(rpc->waitpdu, pdu);
+               rpc_free_pdu(rpc, pdu);
+       }
+}
+
+
+void rpc_destroy_context(struct rpc_context *rpc)
+{
+       struct rpc_pdu *pdu;
+
+       while((pdu = rpc->outqueue) != NULL) {
+               pdu->cb(rpc, RPC_STATUS_CANCEL, NULL, pdu->private_data);
+               DLIST_REMOVE(rpc->outqueue, pdu);
+               rpc_free_pdu(rpc, pdu);
+       }
+       while((pdu = rpc->waitpdu) != NULL) {
+               pdu->cb(rpc, RPC_STATUS_CANCEL, NULL, pdu->private_data);
+               DLIST_REMOVE(rpc->waitpdu, pdu);
+               rpc_free_pdu(rpc, pdu);
+       }
+
+       auth_destroy(rpc->auth);
+       rpc->auth =NULL;
+
+       if (rpc->fd != -1) {
+               close(rpc->fd);
+       }
+
+       if (rpc->encodebuf != NULL) {
+               free(rpc->encodebuf);
+               rpc->encodebuf = NULL;
+       }
+
+       if (rpc->error_string != NULL) {
+               free(rpc->error_string);
+               rpc->error_string = NULL;
+       }
+
+       free(rpc);
+}
+
+
diff --git a/ccan/nfs/libnfs-private.h b/ccan/nfs/libnfs-private.h
new file mode 100644 (file)
index 0000000..d075f17
--- /dev/null
@@ -0,0 +1,68 @@
+/* 
+   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/>.
+*/
+
+#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 *prev, *next;
+
+       unsigned long xid;
+       XDR xdr;
+
+       int written;
+       struct rpc_data outdata;
+
+       rpc_cb cb;
+       void *private_data;
+
+       /* function to decode the xdr reply data and buffer to decode into */
+       xdrproc_t xdr_decode_fn;
+       caddr_t xdr_decode_buf;
+       int xdr_decode_bufsize;
+};
+
+struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, xdrproc_t xdr_decode_fn, int xdr_bufsize);
+void rpc_free_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu);
+int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu);
+int rpc_get_pdu_size(char *buf);
+int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size);
+void rpc_error_all_pdus(struct rpc_context *rpc, char *error);
+
diff --git a/ccan/nfs/libnfs-raw-mount.c b/ccan/nfs/libnfs-raw-mount.c
new file mode 100644 (file)
index 0000000..eccae5c
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "rpc/mount.h"
+
+bool_t
+xdr_fhandle3 (XDR *xdrs, fhandle3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bytes (xdrs, (char **)&objp->fhandle3_val, (u_int *) &objp->fhandle3_len, FHSIZE3))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_dirpath (XDR *xdrs, dirpath *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, objp, MNTPATHLEN))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_name (XDR *xdrs, name *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, objp, MNTNAMLEN))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mountstat3 (XDR *xdrs, mountstat3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mountlist (XDR *xdrs, mountlist *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_pointer (xdrs, (char **)objp, sizeof (struct mountbody), (xdrproc_t) xdr_mountbody))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mountbody (XDR *xdrs, mountbody *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_name (xdrs, &objp->ml_hostname))
+                return FALSE;
+        if (!xdr_dirpath (xdrs, &objp->ml_directory))
+                return FALSE;
+        if (!xdr_mountlist (xdrs, &objp->ml_next))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_groups (XDR *xdrs, groups *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_pointer (xdrs, (char **)objp, sizeof (struct groupnode), (xdrproc_t) xdr_groupnode))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_groupnode (XDR *xdrs, groupnode *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_name (xdrs, &objp->gr_name))
+                return FALSE;
+        if (!xdr_groups (xdrs, &objp->gr_next))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_exports (XDR *xdrs, exports *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_pointer (xdrs, (char **)objp, sizeof (struct exportnode), (xdrproc_t) xdr_exportnode))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_exportnode (XDR *xdrs, exportnode *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_dirpath (xdrs, &objp->ex_dir))
+                return FALSE;
+        if (!xdr_groups (xdrs, &objp->ex_groups))
+                return FALSE;
+        if (!xdr_exports (xdrs, &objp->ex_next))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mountres3_ok (XDR *xdrs, mountres3_ok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_fhandle3 (xdrs, &objp->fhandle))
+                return FALSE;
+        if (!xdr_array (xdrs, (char **)&objp->auth_flavors.auth_flavors_val, (u_int *) &objp->auth_flavors.auth_flavors_len, ~0,
+               sizeof (int), (xdrproc_t) xdr_int))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mountres3 (XDR *xdrs, mountres3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_mountstat3 (xdrs, &objp->fhs_status))
+                return FALSE;
+       switch (objp->fhs_status) {
+       case MNT3_OK:
+                if (!xdr_mountres3_ok (xdrs, &objp->mountres3_u.mountinfo))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
diff --git a/ccan/nfs/libnfs-raw-nfs.c b/ccan/nfs/libnfs-raw-nfs.c
new file mode 100644 (file)
index 0000000..9a53ae5
--- /dev/null
@@ -0,0 +1,1898 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "rpc/nfs.h"
+
+bool_t
+xdr_cookieverf3 (XDR *xdrs, cookieverf3 objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_opaque (xdrs, objp, NFS3_COOKIEVERFSIZE))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_uint64 (XDR *xdrs, uint64 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_u_quad_t (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_cookie3 (XDR *xdrs, cookie3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint64 (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_nfs_fh3 (XDR *xdrs, nfs_fh3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, NFS3_FHSIZE))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_filename3 (XDR *xdrs, filename3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, objp, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_diropargs3 (XDR *xdrs, diropargs3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->dir))
+                return FALSE;
+        if (!xdr_filename3 (xdrs, &objp->name))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_ftype3 (XDR *xdrs, ftype3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_uint32 (XDR *xdrs, uint32 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_u_long (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_int32 (XDR *xdrs, int32 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_long (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mode3 (XDR *xdrs, mode3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint32 (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_uid3 (XDR *xdrs, uid3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint32 (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_gid3 (XDR *xdrs, gid3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint32 (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_size3 (XDR *xdrs, size3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint64 (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_fileid3 (XDR *xdrs, fileid3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint64 (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_specdata3 (XDR *xdrs, specdata3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint32 (xdrs, &objp->specdata1))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->specdata2))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_nfstime3 (XDR *xdrs, nfstime3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint32 (xdrs, &objp->seconds))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->nseconds))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_fattr3 (XDR *xdrs, fattr3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_ftype3 (xdrs, &objp->type))
+                return FALSE;
+        if (!xdr_mode3 (xdrs, &objp->mode))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->nlink))
+                return FALSE;
+        if (!xdr_uid3 (xdrs, &objp->uid))
+                return FALSE;
+        if (!xdr_gid3 (xdrs, &objp->gid))
+                return FALSE;
+        if (!xdr_size3 (xdrs, &objp->size))
+                return FALSE;
+        if (!xdr_size3 (xdrs, &objp->used))
+                return FALSE;
+        if (!xdr_specdata3 (xdrs, &objp->rdev))
+                return FALSE;
+        if (!xdr_uint64 (xdrs, &objp->fsid))
+                return FALSE;
+        if (!xdr_fileid3 (xdrs, &objp->fileid))
+                return FALSE;
+        if (!xdr_nfstime3 (xdrs, &objp->atime))
+                return FALSE;
+        if (!xdr_nfstime3 (xdrs, &objp->mtime))
+                return FALSE;
+        if (!xdr_nfstime3 (xdrs, &objp->ctime))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_post_op_attr (XDR *xdrs, post_op_attr *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->attributes_follow))
+                return FALSE;
+       switch (objp->attributes_follow) {
+       case TRUE:
+                if (!xdr_fattr3 (xdrs, &objp->post_op_attr_u.attributes))
+                        return FALSE;
+               break;
+       case FALSE:
+               break;
+       default:
+               return FALSE;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_nfsstat3 (XDR *xdrs, nfsstat3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_stable_how (XDR *xdrs, stable_how *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_offset3 (XDR *xdrs, offset3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint64 (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_count3 (XDR *xdrs, count3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint32 (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_wcc_attr (XDR *xdrs, wcc_attr *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_size3 (xdrs, &objp->size))
+                return FALSE;
+        if (!xdr_nfstime3 (xdrs, &objp->mtime))
+                return FALSE;
+        if (!xdr_nfstime3 (xdrs, &objp->ctime))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_pre_op_attr (XDR *xdrs, pre_op_attr *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->attributes_follow))
+                return FALSE;
+       switch (objp->attributes_follow) {
+       case TRUE:
+                if (!xdr_wcc_attr (xdrs, &objp->pre_op_attr_u.attributes))
+                        return FALSE;
+               break;
+       case FALSE:
+               break;
+       default:
+               return FALSE;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_wcc_data (XDR *xdrs, wcc_data *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_pre_op_attr (xdrs, &objp->before))
+                return FALSE;
+        if (!xdr_post_op_attr (xdrs, &objp->after))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_WRITE3args (XDR *xdrs, WRITE3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->file))
+                return FALSE;
+        if (!xdr_offset3 (xdrs, &objp->offset))
+                return FALSE;
+        if (!xdr_count3 (xdrs, &objp->count))
+                return FALSE;
+        if (!xdr_stable_how (xdrs, &objp->stable))
+                return FALSE;
+        if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_writeverf3 (XDR *xdrs, writeverf3 objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_opaque (xdrs, objp, NFS3_WRITEVERFSIZE))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_WRITE3resok (XDR *xdrs, WRITE3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->file_wcc))
+                return FALSE;
+        if (!xdr_count3 (xdrs, &objp->count))
+                return FALSE;
+        if (!xdr_stable_how (xdrs, &objp->committed))
+                return FALSE;
+        if (!xdr_writeverf3 (xdrs, objp->verf))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_WRITE3resfail (XDR *xdrs, WRITE3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->file_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_WRITE3res (XDR *xdrs, WRITE3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_WRITE3resok (xdrs, &objp->WRITE3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_WRITE3resfail (xdrs, &objp->WRITE3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_LOOKUP3args (XDR *xdrs, LOOKUP3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_diropargs3 (xdrs, &objp->what))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_LOOKUP3resok (XDR *xdrs, LOOKUP3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->object))
+                return FALSE;
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+        if (!xdr_post_op_attr (xdrs, &objp->dir_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_LOOKUP3resfail (XDR *xdrs, LOOKUP3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->dir_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_LOOKUP3res (XDR *xdrs, LOOKUP3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_LOOKUP3resok (xdrs, &objp->LOOKUP3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_LOOKUP3resfail (xdrs, &objp->LOOKUP3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_COMMIT3args (XDR *xdrs, COMMIT3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->file))
+                return FALSE;
+        if (!xdr_offset3 (xdrs, &objp->offset))
+                return FALSE;
+        if (!xdr_count3 (xdrs, &objp->count))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_COMMIT3resok (XDR *xdrs, COMMIT3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->file_wcc))
+                return FALSE;
+        if (!xdr_writeverf3 (xdrs, objp->verf))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_COMMIT3resfail (XDR *xdrs, COMMIT3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->file_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_COMMIT3res (XDR *xdrs, COMMIT3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_COMMIT3resok (xdrs, &objp->COMMIT3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_COMMIT3resfail (xdrs, &objp->COMMIT3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_ACCESS3args (XDR *xdrs, ACCESS3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->object))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->access))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_ACCESS3resok (XDR *xdrs, ACCESS3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->access))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_ACCESS3resfail (XDR *xdrs, ACCESS3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_ACCESS3res (XDR *xdrs, ACCESS3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_ACCESS3resok (xdrs, &objp->ACCESS3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_ACCESS3resfail (xdrs, &objp->ACCESS3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_GETATTR3args (XDR *xdrs, GETATTR3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->object))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_GETATTR3resok (XDR *xdrs, GETATTR3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_fattr3 (xdrs, &objp->obj_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_GETATTR3res (XDR *xdrs, GETATTR3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_GETATTR3resok (xdrs, &objp->GETATTR3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_time_how (XDR *xdrs, time_how *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_set_mode3 (XDR *xdrs, set_mode3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->set_it))
+                return FALSE;
+       switch (objp->set_it) {
+       case TRUE:
+                if (!xdr_mode3 (xdrs, &objp->set_mode3_u.mode))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_set_uid3 (XDR *xdrs, set_uid3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->set_it))
+                return FALSE;
+       switch (objp->set_it) {
+       case TRUE:
+                if (!xdr_uid3 (xdrs, &objp->set_uid3_u.uid))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_set_gid3 (XDR *xdrs, set_gid3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->set_it))
+                return FALSE;
+       switch (objp->set_it) {
+       case TRUE:
+                if (!xdr_gid3 (xdrs, &objp->set_gid3_u.gid))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_set_size3 (XDR *xdrs, set_size3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->set_it))
+                return FALSE;
+       switch (objp->set_it) {
+       case TRUE:
+                if (!xdr_size3 (xdrs, &objp->set_size3_u.size))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_set_atime (XDR *xdrs, set_atime *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_time_how (xdrs, &objp->set_it))
+                return FALSE;
+       switch (objp->set_it) {
+       case SET_TO_CLIENT_TIME:
+                if (!xdr_nfstime3 (xdrs, &objp->set_atime_u.atime))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_set_mtime (XDR *xdrs, set_mtime *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_time_how (xdrs, &objp->set_it))
+                return FALSE;
+       switch (objp->set_it) {
+       case SET_TO_CLIENT_TIME:
+                if (!xdr_nfstime3 (xdrs, &objp->set_mtime_u.mtime))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_sattr3 (XDR *xdrs, sattr3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_set_mode3 (xdrs, &objp->mode))
+                return FALSE;
+        if (!xdr_set_uid3 (xdrs, &objp->uid))
+                return FALSE;
+        if (!xdr_set_gid3 (xdrs, &objp->gid))
+                return FALSE;
+        if (!xdr_set_size3 (xdrs, &objp->size))
+                return FALSE;
+        if (!xdr_set_atime (xdrs, &objp->atime))
+                return FALSE;
+        if (!xdr_set_mtime (xdrs, &objp->mtime))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_createmode3 (XDR *xdrs, createmode3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_createverf3 (XDR *xdrs, createverf3 objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_opaque (xdrs, objp, NFS3_CREATEVERFSIZE))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_createhow3 (XDR *xdrs, createhow3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_createmode3 (xdrs, &objp->mode))
+                return FALSE;
+       switch (objp->mode) {
+       case UNCHECKED:
+       case GUARDED:
+                if (!xdr_sattr3 (xdrs, &objp->createhow3_u.obj_attributes))
+                        return FALSE;
+               break;
+       case EXCLUSIVE:
+                if (!xdr_createverf3 (xdrs, objp->createhow3_u.verf))
+                        return FALSE;
+               break;
+       default:
+               return FALSE;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_CREATE3args (XDR *xdrs, CREATE3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_diropargs3 (xdrs, &objp->where))
+                return FALSE;
+        if (!xdr_createhow3 (xdrs, &objp->how))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_post_op_fh3 (XDR *xdrs, post_op_fh3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->handle_follows))
+                return FALSE;
+       switch (objp->handle_follows) {
+       case TRUE:
+                if (!xdr_nfs_fh3 (xdrs, &objp->post_op_fh3_u.handle))
+                        return FALSE;
+               break;
+       case FALSE:
+               break;
+       default:
+               return FALSE;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_CREATE3resok (XDR *xdrs, CREATE3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_fh3 (xdrs, &objp->obj))
+                return FALSE;
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_CREATE3resfail (XDR *xdrs, CREATE3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_CREATE3res (XDR *xdrs, CREATE3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_CREATE3resok (xdrs, &objp->CREATE3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_CREATE3resfail (xdrs, &objp->CREATE3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_REMOVE3args (XDR *xdrs, REMOVE3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_diropargs3 (xdrs, &objp->object))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_REMOVE3resok (XDR *xdrs, REMOVE3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_REMOVE3resfail (XDR *xdrs, REMOVE3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_REMOVE3res (XDR *xdrs, REMOVE3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_REMOVE3resok (xdrs, &objp->REMOVE3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_REMOVE3resfail (xdrs, &objp->REMOVE3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_READ3args (XDR *xdrs, READ3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->file))
+                return FALSE;
+        if (!xdr_offset3 (xdrs, &objp->offset))
+                return FALSE;
+        if (!xdr_count3 (xdrs, &objp->count))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READ3resok (XDR *xdrs, READ3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->file_attributes))
+                return FALSE;
+        if (!xdr_count3 (xdrs, &objp->count))
+                return FALSE;
+        if (!xdr_bool (xdrs, &objp->eof))
+                return FALSE;
+        if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READ3resfail (XDR *xdrs, READ3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->file_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READ3res (XDR *xdrs, READ3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_READ3resok (xdrs, &objp->READ3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_READ3resfail (xdrs, &objp->READ3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_FSINFO3args (XDR *xdrs, FSINFO3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->fsroot))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_FSINFO3resok (XDR *xdrs, FSINFO3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->rtmax))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->rtpref))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->rtmult))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->wtmax))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->wtpref))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->wtmult))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->dtpref))
+                return FALSE;
+        if (!xdr_size3 (xdrs, &objp->maxfilesize))
+                return FALSE;
+        if (!xdr_nfstime3 (xdrs, &objp->time_delta))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->properties))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_FSINFO3resfail (XDR *xdrs, FSINFO3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_FSINFO3res (XDR *xdrs, FSINFO3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_FSINFO3resok (xdrs, &objp->FSINFO3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_FSINFO3resfail (xdrs, &objp->FSINFO3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_FSSTAT3args (XDR *xdrs, FSSTAT3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->fsroot))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_FSSTAT3resok (XDR *xdrs, FSSTAT3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+        if (!xdr_size3 (xdrs, &objp->tbytes))
+                return FALSE;
+        if (!xdr_size3 (xdrs, &objp->fbytes))
+                return FALSE;
+        if (!xdr_size3 (xdrs, &objp->abytes))
+                return FALSE;
+        if (!xdr_size3 (xdrs, &objp->tfiles))
+                return FALSE;
+        if (!xdr_size3 (xdrs, &objp->ffiles))
+                return FALSE;
+        if (!xdr_size3 (xdrs, &objp->afiles))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->invarsec))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_FSSTAT3resfail (XDR *xdrs, FSSTAT3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_FSSTAT3res (XDR *xdrs, FSSTAT3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_FSSTAT3resok (xdrs, &objp->FSSTAT3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_FSSTAT3resfail (xdrs, &objp->FSSTAT3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_PATHCONF3args (XDR *xdrs, PATHCONF3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->object))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_PATHCONF3resok (XDR *xdrs, PATHCONF3resok *objp)
+{
+       register int32_t *buf;
+
+
+       if (xdrs->x_op == XDR_ENCODE) {
+                if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                        return FALSE;
+                if (!xdr_uint32 (xdrs, &objp->linkmax))
+                        return FALSE;
+                if (!xdr_uint32 (xdrs, &objp->name_max))
+                        return FALSE;
+               buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
+               if (buf == NULL) {
+                        if (!xdr_bool (xdrs, &objp->no_trunc))
+                                return FALSE;
+                        if (!xdr_bool (xdrs, &objp->chown_restricted))
+                                return FALSE;
+                        if (!xdr_bool (xdrs, &objp->case_insensitive))
+                                return FALSE;
+                        if (!xdr_bool (xdrs, &objp->case_preserving))
+                                return FALSE;
+               } else {
+                       IXDR_PUT_BOOL(buf, objp->no_trunc);
+                       IXDR_PUT_BOOL(buf, objp->chown_restricted);
+                       IXDR_PUT_BOOL(buf, objp->case_insensitive);
+                       IXDR_PUT_BOOL(buf, objp->case_preserving);
+               }
+               return TRUE;
+       } else if (xdrs->x_op == XDR_DECODE) {
+                if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                        return FALSE;
+                if (!xdr_uint32 (xdrs, &objp->linkmax))
+                        return FALSE;
+                if (!xdr_uint32 (xdrs, &objp->name_max))
+                        return FALSE;
+               buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
+               if (buf == NULL) {
+                        if (!xdr_bool (xdrs, &objp->no_trunc))
+                                return FALSE;
+                        if (!xdr_bool (xdrs, &objp->chown_restricted))
+                                return FALSE;
+                        if (!xdr_bool (xdrs, &objp->case_insensitive))
+                                return FALSE;
+                        if (!xdr_bool (xdrs, &objp->case_preserving))
+                                return FALSE;
+               } else {
+                       objp->no_trunc = IXDR_GET_BOOL(buf);
+                       objp->chown_restricted = IXDR_GET_BOOL(buf);
+                       objp->case_insensitive = IXDR_GET_BOOL(buf);
+                       objp->case_preserving = IXDR_GET_BOOL(buf);
+               }
+        return TRUE;
+       }
+
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->linkmax))
+                return FALSE;
+        if (!xdr_uint32 (xdrs, &objp->name_max))
+                return FALSE;
+        if (!xdr_bool (xdrs, &objp->no_trunc))
+                return FALSE;
+        if (!xdr_bool (xdrs, &objp->chown_restricted))
+                return FALSE;
+        if (!xdr_bool (xdrs, &objp->case_insensitive))
+                return FALSE;
+        if (!xdr_bool (xdrs, &objp->case_preserving))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_PATHCONF3resfail (XDR *xdrs, PATHCONF3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_PATHCONF3res (XDR *xdrs, PATHCONF3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_PATHCONF3resok (xdrs, &objp->PATHCONF3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_PATHCONF3resfail (xdrs, &objp->PATHCONF3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_nfspath3 (XDR *xdrs, nfspath3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, objp, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_symlinkdata3 (XDR *xdrs, symlinkdata3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_sattr3 (xdrs, &objp->symlink_attributes))
+                return FALSE;
+        if (!xdr_nfspath3 (xdrs, &objp->symlink_data))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_SYMLINK3args (XDR *xdrs, SYMLINK3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_diropargs3 (xdrs, &objp->where))
+                return FALSE;
+        if (!xdr_symlinkdata3 (xdrs, &objp->symlink))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_SYMLINK3resok (XDR *xdrs, SYMLINK3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_fh3 (xdrs, &objp->obj))
+                return FALSE;
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_SYMLINK3resfail (XDR *xdrs, SYMLINK3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_SYMLINK3res (XDR *xdrs, SYMLINK3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_SYMLINK3resok (xdrs, &objp->SYMLINK3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_SYMLINK3resfail (xdrs, &objp->SYMLINK3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_READLINK3args (XDR *xdrs, READLINK3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->symlink))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READLINK3resok (XDR *xdrs, READLINK3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->symlink_attributes))
+                return FALSE;
+        if (!xdr_nfspath3 (xdrs, &objp->data))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READLINK3resfail (XDR *xdrs, READLINK3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->symlink_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READLINK3res (XDR *xdrs, READLINK3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_READLINK3resok (xdrs, &objp->READLINK3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_READLINK3resfail (xdrs, &objp->READLINK3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_devicedata3 (XDR *xdrs, devicedata3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_sattr3 (xdrs, &objp->dev_attributes))
+                return FALSE;
+        if (!xdr_specdata3 (xdrs, &objp->spec))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mknoddata3 (XDR *xdrs, mknoddata3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_ftype3 (xdrs, &objp->type))
+                return FALSE;
+       switch (objp->type) {
+       case NF3CHR:
+       case NF3BLK:
+                if (!xdr_devicedata3 (xdrs, &objp->mknoddata3_u.device))
+                        return FALSE;
+               break;
+       case NF3SOCK:
+       case NF3FIFO:
+                if (!xdr_sattr3 (xdrs, &objp->mknoddata3_u.pipe_attributes))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_MKNOD3args (XDR *xdrs, MKNOD3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_diropargs3 (xdrs, &objp->where))
+                return FALSE;
+        if (!xdr_mknoddata3 (xdrs, &objp->what))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_MKNOD3resok (XDR *xdrs, MKNOD3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_fh3 (xdrs, &objp->obj))
+                return FALSE;
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_MKNOD3resfail (XDR *xdrs, MKNOD3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_MKNOD3res (XDR *xdrs, MKNOD3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_MKNOD3resok (xdrs, &objp->MKNOD3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_MKNOD3resfail (xdrs, &objp->MKNOD3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_MKDIR3args (XDR *xdrs, MKDIR3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_diropargs3 (xdrs, &objp->where))
+                return FALSE;
+        if (!xdr_sattr3 (xdrs, &objp->attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_MKDIR3resok (XDR *xdrs, MKDIR3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_fh3 (xdrs, &objp->obj))
+                return FALSE;
+        if (!xdr_post_op_attr (xdrs, &objp->obj_attributes))
+                return FALSE;
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_MKDIR3resfail (XDR *xdrs, MKDIR3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_MKDIR3res (XDR *xdrs, MKDIR3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_MKDIR3resok (xdrs, &objp->MKDIR3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_MKDIR3resfail (xdrs, &objp->MKDIR3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_RMDIR3args (XDR *xdrs, RMDIR3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_diropargs3 (xdrs, &objp->object))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_RMDIR3resok (XDR *xdrs, RMDIR3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_RMDIR3resfail (XDR *xdrs, RMDIR3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->dir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_RMDIR3res (XDR *xdrs, RMDIR3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_RMDIR3resok (xdrs, &objp->RMDIR3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_RMDIR3resfail (xdrs, &objp->RMDIR3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_RENAME3args (XDR *xdrs, RENAME3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_diropargs3 (xdrs, &objp->from))
+                return FALSE;
+        if (!xdr_diropargs3 (xdrs, &objp->to))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_RENAME3resok (XDR *xdrs, RENAME3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->fromdir_wcc))
+                return FALSE;
+        if (!xdr_wcc_data (xdrs, &objp->todir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_RENAME3resfail (XDR *xdrs, RENAME3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->fromdir_wcc))
+                return FALSE;
+        if (!xdr_wcc_data (xdrs, &objp->todir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_RENAME3res (XDR *xdrs, RENAME3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_RENAME3resok (xdrs, &objp->RENAME3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_RENAME3resfail (xdrs, &objp->RENAME3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_READDIRPLUS3args (XDR *xdrs, READDIRPLUS3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->dir))
+                return FALSE;
+        if (!xdr_cookie3 (xdrs, &objp->cookie))
+                return FALSE;
+        if (!xdr_cookieverf3 (xdrs, objp->cookieverf))
+                return FALSE;
+        if (!xdr_count3 (xdrs, &objp->dircount))
+                return FALSE;
+        if (!xdr_count3 (xdrs, &objp->maxcount))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_entryplus3 (XDR *xdrs, entryplus3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_fileid3 (xdrs, &objp->fileid))
+                return FALSE;
+        if (!xdr_filename3 (xdrs, &objp->name))
+                return FALSE;
+        if (!xdr_cookie3 (xdrs, &objp->cookie))
+                return FALSE;
+        if (!xdr_post_op_attr (xdrs, &objp->name_attributes))
+                return FALSE;
+        if (!xdr_post_op_fh3 (xdrs, &objp->name_handle))
+                return FALSE;
+        if (!xdr_pointer (xdrs, (char **)&objp->nextentry, sizeof (entryplus3), (xdrproc_t) xdr_entryplus3))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_dirlistplus3 (XDR *xdrs, dirlistplus3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_pointer (xdrs, (char **)&objp->entries, sizeof (entryplus3), (xdrproc_t) xdr_entryplus3))
+                return FALSE;
+        if (!xdr_bool (xdrs, &objp->eof))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READDIRPLUS3resok (XDR *xdrs, READDIRPLUS3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->dir_attributes))
+                return FALSE;
+        if (!xdr_cookieverf3 (xdrs, objp->cookieverf))
+                return FALSE;
+        if (!xdr_dirlistplus3 (xdrs, &objp->reply))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READDIRPLUS3resfail (XDR *xdrs, READDIRPLUS3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->dir_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READDIRPLUS3res (XDR *xdrs, READDIRPLUS3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_READDIRPLUS3resok (xdrs, &objp->READDIRPLUS3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_READDIRPLUS3resfail (xdrs, &objp->READDIRPLUS3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_READDIR3args (XDR *xdrs, READDIR3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->dir))
+                return FALSE;
+        if (!xdr_cookie3 (xdrs, &objp->cookie))
+                return FALSE;
+        if (!xdr_cookieverf3 (xdrs, objp->cookieverf))
+                return FALSE;
+        if (!xdr_count3 (xdrs, &objp->count))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_entry3 (XDR *xdrs, entry3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_fileid3 (xdrs, &objp->fileid))
+                return FALSE;
+        if (!xdr_filename3 (xdrs, &objp->name))
+                return FALSE;
+        if (!xdr_cookie3 (xdrs, &objp->cookie))
+                return FALSE;
+        if (!xdr_pointer (xdrs, (char **)&objp->nextentry, sizeof (entry3), (xdrproc_t) xdr_entry3))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_dirlist3 (XDR *xdrs, dirlist3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_pointer (xdrs, (char **)&objp->entries, sizeof (entry3), (xdrproc_t) xdr_entry3))
+                return FALSE;
+        if (!xdr_bool (xdrs, &objp->eof))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READDIR3resok (XDR *xdrs, READDIR3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->dir_attributes))
+                return FALSE;
+        if (!xdr_cookieverf3 (xdrs, objp->cookieverf))
+                return FALSE;
+        if (!xdr_dirlist3 (xdrs, &objp->reply))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READDIR3resfail (XDR *xdrs, READDIR3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->dir_attributes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_READDIR3res (XDR *xdrs, READDIR3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_READDIR3resok (xdrs, &objp->READDIR3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_READDIR3resfail (xdrs, &objp->READDIR3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_LINK3args (XDR *xdrs, LINK3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->file))
+                return FALSE;
+        if (!xdr_diropargs3 (xdrs, &objp->link))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_LINK3resok (XDR *xdrs, LINK3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->file_attributes))
+                return FALSE;
+        if (!xdr_wcc_data (xdrs, &objp->linkdir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_LINK3resfail (XDR *xdrs, LINK3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_post_op_attr (xdrs, &objp->file_attributes))
+                return FALSE;
+        if (!xdr_wcc_data (xdrs, &objp->linkdir_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_LINK3res (XDR *xdrs, LINK3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_LINK3resok (xdrs, &objp->LINK3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_LINK3resfail (xdrs, &objp->LINK3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_sattrguard3 (XDR *xdrs, sattrguard3 *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->check))
+                return FALSE;
+       switch (objp->check) {
+       case TRUE:
+                if (!xdr_nfstime3 (xdrs, &objp->sattrguard3_u.obj_ctime))
+                        return FALSE;
+               break;
+       case FALSE:
+               break;
+       default:
+               return FALSE;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_SETATTR3args (XDR *xdrs, SETATTR3args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfs_fh3 (xdrs, &objp->object))
+                return FALSE;
+        if (!xdr_sattr3 (xdrs, &objp->new_attributes))
+                return FALSE;
+        if (!xdr_sattrguard3 (xdrs, &objp->guard))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_SETATTR3resok (XDR *xdrs, SETATTR3resok *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->obj_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_SETATTR3resfail (XDR *xdrs, SETATTR3resfail *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_wcc_data (xdrs, &objp->obj_wcc))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_SETATTR3res (XDR *xdrs, SETATTR3res *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_nfsstat3 (xdrs, &objp->status))
+                return FALSE;
+       switch (objp->status) {
+       case NFS3_OK:
+                if (!xdr_SETATTR3resok (xdrs, &objp->SETATTR3res_u.resok))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_SETATTR3resfail (xdrs, &objp->SETATTR3res_u.resfail))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
diff --git a/ccan/nfs/libnfs-raw-nfsacl.c b/ccan/nfs/libnfs-raw-nfsacl.c
new file mode 100644 (file)
index 0000000..33c603e
--- /dev/null
@@ -0,0 +1,6 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "rpc/nfsacl.h"
diff --git a/ccan/nfs/libnfs-raw-portmap.c b/ccan/nfs/libnfs-raw-portmap.c
new file mode 100644 (file)
index 0000000..a53bc2f
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "rpc/portmap.h"
+
+bool_t
+xdr_mapping (XDR *xdrs, mapping *objp)
+{
+       register int32_t *buf;
+
+
+       if (xdrs->x_op == XDR_ENCODE) {
+               buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
+               if (buf == NULL) {
+                        if (!xdr_u_int (xdrs, &objp->prog))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->vers))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->prot))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->port))
+                                return FALSE;
+               } else {
+                       IXDR_PUT_U_LONG(buf, objp->prog);
+                       IXDR_PUT_U_LONG(buf, objp->vers);
+                       IXDR_PUT_U_LONG(buf, objp->prot);
+                       IXDR_PUT_U_LONG(buf, objp->port);
+               }
+               return TRUE;
+       } else if (xdrs->x_op == XDR_DECODE) {
+               buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
+               if (buf == NULL) {
+                        if (!xdr_u_int (xdrs, &objp->prog))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->vers))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->prot))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->port))
+                                return FALSE;
+               } else {
+                       objp->prog = IXDR_GET_U_LONG(buf);
+                       objp->vers = IXDR_GET_U_LONG(buf);
+                       objp->prot = IXDR_GET_U_LONG(buf);
+                       objp->port = IXDR_GET_U_LONG(buf);
+               }
+        return TRUE;
+       }
+
+        if (!xdr_u_int (xdrs, &objp->prog))
+                return FALSE;
+        if (!xdr_u_int (xdrs, &objp->vers))
+                return FALSE;
+        if (!xdr_u_int (xdrs, &objp->prot))
+                return FALSE;
+        if (!xdr_u_int (xdrs, &objp->port))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_call_args (XDR *xdrs, call_args *objp)
+{
+       register int32_t *buf;
+
+
+       if (xdrs->x_op == XDR_ENCODE) {
+               buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+               if (buf == NULL) {
+                        if (!xdr_u_int (xdrs, &objp->prog))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->vers))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->proc))
+                                return FALSE;
+
+               } else {
+               IXDR_PUT_U_LONG(buf, objp->prog);
+               IXDR_PUT_U_LONG(buf, objp->vers);
+               IXDR_PUT_U_LONG(buf, objp->proc);
+               }
+                if (!xdr_bytes (xdrs, (char **)&objp->args.args_val, (u_int *) &objp->args.args_len, ~0))
+                        return FALSE;
+               return TRUE;
+       } else if (xdrs->x_op == XDR_DECODE) {
+               buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+               if (buf == NULL) {
+                        if (!xdr_u_int (xdrs, &objp->prog))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->vers))
+                                return FALSE;
+                        if (!xdr_u_int (xdrs, &objp->proc))
+                                return FALSE;
+
+               } else {
+               objp->prog = IXDR_GET_U_LONG(buf);
+               objp->vers = IXDR_GET_U_LONG(buf);
+               objp->proc = IXDR_GET_U_LONG(buf);
+               }
+                if (!xdr_bytes (xdrs, (char **)&objp->args.args_val, (u_int *) &objp->args.args_len, ~0))
+                        return FALSE;
+        return TRUE;
+       }
+
+        if (!xdr_u_int (xdrs, &objp->prog))
+                return FALSE;
+        if (!xdr_u_int (xdrs, &objp->vers))
+                return FALSE;
+        if (!xdr_u_int (xdrs, &objp->proc))
+                return FALSE;
+        if (!xdr_bytes (xdrs, (char **)&objp->args.args_val, (u_int *) &objp->args.args_len, ~0))
+                return FALSE;
+       return TRUE;
+}
diff --git a/ccan/nfs/libnfs-raw.h b/ccan/nfs/libnfs-raw.h
new file mode 100644 (file)
index 0000000..c4589cb
--- /dev/null
@@ -0,0 +1,559 @@
+/* 
+   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/>.
+*/
+/*
+ * 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, int use_privileged_port, rpc_cb cb, void *private_data);
+/*
+ * When disconnecting a connection in flight. All commands in flight will be called with the callback
+ * and status RPC_STATUS_ERROR. Data will be the error string for the disconnection.
+ */
+int rpc_disconnect(struct rpc_context *rpc, char *error);
+
+void rpc_set_error(struct rpc_context *rpc, char *error_string, ...);
+
+
+/* 
+ * PORTMAP FUNCTIONS
+ */
+
+/*
+ * Call PORTMAPPER/NULL
+ * Function returns
+ *  0 : The connection was initiated. Once the connection establish finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the connection. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the portmapper daemon.
+ *                      data is NULL.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the portmapper.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_pmap_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
+
+
+/*
+ * Call PORTMAPPER/GETPORT.
+ * Function returns
+ *  0 : The connection was initiated. Once the connection establish finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the connection. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the portmapper daemon.
+ *                      data is a (uint32_t *), containing the port returned.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the portmapper.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_pmap_getport_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data);
+
+
+
+/* 
+ * MOUNT FUNCTIONS
+ */
+char *mountstat3_to_str(int stat);
+int mountstat3_to_errno(int error);
+
+/*
+ * Call MOUNT/NULL
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
+ *                      data is NULL.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the mount daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_mount_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
+
+/*
+ * Call MOUNT/MNT
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
+ *                      data is  mountres3 *.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the mount daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_mount_mnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data);
+
+/*
+ * Call MOUNT/DUMP
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
+ *                      data is a mountlist.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the mount daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_mount_dump_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
+
+/*
+ * Call MOUNT/UMNT
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
+ *                      data NULL.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the mount daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_mount_umnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data);
+
+/*
+ * Call MOUNT/UMNTALL
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
+ *                      data NULL.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the mount daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_mount_umntall_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
+
+/*
+ * Call MOUNT/EXPORT
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the mount daemon.
+ *                      data is an exports.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the mount daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_mount_export_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
+
+
+
+
+/* 
+ * NFS FUNCTIONS
+ */
+struct nfs_fh3;
+char *nfsstat3_to_str(int error);
+int nfsstat3_to_errno(int error);
+
+/*
+ * Call NFS/NULL
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is NULL.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
+
+/*
+ * Call NFS/GETATTR
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is GETATTR3res
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_getattr_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data);
+
+/*
+ * Call NFS/LOOKUP
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is LOOKUP3res
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_lookup_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, void *private_data);
+
+/*
+ * Call NFS/ACCESS
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is ACCESS3res
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_access_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, int access, void *private_data);
+
+/*
+ * Call NFS/READ
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is ACCESS3res
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_read_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, off_t offset, size_t count, void *private_data);
+
+/*
+ * Call NFS/WRITE
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is WRITE3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_write_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *buf, off_t offset, size_t count, int stable_how, void *private_data);
+
+/*
+ * Call NFS/COMMIT
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is COMMIT3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_commit_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data);
+
+
+/*
+ * Call NFS/SETATTR
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is SETATTR3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+struct SETATTR3args;
+int rpc_nfs_setattr_async(struct rpc_context *rpc, rpc_cb cb, struct SETATTR3args *args, void *private_data);
+
+
+
+/*
+ * Call NFS/MKDIR
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is MKDIR3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_mkdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data);
+
+
+
+
+
+/*
+ * Call NFS/RMDIR
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is RMDIR3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_rmdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data);
+
+
+
+
+/*
+ * Call NFS/CREATE
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is CREATE3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_create_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, int mode, void *private_data);
+
+
+
+
+/*
+ * Call NFS/REMOVE
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is REMOVE3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_remove_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, void *private_data);
+
+
+
+/*
+ * Call NFS/REMOVE
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is READDIR3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_readdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, uint64_t cookie, char *cookieverf, int count, void *private_data);
+
+/*
+ * Call NFS/FSSTAT
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is FSSTAT3res
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_fsstat_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data);
+
+
+
+
+/*
+ * Call NFS/READLINK
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is READLINK3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_readlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data);
+
+
+
+/*
+ * Call NFS/SYMLINK
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is SYMLINK3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_symlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *newname, char *oldpath, void *private_data);
+
+
+/*
+ * Call NFS/RENAME
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is RENAME3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_rename_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *olddir, char *oldname, struct nfs_fh3 *newdir, char *newname, void *private_data);
+
+
+
+/*
+ * Call NFS/LINK
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is LINK3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_link_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *file, struct nfs_fh3 *newdir, char *newname, void *private_data);
+
+
+
+
+
+
+
+/* 
+ * NFSACL FUNCTIONS
+ */
+/*
+ * Call NFSACL/NULL
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is NULL.
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfsacl_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data);
+
diff --git a/ccan/nfs/libnfs-sync.c b/ccan/nfs/libnfs-sync.c
new file mode 100644 (file)
index 0000000..d6f58f4
--- /dev/null
@@ -0,0 +1,1069 @@
+/* 
+   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/>.
+*/
+/*
+ * 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 "nfs.h"
+#include "libnfs-raw.h"
+#include "rpc/mount.h"
+#include "rpc/nfs.h"
+
+struct sync_cb_data {
+       int is_finished;
+       int status;
+       off_t offset;
+       void *return_data;
+       int return_int;
+};
+
+
+static void wait_for_reply(struct nfs_context *nfs, struct sync_cb_data *cb_data)
+{
+       struct pollfd pfd;
+
+       for (;;) {
+               if (cb_data->is_finished) {
+                       break;
+               }
+               pfd.fd = nfs_get_fd(nfs);
+               pfd.events = nfs_which_events(nfs);
+
+               if (poll(&pfd, 1, -1) < 0) {
+                       printf("Poll failed");
+                       cb_data->status = -EIO;
+                       break;
+               }
+               if (nfs_service(nfs, pfd.revents) < 0) {
+                       printf("nfs_service failed\n");
+                       cb_data->status = -EIO;
+                       break;
+               }
+       }
+}
+
+
+
+
+
+
+/*
+ * connect to the server and mount the export
+ */
+static void mount_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("mount/mnt call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_mount_sync(struct nfs_context *nfs, const char *server, const char *export)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_mount_async(nfs, server, export, mount_cb, &cb_data) != 0) {
+               printf("nfs_mount_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+/*
+ * stat()
+ */
+static void stat_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("stat call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+
+       memcpy(cb_data->return_data, data, sizeof(struct stat));
+}
+
+int nfs_stat_sync(struct nfs_context *nfs, const char *path, struct stat *st)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = st;
+
+       if (nfs_stat_async(nfs, path, stat_cb, &cb_data) != 0) {
+               printf("nfs_stat_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+/*
+ * open()
+ */
+static void open_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       struct nfsfh *fh, **nfsfh;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("open call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+
+       fh    = data;
+       nfsfh = cb_data->return_data;
+       *nfsfh = fh;
+}
+
+int nfs_open_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = nfsfh;
+
+       if (nfs_open_async(nfs, path, mode, open_cb, &cb_data) != 0) {
+               printf("nfs_open_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+/*
+ * pread()
+ */
+static void pread_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       char *buffer;
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("pread call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+
+       buffer = cb_data->return_data;
+       memcpy(buffer, (char *)data, status);
+}
+
+int nfs_pread_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buffer)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = buffer;
+
+       if (nfs_pread_async(nfs, nfsfh, offset, count, pread_cb, &cb_data) != 0) {
+               printf("nfs_pread_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+/*
+ * read()
+ */
+int nfs_read_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buffer)
+{
+       return nfs_pread_sync(nfs, nfsfh, nfs_get_current_offset(nfsfh), count, buffer);
+}
+
+/*
+ * close()
+ */
+static void close_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("close call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_close_sync(struct nfs_context *nfs, struct nfsfh *nfsfh)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_close_async(nfs, nfsfh, close_cb, &cb_data) != 0) {
+               printf("nfs_close_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+/*
+ * fstat()
+ */
+int nfs_fstat_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = st;
+
+       if (nfs_fstat_async(nfs, nfsfh, stat_cb, &cb_data) != 0) {
+               printf("nfs_fstat_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+/*
+ * pwrite()
+ */
+static void pwrite_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("pwrite call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_pwrite_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_pwrite_async(nfs, nfsfh, offset, count, buf, pwrite_cb, &cb_data) != 0) {
+               printf("nfs_pwrite_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+/*
+ * write()
+ */
+int nfs_write_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf)
+{
+       return nfs_pwrite_sync(nfs, nfsfh, nfs_get_current_offset(nfsfh), count, buf);
+}
+
+
+/*
+ * fsync()
+ */
+static void fsync_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("fsync call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_fsync_sync(struct nfs_context *nfs, struct nfsfh *nfsfh)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_fsync_async(nfs, nfsfh, fsync_cb, &cb_data) != 0) {
+               printf("nfs_fsync_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+/*
+ * ftruncate()
+ */
+static void ftruncate_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("ftruncate call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_ftruncate_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_ftruncate_async(nfs, nfsfh, length, ftruncate_cb, &cb_data) != 0) {
+               printf("nfs_ftruncate_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * truncate()
+ */
+static void truncate_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("truncate call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_truncate_sync(struct nfs_context *nfs, const char *path, off_t length)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_truncate_async(nfs, path, length, truncate_cb, &cb_data) != 0) {
+               printf("nfs_ftruncate_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+
+/*
+ * mkdir()
+ */
+static void mkdir_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("mkdir call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_mkdir_sync(struct nfs_context *nfs, const char *path)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_mkdir_async(nfs, path, mkdir_cb, &cb_data) != 0) {
+               printf("nfs_mkdir_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+
+/*
+ * rmdir()
+ */
+static void rmdir_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("rmdir call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_rmdir_sync(struct nfs_context *nfs, const char *path)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_rmdir_async(nfs, path, rmdir_cb, &cb_data) != 0) {
+               printf("nfs_rmdir_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * creat()
+ */
+static void creat_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       struct nfsfh *fh, **nfsfh;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("creat call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+
+       fh    = data;
+       nfsfh = cb_data->return_data;
+       *nfsfh = fh;
+}
+
+int nfs_creat_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = nfsfh;
+
+       if (nfs_creat_async(nfs, path, mode, creat_cb, &cb_data) != 0) {
+               printf("nfs_creat_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+/*
+ * unlink()
+ */
+static void unlink_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("unlink call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_unlink_sync(struct nfs_context *nfs, const char *path)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_unlink_async(nfs, path, unlink_cb, &cb_data) != 0) {
+               printf("nfs_unlink_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * opendir()
+ */
+static void opendir_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+       struct nfsdir *dir, **nfsdir;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("opendir call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+
+       dir     = data;
+       nfsdir  = cb_data->return_data;
+       *nfsdir = dir;
+}
+
+int nfs_opendir_sync(struct nfs_context *nfs, const char *path, struct nfsdir **nfsdir)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = nfsdir;
+
+       if (nfs_opendir_async(nfs, path, opendir_cb, &cb_data) != 0) {
+               printf("nfs_opendir_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+/*
+ * lseek()
+ */
+static void lseek_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("lseek call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+
+       if (cb_data->return_data != NULL) {
+               memcpy(cb_data->return_data, data, sizeof(off_t));
+       }
+}
+
+int nfs_lseek_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, off_t *current_offset)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = current_offset;
+
+       if (nfs_lseek_async(nfs, nfsfh, offset, whence, lseek_cb, &cb_data) != 0) {
+               printf("nfs_lseek_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * statvfs()
+ */
+static void statvfs_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("statvfs call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+
+       memcpy(cb_data->return_data, data, sizeof(struct statvfs));
+}
+
+int nfs_statvfs_sync(struct nfs_context *nfs, const char *path, struct statvfs *svfs)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = svfs;
+
+       if (nfs_statvfs_async(nfs, path, statvfs_cb, &cb_data) != 0) {
+               printf("nfs_statvfs_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+
+/*
+ * readlink()
+ */
+static void readlink_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("readlink call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+
+       if (strlen(data) > (size_t)cb_data->return_int) {
+               printf("Too small buffer for readlink\n");
+               cb_data->status = -ENAMETOOLONG;
+               return;
+       }
+
+       memcpy(cb_data->return_data, data, strlen(data)+1);
+}
+
+int nfs_readlink_sync(struct nfs_context *nfs, const char *path, char *buf, int bufsize)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+       cb_data.return_data = buf;
+       cb_data.return_int  = bufsize;
+
+       if (nfs_readlink_async(nfs, path, readlink_cb, &cb_data) != 0) {
+               printf("nfs_readlink_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * chmod()
+ */
+static void chmod_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("chmod call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_chmod_sync(struct nfs_context *nfs, const char *path, int mode)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_chmod_async(nfs, path, mode, chmod_cb, &cb_data) != 0) {
+               printf("nfs_chmod_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+/*
+ * fchmod()
+ */
+static void fchmod_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("fchmod call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_fchmod_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_fchmod_async(nfs, nfsfh, mode, fchmod_cb, &cb_data) != 0) {
+               printf("nfs_fchmod_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+/*
+ * chown()
+ */
+static void chown_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("chown call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_chown_sync(struct nfs_context *nfs, const char *path, int uid, int gid)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_chown_async(nfs, path, uid, gid, chown_cb, &cb_data) != 0) {
+               printf("nfs_chown_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+/*
+ * fchown()
+ */
+static void fchown_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("fchown call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_fchown_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_fchown_async(nfs, nfsfh, uid, gid, fchown_cb, &cb_data) != 0) {
+               printf("nfs_fchown_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * utimes()
+ */
+static void utimes_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("utimes call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_utimes_sync(struct nfs_context *nfs, const char *path, struct timeval *times)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_utimes_async(nfs, path, times, utimes_cb, &cb_data) != 0) {
+               printf("nfs_utimes_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * utime()
+ */
+static void utime_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("utime call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_utime_sync(struct nfs_context *nfs, const char *path, struct utimbuf *times)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_utime_async(nfs, path, times, utime_cb, &cb_data) != 0) {
+               printf("nfs_utimes_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+
+/*
+ * access()
+ */
+static void access_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("access call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_access_sync(struct nfs_context *nfs, const char *path, int mode)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_access_async(nfs, path, mode, access_cb, &cb_data) != 0) {
+               printf("nfs_access_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * symlink()
+ */
+static void symlink_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("symlink call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_symlink_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_symlink_async(nfs, oldpath, newpath, symlink_cb, &cb_data) != 0) {
+               printf("nfs_symlink_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * rename()
+ */
+static void rename_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("rename call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_rename_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_rename_async(nfs, oldpath, newpath, rename_cb, &cb_data) != 0) {
+               printf("nfs_rename_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
+
+
+
+/*
+ * link()
+ */
+static void link_cb(int status, struct nfs_context *nfs _U_, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
+
+       if (status < 0) {
+               printf("link call failed with \"%s\"\n", (char *)data);
+               return;
+       }
+}
+
+int nfs_link_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_link_async(nfs, oldpath, newpath, link_cb, &cb_data) != 0) {
+               printf("nfs_link_async failed\n");
+               return -1;
+       }
+
+       wait_for_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
diff --git a/ccan/nfs/libnfs.c b/ccan/nfs/libnfs.c
new file mode 100644 (file)
index 0000000..025b391
--- /dev/null
@@ -0,0 +1,2727 @@
+/* 
+   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/>.
+*/
+/*
+ * 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 "nfs.h"
+#include "libnfs-raw.h"
+#include "rpc/mount.h"
+#include "rpc/nfs.h"
+
+struct nfsfh {
+       struct nfs_fh3 fh;
+       int is_sync;
+       off_t offset;
+};
+
+struct nfsdir {
+       struct nfsdirent *entries;
+       struct nfsdirent *current;
+};
+
+void nfs_free_nfsdir(struct nfsdir *nfsdir)
+{
+       while (nfsdir->entries) {
+               struct nfsdirent *dirent = nfsdir->entries->next;
+               if (nfsdir->entries->name != NULL) {
+                       free(nfsdir->entries->name);
+               }
+               free(nfsdir->entries);
+               nfsdir->entries = dirent;
+       }
+       free(nfsdir);
+}
+
+struct nfs_context {
+       struct rpc_context *rpc;
+       char *server;
+       char *export;
+       struct nfs_fh3 rootfh;
+       int acl_support;
+};
+
+struct nfs_cb_data;
+typedef int (*continue_func)(struct nfs_context *nfs, struct nfs_cb_data *data);
+
+struct nfs_cb_data {
+       struct nfs_context *nfs;
+       struct nfsfh *nfsfh;
+       char *saved_path, *path;
+
+       nfs_cb cb;
+       void *private_data;
+
+       continue_func continue_cb;
+       void *continue_data;
+       void (*free_continue_data)(void *);
+       int continue_int;
+
+       struct nfs_fh3 fh;
+};
+
+static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh);
+
+
+void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth)
+{
+       return rpc_set_auth(nfs->rpc, auth);
+}
+
+int nfs_get_fd(struct nfs_context *nfs)
+{
+       return rpc_get_fd(nfs->rpc);
+}
+
+int nfs_which_events(struct nfs_context *nfs)
+{
+       return rpc_which_events(nfs->rpc);
+}
+
+int nfs_service(struct nfs_context *nfs, int revents)
+{
+       return rpc_service(nfs->rpc, revents);
+}
+
+char *nfs_get_error(struct nfs_context *nfs)
+{
+       return rpc_get_error(nfs->rpc);
+};
+
+struct nfs_context *nfs_init_context(void)
+{
+       struct nfs_context *nfs;
+
+       nfs = malloc(sizeof(struct nfs_context));
+       if (nfs == NULL) {
+               printf("Failed to allocate nfs context\n");
+               return NULL;
+       }
+       nfs->rpc = rpc_init_context();
+       if (nfs->rpc == NULL) {
+               printf("Failed to allocate rpc sub-context\n");
+               free(nfs);
+               return NULL;
+       }
+
+       return nfs;
+}
+
+void nfs_destroy_context(struct nfs_context *nfs)
+{
+       rpc_destroy_context(nfs->rpc);
+       nfs->rpc = NULL;
+
+       if (nfs->server) {
+               free(nfs->server);
+               nfs->server = NULL;
+       }
+
+       if (nfs->export) {
+               free(nfs->export);
+               nfs->export = NULL;
+       }
+
+       if (nfs->rootfh.data.data_val != NULL) {
+               free(nfs->rootfh.data.data_val);
+               nfs->rootfh.data.data_val = NULL;
+       }
+
+       free(nfs);
+}
+
+void free_nfs_cb_data(struct nfs_cb_data *data)
+{
+       if (data->saved_path != NULL) {
+               free(data->saved_path);
+               data->saved_path = NULL;
+       }
+
+       if (data->continue_data != NULL) {
+               data->free_continue_data(data->continue_data);
+               data->continue_data = NULL;
+       }
+
+       if (data->fh.data.data_val != NULL) {
+               free(data->fh.data.data_val);
+               data->fh.data.data_val = NULL;
+       }
+
+       free(data);
+}
+
+
+
+
+
+static void nfs_mount_10_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       data->cb(0, nfs, NULL, data->private_data);
+       free_nfs_cb_data(data);
+}
+
+static void nfs_mount_9_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+
+       nfs->acl_support = 1;
+       if (status == RPC_STATUS_ERROR) {
+               nfs->acl_support = 0;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       if (rpc_nfs_getattr_async(rpc, nfs_mount_10_cb, &nfs->rootfh, data) != 0) {
+               data->cb(-ENOMEM, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+static void nfs_mount_8_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       if (rpc_nfsacl_null_async(rpc, nfs_mount_9_cb, data) != 0) {
+               data->cb(-ENOMEM, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+static void nfs_mount_7_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       if (rpc_nfs_null_async(rpc, nfs_mount_8_cb, data) != 0) {
+               data->cb(-ENOMEM, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+
+static void nfs_mount_6_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       mountres3 *res;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       res = command_data;
+       if (res->fhs_status != MNT3_OK) {
+               rpc_set_error(rpc, "RPC error: Mount failed with error %s(%d) %s(%d)", mountstat3_to_str(res->fhs_status), res->fhs_status, strerror(-mountstat3_to_errno(res->fhs_status)), -mountstat3_to_errno(res->fhs_status));
+               data->cb(mountstat3_to_errno(res->fhs_status), nfs, rpc_get_error(rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       nfs->rootfh.data.data_len = res->mountres3_u.mountinfo.fhandle.fhandle3_len;
+       nfs->rootfh.data.data_val = malloc(nfs->rootfh.data.data_len);
+       if (nfs->rootfh.data.data_val == NULL) {
+               rpc_set_error(rpc, "Out of memory. Could not allocate memory to store root filehandle");
+               data->cb(-ENOMEM, nfs, rpc_get_error(rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       memcpy(nfs->rootfh.data.data_val, res->mountres3_u.mountinfo.fhandle.fhandle3_val, nfs->rootfh.data.data_len);
+
+       rpc_disconnect(rpc, "normal disconnect");
+       if (rpc_connect_async(rpc, nfs->server, 2049, 1, nfs_mount_7_cb, data) != 0) {
+               data->cb(-ENOMEM, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+
+static void nfs_mount_5_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       if (rpc_mount_mnt_async(rpc, nfs_mount_6_cb, nfs->export, data) != 0) {
+               data->cb(-ENOMEM, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+static void nfs_mount_4_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       if (rpc_mount_null_async(rpc, nfs_mount_5_cb, data) != 0) {
+               data->cb(-ENOMEM, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+static void nfs_mount_3_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       uint32_t mount_port;
+
+       if (status == RPC_STATUS_ERROR) {       
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       mount_port = *(uint32_t *)command_data;
+       if (mount_port == 0) {
+               rpc_set_error(rpc, "RPC error. Mount program is not available on %s", nfs->server);
+               data->cb(-ENOENT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       rpc_disconnect(rpc, "normal disconnect");
+       if (rpc_connect_async(rpc, nfs->server, mount_port, 1, nfs_mount_4_cb, data) != 0) {
+               data->cb(-ENOMEM, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+
+static void nfs_mount_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, nfs_mount_3_cb, private_data) != 0) {
+               data->cb(-ENOMEM, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+static void nfs_mount_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       if (rpc_pmap_null_async(rpc, nfs_mount_2_cb, data) != 0) {
+               data->cb(-ENOMEM, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+/*
+ * Async call for mounting an nfs share and geting the root filehandle
+ */
+int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *export, nfs_cb cb, void *private_data)
+{
+       struct nfs_cb_data *data;
+
+       data = malloc(sizeof(struct nfs_cb_data));
+       if (data == NULL) {
+               rpc_set_error(nfs->rpc, "out of memory");
+               printf("failed to allocate memory for nfs mount data\n");
+               return -1;
+       }
+       bzero(data, sizeof(struct nfs_cb_data));
+       nfs->server        = strdup(server);
+       nfs->export        = strdup(export);
+       data->nfs          = nfs;
+       data->cb           = cb;
+       data->private_data = private_data;
+
+       if (rpc_connect_async(nfs->rpc, server, 111, 0, nfs_mount_1_cb, data) != 0) {
+               printf("Failed to start connection\n");
+               free_nfs_cb_data(data);
+               return -4;
+       }
+
+       return 0;
+}
+
+
+
+/*
+ * Functions to first look up a path, component by component, and then finally call a specific function once
+ * the filehandle for the final component is found.
+ */
+static void nfs_lookup_path_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       LOOKUP3res *res;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       res = command_data;
+       if (res->status != NFS3_OK) {
+               rpc_set_error(nfs->rpc, "NFS: Lookup of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+               data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       if (nfs_lookup_path_async_internal(nfs, data, &res->LOOKUP3res_u.resok.object) != 0) {
+               rpc_set_error(nfs->rpc, "Failed to create lookup pdu");
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+}
+
+static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh)
+{
+       char *path, *str;
+
+       while (*data->path == '/') {
+             data->path++;
+       }
+
+       path = data->path;
+       str = index(path, '/');
+       if (str != NULL) {
+               *str = 0;
+               data->path = str+1;
+       } else {
+               while (*data->path != 0) {
+                     data->path++;
+               }
+       }
+
+       if (*path == 0) {
+               data->fh.data.data_len = fh->data.data_len;
+               data->fh.data.data_val = malloc(data->fh.data.data_len);
+               if (data->fh.data.data_val == NULL) {
+                       rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh for %s", data->path);
+                       data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+                       free_nfs_cb_data(data);
+                       return -1;
+               }
+               memcpy(data->fh.data.data_val, fh->data.data_val, data->fh.data.data_len);
+               data->continue_cb(nfs, data);
+               return 0;
+       }
+
+       if (rpc_nfs_lookup_async(nfs->rpc, nfs_lookup_path_1_cb, fh, path, data) != 0) {
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send lookup call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
+static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data, continue_func continue_cb, void *continue_data, void (*free_continue_data)(void *), int continue_int)
+{
+       struct nfs_cb_data *data;
+
+       if (path[0] != '/') {
+               rpc_set_error(nfs->rpc, "Pathname is not absulute %s", path);
+               return -1;
+       }
+
+       data = malloc(sizeof(struct nfs_cb_data));
+       if (data == NULL) {
+               rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
+               printf("failed to allocate memory for nfs cb data\n");
+               free_nfs_cb_data(data);
+               return -2;
+       }
+       bzero(data, sizeof(struct nfs_cb_data));
+       data->nfs                = nfs;
+       data->cb                 = cb;
+       data->continue_cb        = continue_cb;
+       data->continue_data      = continue_data;
+       data->free_continue_data = free_continue_data;
+       data->continue_int       = continue_int;
+       data->private_data       = private_data;
+       data->saved_path         = strdup(path);
+       if (data->saved_path == NULL) {
+               rpc_set_error(nfs->rpc, "out of memory: failed to copy path string");
+               printf("failed to allocate memory for path string\n");
+               free_nfs_cb_data(data);
+               return -2;
+       }
+       data->path = data->saved_path;
+
+       if (nfs_lookup_path_async_internal(nfs, data, &nfs->rootfh) != 0) {
+               printf("failed to lookup path\n");
+               /* return 0 here since the callback will be invoked if there is a failure */
+               return 0;
+       }
+       return 0;
+}
+
+
+
+
+
+/*
+ * Async stat()
+ */
+static void nfs_stat_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+       GETATTR3res *res;
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       struct stat st;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       res = command_data;
+       if (res->status != NFS3_OK) {
+               rpc_set_error(nfs->rpc, "NFS: GETATTR of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+               data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+        st.st_dev     = -1;
+        st.st_ino     = res->GETATTR3res_u.resok.obj_attributes.fileid;
+        st.st_mode    = res->GETATTR3res_u.resok.obj_attributes.mode;
+        st.st_nlink   = res->GETATTR3res_u.resok.obj_attributes.nlink;
+        st.st_uid     = res->GETATTR3res_u.resok.obj_attributes.uid;
+        st.st_gid     = res->GETATTR3res_u.resok.obj_attributes.gid;
+        st.st_rdev    = 0;
+        st.st_size    = res->GETATTR3res_u.resok.obj_attributes.size;
+        st.st_blksize = 4096;
+        st.st_blocks  = res->GETATTR3res_u.resok.obj_attributes.size / 4096;
+        st.st_atime   = res->GETATTR3res_u.resok.obj_attributes.atime.seconds;
+        st.st_mtime   = res->GETATTR3res_u.resok.obj_attributes.mtime.seconds;
+        st.st_ctime   = res->GETATTR3res_u.resok.obj_attributes.ctime.seconds;
+
+       data->cb(0, nfs, &st, data->private_data);
+       free_nfs_cb_data(data);
+}
+
+static int nfs_stat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+       if (rpc_nfs_getattr_async(nfs->rpc, nfs_stat_1_cb, &data->fh, data) != 0) {
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send STAT GETATTR call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
+int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+       if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_stat_continue_internal, NULL, NULL, 0) != 0) {
+               printf("Out of memory: failed to start parsing the path components\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+
+
+
+/*
+ * Async open()
+ */
+static void nfs_open_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+       ACCESS3res *res;
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       struct nfsfh *nfsfh;
+       unsigned int nfsmode = 0;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       res = command_data;
+       if (res->status != NFS3_OK) {
+               rpc_set_error(nfs->rpc, "NFS: ACCESS of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+               data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       if (data->continue_int & O_WRONLY) {
+               nfsmode |= ACCESS3_MODIFY;
+       }
+       if (data->continue_int & O_RDWR) {
+               nfsmode |= ACCESS3_READ|ACCESS3_MODIFY;
+       }
+       if (!(data->continue_int & (O_WRONLY|O_RDWR))) {
+               nfsmode |= ACCESS3_READ;
+       }
+
+
+       if (res->ACCESS3res_u.resok.access != nfsmode) {
+               rpc_set_error(nfs->rpc, "NFS: ACCESS denied. Required access %c%c%c. Allowed access %c%c%c",
+                                       nfsmode&ACCESS3_READ?'r':'-',
+                                       nfsmode&ACCESS3_MODIFY?'w':'-',
+                                       nfsmode&ACCESS3_EXECUTE?'x':'-',
+                                       res->ACCESS3res_u.resok.access&ACCESS3_READ?'r':'-',
+                                       res->ACCESS3res_u.resok.access&ACCESS3_MODIFY?'w':'-',
+                                       res->ACCESS3res_u.resok.access&ACCESS3_EXECUTE?'x':'-');
+               data->cb(-EACCES, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       nfsfh = malloc(sizeof(struct nfsfh));
+       if (nfsfh == NULL) {
+               rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure");
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       bzero(nfsfh, sizeof(struct nfsfh));
+
+       if (data->continue_int & O_SYNC) {
+               nfsfh->is_sync = 1;
+       }
+
+       /* steal the filehandle */
+       nfsfh->fh.data.data_len = data->fh.data.data_len;
+       nfsfh->fh.data.data_val = data->fh.data.data_val;
+       data->fh.data.data_val = NULL;
+
+       data->cb(0, nfs, nfsfh, data->private_data);
+       free_nfs_cb_data(data);
+}
+
+static int nfs_open_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+       int nfsmode = 0;
+
+       if (data->continue_int & O_WRONLY) {
+               nfsmode |= ACCESS3_MODIFY;
+       }
+       if (data->continue_int & O_RDWR) {
+               nfsmode |= ACCESS3_READ|ACCESS3_MODIFY;
+       }
+       if (!(data->continue_int & (O_WRONLY|O_RDWR))) {
+               nfsmode |= ACCESS3_READ;
+       }
+
+       if (rpc_nfs_access_async(nfs->rpc, nfs_open_cb, &data->fh, nfsmode, data) != 0) {
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send OPEN ACCESS call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
+int nfs_open_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
+{
+       if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_open_continue_internal, NULL, NULL, mode) != 0) {
+               printf("Out of memory: failed to start parsing the path components\n");
+               return -2;
+       }
+
+       return 0;
+}
+
+
+
+
+
+/*
+ * Async pread()
+ */
+static void nfs_pread_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       READ3res *res;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       res = command_data;
+       if (res->status != NFS3_OK) {
+               rpc_set_error(nfs->rpc, "NFS: Read failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+               data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       data->nfsfh->offset += res->READ3res_u.resok.count;
+       data->cb(res->READ3res_u.resok.count, nfs, res->READ3res_u.resok.data.data_val, data->private_data);
+       free_nfs_cb_data(data);
+}
+
+int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, nfs_cb cb, void *private_data)
+{
+       struct nfs_cb_data *data;
+
+       data = malloc(sizeof(struct nfs_cb_data));
+       if (data == NULL) {
+               rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
+               printf("failed to allocate memory for nfs cb data\n");
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       bzero(data, sizeof(struct nfs_cb_data));
+       data->nfs          = nfs;
+       data->cb           = cb;
+       data->private_data = private_data;
+       data->nfsfh        = nfsfh;
+
+       nfsfh->offset = offset;
+       if (rpc_nfs_read_async(nfs->rpc, nfs_pread_cb, &nfsfh->fh, offset, count, data) != 0) {
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send READ call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Async read()
+ */
+int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, nfs_cb cb, void *private_data)
+{
+       return nfs_pread_async(nfs, nfsfh, nfsfh->offset, count, cb, private_data);
+}
+
+
+
+/*
+ * Async pwrite()
+ */
+static void nfs_pwrite_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       WRITE3res *res;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       res = command_data;
+       if (res->status != NFS3_OK) {
+               rpc_set_error(nfs->rpc, "NFS: Write failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+               data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       data->nfsfh->offset += res->WRITE3res_u.resok.count;
+       data->cb(res->WRITE3res_u.resok.count, nfs, NULL, data->private_data);
+       free_nfs_cb_data(data);
+}
+
+int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf, nfs_cb cb, void *private_data)
+{
+       struct nfs_cb_data *data;
+
+       data = malloc(sizeof(struct nfs_cb_data));
+       if (data == NULL) {
+               rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
+               printf("failed to allocate memory for nfs cb data\n");
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       bzero(data, sizeof(struct nfs_cb_data));
+       data->nfs          = nfs;
+       data->cb           = cb;
+       data->private_data = private_data;
+       data->nfsfh        = nfsfh;
+
+       nfsfh->offset = offset;
+       if (rpc_nfs_write_async(nfs->rpc, nfs_pwrite_cb, &nfsfh->fh, buf, offset, count, nfsfh->is_sync?FILE_SYNC:UNSTABLE, data) != 0) {
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send WRITE call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Async write()
+ */
+int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf, nfs_cb cb, void *private_data)
+{
+       return nfs_pwrite_async(nfs, nfsfh, nfsfh->offset, count, buf, cb, private_data);
+}
+
+
+
+
+/*
+ * close
+ */
+int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
+{
+       if (nfsfh->fh.data.data_val != NULL){
+               free(nfsfh->fh.data.data_val);
+               nfsfh->fh.data.data_val = NULL;
+       }
+       free(nfsfh);
+
+       cb(0, nfs, NULL, private_data);
+       return 0;
+};
+
+
+
+
+
+/*
+ * Async fstat()
+ */
+int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
+{
+       struct nfs_cb_data *data;
+
+       data = malloc(sizeof(struct nfs_cb_data));
+       if (data == NULL) {
+               rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
+               printf("failed to allocate memory for nfs cb data\n");
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       bzero(data, sizeof(struct nfs_cb_data));
+       data->nfs          = nfs;
+       data->cb           = cb;
+       data->private_data = private_data;
+
+       if (rpc_nfs_getattr_async(nfs->rpc, nfs_stat_1_cb, &nfsfh->fh, data) != 0) {
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send STAT GETATTR call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
+
+
+/*
+ * Async fsync()
+ */
+static void nfs_fsync_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       COMMIT3res *res;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       res = command_data;
+       if (res->status != NFS3_OK) {
+               rpc_set_error(nfs->rpc, "NFS: Commit failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+               data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       data->cb(0, nfs, NULL, data->private_data);
+       free_nfs_cb_data(data);
+}
+
+int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
+{
+       struct nfs_cb_data *data;
+
+       data = malloc(sizeof(struct nfs_cb_data));
+       if (data == NULL) {
+               rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
+               printf("failed to allocate memory for nfs cb data\n");
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       bzero(data, sizeof(struct nfs_cb_data));
+       data->nfs          = nfs;
+       data->cb           = cb;
+       data->private_data = private_data;
+
+       if (rpc_nfs_commit_async(nfs->rpc, nfs_fsync_cb, &nfsfh->fh, data) != 0) {
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send COMMIT call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
+
+
+
+/*
+ * Async ftruncate()
+ */
+static void nfs_ftruncate_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       SETATTR3res *res;
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       res = command_data;
+       if (res->status != NFS3_OK) {
+               rpc_set_error(nfs->rpc, "NFS: Setattr failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+               data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       data->cb(0, nfs, NULL, data->private_data);
+       free_nfs_cb_data(data);
+}
+
+int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length, nfs_cb cb, void *private_data)
+{
+       struct nfs_cb_data *data;
+       SETATTR3args args;
+
+       data = malloc(sizeof(struct nfs_cb_data));
+       if (data == NULL) {
+               rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure");
+               printf("failed to allocate memory for nfs cb data\n");
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       bzero(data, sizeof(struct nfs_cb_data));
+       data->nfs          = nfs;
+       data->cb           = cb;
+       data->private_data = private_data;
+
+       bzero(&args, sizeof(SETATTR3args));
+       args.object.data.data_len = nfsfh->fh.data.data_len;
+       args.object.data.data_val = nfsfh->fh.data.data_val;
+       args.new_attributes.size.set_it = 1;
+       args.new_attributes.size.set_size3_u.size = length;
+
+       if (rpc_nfs_setattr_async(nfs->rpc, nfs_ftruncate_cb, &args, data) != 0) {
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
+
+/*
+ * Async truncate()
+ */
+static int nfs_truncate_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+       off_t offset = data->continue_int;
+       struct nfsfh nfsfh;
+
+       nfsfh.fh.data.data_val = data->fh.data.data_val;
+       nfsfh.fh.data.data_len = data->fh.data.data_len;
+
+       if (nfs_ftruncate_async(nfs, &nfsfh, offset, data->cb, data->private_data) != 0) {
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       free_nfs_cb_data(data);
+       return 0;
+}
+
+int nfs_truncate_async(struct nfs_context *nfs, const char *path, off_t length, nfs_cb cb, void *private_data)
+{
+       off_t offset;
+
+       offset = length;
+
+       if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_truncate_continue_internal, NULL, NULL, offset) != 0) {
+               printf("Out of memory: failed to start parsing the path components\n");
+               return -2;
+       }
+
+       return 0;
+}
+
+
+
+