}
/* 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;
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;
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;
* 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.
*
*
* // 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;
#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
#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, ...)
{
}
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;
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;
}
/* 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;
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
};
* 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.
* 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.
* 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.
* 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.
--- /dev/null
+../../licenses/GPL-3
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+#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;
+}
+
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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;
+}
+
+
+
--- /dev/null
+#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();
+}
--- /dev/null
+/* 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;
+}
+
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.
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,
* 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.
--- /dev/null
+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
+
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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);
+}
+
+
--- /dev/null
+/*
+ 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);
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "rpc/nfsacl.h"
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ 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);
+
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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;
+}
+
+
+
+
+/*
+ * Async mkdir()
+ */
+static void nfs_mkdir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ MKDIR3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ char *str = data->continue_data;
+
+ str = &str[strlen(str) + 1];
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: MKDIR of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_mkdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ char *str = data->continue_data;
+
+ str = &str[strlen(str) + 1];
+
+ if (rpc_nfs_mkdir_async(nfs->rpc, nfs_mkdir_cb, &data->fh, str, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send MKDIR call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+ char *new_path;
+ char *ptr;
+
+ new_path = strdup(path);
+ if (new_path == NULL) {
+ printf("Out of memory, failed to allocate mode buffer for path\n");
+ return -1;
+ }
+
+ ptr = rindex(new_path, '/');
+ if (ptr == NULL) {
+ printf("Invalid path %s\n", path);
+ return -2;
+ }
+ *ptr = 0;
+
+ /* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */
+ if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_mkdir_continue_internal, new_path, free, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+
+
+/*
+ * Async rmdir()
+ */
+static void nfs_rmdir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ RMDIR3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ char *str = data->continue_data;
+
+ str = &str[strlen(str) + 1];
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: RMDIR of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_rmdir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ char *str = data->continue_data;
+
+ str = &str[strlen(str) + 1];
+
+ if (rpc_nfs_rmdir_async(nfs->rpc, nfs_rmdir_cb, &data->fh, str, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send RMDIR call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+ char *new_path;
+ char *ptr;
+
+ new_path = strdup(path);
+ if (new_path == NULL) {
+ printf("Out of memory, failed to allocate mode buffer for path\n");
+ return -1;
+ }
+
+ ptr = rindex(new_path, '/');
+ if (ptr == NULL) {
+ printf("Invalid path %s\n", path);
+ return -2;
+ }
+ *ptr = 0;
+
+ /* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */
+ if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_rmdir_continue_internal, new_path, free, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+
+/*
+ * Async creat()
+ */
+static void nfs_create_2_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ LOOKUP3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ struct nfsfh *nfsfh;
+ char *str = data->continue_data;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ str = &str[strlen(str) + 1];
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: CREATE of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+
+ return;
+ }
+
+ nfsfh = malloc(sizeof(struct nfsfh));
+ if (nfsfh == NULL) {
+ rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure");
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ bzero(nfsfh, sizeof(struct nfsfh));
+
+ /* steal the filehandle */
+ nfsfh->fh.data.data_len = data->fh.data.data_len;
+ nfsfh->fh.data.data_val = data->fh.data.data_val;
+ data->fh.data.data_val = NULL;
+
+ data->cb(0, nfs, nfsfh, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+
+
+static void nfs_creat_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ CREATE3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ char *str = data->continue_data;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ str = &str[strlen(str) + 1];
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: CREATE of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+
+ return;
+ }
+
+ if (rpc_nfs_lookup_async(nfs->rpc, nfs_create_2_cb, &data->fh, str, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send lookup call for %s/%s", data->saved_path, str);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ return;
+}
+
+static int nfs_creat_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ char *str = data->continue_data;
+
+ str = &str[strlen(str) + 1];
+
+ if (rpc_nfs_create_async(nfs->rpc, nfs_creat_1_cb, &data->fh, str, data->continue_int, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send CREATE call for %s/%s", data->path, str);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
+{
+ char *new_path;
+ char *ptr;
+
+ new_path = strdup(path);
+ if (new_path == NULL) {
+ printf("Out of memory, failed to allocate mode buffer for path\n");
+ return -1;
+ }
+
+ ptr = rindex(new_path, '/');
+ if (ptr == NULL) {
+ printf("Invalid path %s\n", path);
+ return -2;
+ }
+ *ptr = 0;
+
+ /* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */
+ if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_creat_continue_internal, new_path, free, mode) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+
+/*
+ * Async unlink()
+ */
+static void nfs_unlink_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ REMOVE3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ char *str = data->continue_data;
+
+ str = &str[strlen(str) + 1];
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: REMOVE of %s/%s failed with %s(%d)", data->saved_path, str, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_unlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ char *str = data->continue_data;
+
+ str = &str[strlen(str) + 1];
+
+ if (rpc_nfs_remove_async(nfs->rpc, nfs_unlink_cb, &data->fh, str, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send REMOVE call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+ char *new_path;
+ char *ptr;
+
+ new_path = strdup(path);
+ if (new_path == NULL) {
+ printf("Out of memory, failed to allocate mode buffer for path\n");
+ return -1;
+ }
+
+ ptr = rindex(new_path, '/');
+ if (ptr == NULL) {
+ printf("Invalid path %s\n", path);
+ return -2;
+ }
+ *ptr = 0;
+
+ /* new_path now points to the parent directory, and beyond the nul terminateor is the new directory to create */
+ if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_unlink_continue_internal, new_path, free, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+
+
+/*
+ * Async opendir()
+ */
+static void nfs_opendir_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ READDIR3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ struct nfsdir *nfsdir = data->continue_data;;
+ struct entry3 *entry;
+ uint64_t cookie;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ nfs_free_nfsdir(nfsdir);
+ data->continue_data = NULL;
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ nfs_free_nfsdir(nfsdir);
+ data->continue_data = NULL;
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: READDIR of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ nfs_free_nfsdir(nfsdir);
+ data->continue_data = NULL;
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ entry =res->READDIR3res_u.resok.reply.entries;
+ while (entry != NULL) {
+ struct nfsdirent *nfsdirent;
+
+ nfsdirent = malloc(sizeof(struct nfsdirent));
+ if (nfsdirent == NULL) {
+ data->cb(-ENOMEM, nfs, "Failed to allocate dirent", data->private_data);
+ nfs_free_nfsdir(nfsdir);
+ data->continue_data = NULL;
+ free_nfs_cb_data(data);
+ return;
+ }
+ bzero(nfsdirent, sizeof(struct nfsdirent));
+ nfsdirent->name = strdup(entry->name);
+ if (nfsdirent->name == NULL) {
+ data->cb(-ENOMEM, nfs, "Failed to allocate dirent->name", data->private_data);
+ nfs_free_nfsdir(nfsdir);
+ data->continue_data = NULL;
+ free_nfs_cb_data(data);
+ return;
+ }
+ nfsdirent->inode = entry->fileid;
+ nfsdirent->next = nfsdir->entries;
+ nfsdir->entries = nfsdirent;
+
+ cookie = entry->cookie;
+ entry = entry->nextentry;
+ }
+
+ if (res->READDIR3res_u.resok.reply.eof == 0) {
+ if (rpc_nfs_readdir_async(nfs->rpc, nfs_opendir_cb, &data->fh, cookie, res->READDIR3res_u.resok.cookieverf, 20000, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ nfs_free_nfsdir(nfsdir);
+ data->continue_data = NULL;
+ free_nfs_cb_data(data);
+ return;
+ }
+ return;
+ }
+
+ /* steal the dirhandle */
+ data->continue_data = NULL;
+ nfsdir->current = nfsdir->entries;
+
+ data->cb(0, nfs, nfsdir, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_opendir_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ cookieverf3 cv;
+
+ bzero(cv, sizeof(cookieverf3));
+ if (rpc_nfs_readdir_async(nfs->rpc, nfs_opendir_cb, &data->fh, 0, (char *)&cv, 20000, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send READDIR call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_opendir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+ struct nfsdir *nfsdir;
+
+ nfsdir = malloc(sizeof(struct nfsdir));
+ if (nfsdir == NULL) {
+ printf("failed to allocate buffer for nfsdir\n");
+ return -1;
+ }
+ bzero(nfsdir, sizeof(struct nfsdir));
+
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_opendir_continue_internal, nfsdir, free, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -2;
+ }
+
+ return 0;
+}
+
+
+struct nfsdirent *nfs_readdir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir)
+{
+ struct nfsdirent *nfsdirent = nfsdir->current;
+
+ if (nfsdir->current != NULL) {
+ nfsdir->current = nfsdir->current->next;
+ }
+ return nfsdirent;
+}
+
+
+void nfs_closedir(struct nfs_context *nfs _U_, struct nfsdir *nfsdir)
+{
+ nfs_free_nfsdir(nfsdir);
+}
+
+
+
+
+
+
+
+/*
+ * Async lseek()
+ */
+struct lseek_cb_data {
+ struct nfs_context *nfs;
+ struct nfsfh *nfsfh;
+ off_t offset;
+ nfs_cb cb;
+ void *private_data;
+};
+
+static void nfs_lseek_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ GETATTR3res *res;
+ struct lseek_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: GETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free(data);
+ return;
+ }
+
+ data->nfsfh->offset = data->offset + res->GETATTR3res_u.resok.obj_attributes.size;
+ data->cb(0, nfs, &data->nfsfh->offset, data->private_data);
+ free(data);
+}
+
+int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, nfs_cb cb, void *private_data)
+{
+ struct lseek_cb_data *data;
+
+ if (whence == SEEK_SET) {
+ nfsfh->offset = offset;
+ cb(0, nfs, &nfsfh->offset, private_data);
+ return 0;
+ }
+ if (whence == SEEK_CUR) {
+ nfsfh->offset += offset;
+ cb(0, nfs, &nfsfh->offset, private_data);
+ return 0;
+ }
+
+ data = malloc(sizeof(struct lseek_cb_data));
+ if (data == NULL) {
+ rpc_set_error(nfs->rpc, "Out Of Memory: Failed to malloc lseek cb data");
+ return -1;
+ }
+
+ data->nfs = nfs;
+ data->nfsfh = nfsfh;
+ data->offset = offset;
+ data->cb = cb;
+ data->private_data = private_data;
+
+ if (rpc_nfs_getattr_async(nfs->rpc, nfs_lseek_1_cb, &nfsfh->fh, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send LSEEK GETATTR call");
+ free(data);
+ return -2;
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * Async statvfs()
+ */
+static void nfs_statvfs_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ FSSTAT3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ struct statvfs svfs;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: FSSTAT of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ svfs.f_bsize = 4096;
+ svfs.f_frsize = 4096;
+ svfs.f_blocks = res->FSSTAT3res_u.resok.tbytes/4096;
+ svfs.f_bfree = res->FSSTAT3res_u.resok.fbytes/4096;
+ svfs.f_bavail = res->FSSTAT3res_u.resok.abytes/4096;
+ svfs.f_files = res->FSSTAT3res_u.resok.tfiles;
+ svfs.f_ffree = res->FSSTAT3res_u.resok.ffiles;
+ svfs.f_favail = res->FSSTAT3res_u.resok.afiles;
+ svfs.f_fsid = 0;
+ svfs.f_flag = 0;
+ svfs.f_namemax = 256;
+
+ data->cb(0, nfs, &svfs, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_statvfs_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ if (rpc_nfs_fsstat_async(nfs->rpc, nfs_statvfs_1_cb, &data->fh, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send FSSTAT call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_statvfs_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_statvfs_continue_internal, NULL, NULL, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
+/*
+ * Async readlink()
+ */
+static void nfs_readlink_1_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ READLINK3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: READLINK of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+
+ data->cb(0, nfs, res->READLINK3res_u.resok.data, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_readlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ if (rpc_nfs_readlink_async(nfs->rpc, nfs_readlink_1_cb, &data->fh, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send READLINK call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data)
+{
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_readlink_continue_internal, NULL, NULL, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
+/*
+ * Async chmod()
+ */
+static void nfs_chmod_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ SETATTR3res *res;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: SETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_chmod_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ SETATTR3args args;
+
+ bzero(&args, sizeof(SETATTR3args));
+ args.object.data.data_len = data->fh.data.data_len;
+ args.object.data.data_val = data->fh.data.data_val;
+ args.new_attributes.mode.set_it = 1;
+ args.new_attributes.mode.set_mode3_u.mode = data->continue_int;
+
+ if (rpc_nfs_setattr_async(nfs->rpc, nfs_chmod_cb, &args, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+
+int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
+{
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_chmod_continue_internal, NULL, NULL, mode) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Async fchmod()
+ */
+int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs_cb cb, void *private_data)
+{
+ struct nfs_cb_data *data;
+
+ data = malloc(sizeof(struct nfs_cb_data));
+ if (data == NULL) {
+ rpc_set_error(nfs->rpc, "out of memory");
+ printf("failed to allocate memory for nfs mount data\n");
+ return -1;
+ }
+ bzero(data, sizeof(struct nfs_cb_data));
+ data->nfs = nfs;
+ data->cb = cb;
+ data->private_data = private_data;
+ data->continue_int = mode;
+ data->fh.data.data_len = nfsfh->fh.data.data_len;
+ data->fh.data.data_val = malloc(data->fh.data.data_len);
+ if (data->fh.data.data_val == NULL) {
+ rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh");
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len);
+
+ if (nfs_chmod_continue_internal(nfs, data) != 0) {
+ free_nfs_cb_data(data);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Async chown()
+ */
+static void nfs_chown_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ SETATTR3res *res;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: SETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+struct nfs_chown_data {
+ uid_t uid;
+ gid_t gid;
+};
+
+static int nfs_chown_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ SETATTR3args args;
+ struct nfs_chown_data *chown_data = data->continue_data;
+
+ bzero(&args, sizeof(SETATTR3args));
+ args.object.data.data_len = data->fh.data.data_len;
+ args.object.data.data_val = data->fh.data.data_val;
+ if (chown_data->uid != (uid_t)-1) {
+ args.new_attributes.uid.set_it = 1;
+ args.new_attributes.uid.set_uid3_u.uid = chown_data->uid;
+ }
+ if (chown_data->gid != (gid_t)-1) {
+ args.new_attributes.gid.set_it = 1;
+ args.new_attributes.gid.set_gid3_u.gid = chown_data->gid;
+ }
+
+ if (rpc_nfs_setattr_async(nfs->rpc, nfs_chown_cb, &args, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+
+int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data)
+{
+ struct nfs_chown_data *chown_data;
+
+ chown_data = malloc(sizeof(struct nfs_chown_data));
+ if (chown_data == NULL) {
+ printf("Failed to allocate memory for chown data structure\n");
+ return -1;
+ }
+
+ chown_data->uid = uid;
+ chown_data->gid = gid;
+
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_chown_continue_internal, chown_data, free, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Async fchown()
+ */
+int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid, nfs_cb cb, void *private_data)
+{
+ struct nfs_cb_data *data;
+ struct nfs_chown_data *chown_data;
+
+ chown_data = malloc(sizeof(struct nfs_chown_data));
+ if (chown_data == NULL) {
+ printf("Failed to allocate memory for chown data structure\n");
+ return -1;
+ }
+
+ chown_data->uid = uid;
+ chown_data->gid = gid;
+
+
+ data = malloc(sizeof(struct nfs_cb_data));
+ if (data == NULL) {
+ rpc_set_error(nfs->rpc, "out of memory");
+ printf("failed to allocate memory for fchown data\n");
+ return -1;
+ }
+ bzero(data, sizeof(struct nfs_cb_data));
+ data->nfs = nfs;
+ data->cb = cb;
+ data->private_data = private_data;
+ data->continue_data = chown_data;
+ data->fh.data.data_len = nfsfh->fh.data.data_len;
+ data->fh.data.data_val = malloc(data->fh.data.data_len);
+ if (data->fh.data.data_val == NULL) {
+ rpc_set_error(nfs->rpc, "Out of memory: Failed to allocate fh");
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len);
+
+
+ if (nfs_chown_continue_internal(nfs, data) != 0) {
+ free_nfs_cb_data(data);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
+
+/*
+ * Async utimes()
+ */
+static void nfs_utimes_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ SETATTR3res *res;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: SETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_utimes_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ SETATTR3args args;
+ struct timeval *utimes_data = data->continue_data;
+
+ bzero(&args, sizeof(SETATTR3args));
+ args.object.data.data_len = data->fh.data.data_len;
+ args.object.data.data_val = data->fh.data.data_val;
+ if (utimes_data != NULL) {
+ args.new_attributes.atime.set_it = SET_TO_CLIENT_TIME;
+ args.new_attributes.atime.set_atime_u.atime.seconds = utimes_data[0].tv_sec;
+ args.new_attributes.atime.set_atime_u.atime.nseconds = utimes_data[0].tv_usec * 1000;
+ args.new_attributes.mtime.set_it = SET_TO_CLIENT_TIME;
+ args.new_attributes.mtime.set_mtime_u.mtime.seconds = utimes_data[1].tv_sec;
+ args.new_attributes.mtime.set_mtime_u.mtime.nseconds = utimes_data[1].tv_usec * 1000;
+ } else {
+ args.new_attributes.atime.set_it = SET_TO_SERVER_TIME;
+ args.new_attributes.mtime.set_it = SET_TO_SERVER_TIME;
+ }
+
+ if (rpc_nfs_setattr_async(nfs->rpc, nfs_utimes_cb, &args, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send SETATTR call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+
+int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data)
+{
+ struct timeval *new_times = NULL;
+
+ if (times != NULL) {
+ new_times = malloc(sizeof(struct timeval)*2);
+ if (new_times == NULL) {
+ printf("Failed to allocate memory for timeval structure\n");
+ return -1;
+ }
+
+ memcpy(new_times, times, sizeof(struct timeval)*2);
+ }
+
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_utimes_continue_internal, new_times, free, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Async utime()
+ */
+int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *times, nfs_cb cb, void *private_data)
+{
+ struct timeval *new_times = NULL;
+
+ if (times != NULL) {
+ new_times = malloc(sizeof(struct timeval)*2);
+ if (new_times == NULL) {
+ printf("Failed to allocate memory for timeval structure\n");
+ return -1;
+ }
+
+ new_times[0].tv_sec = times->actime;
+ new_times[0].tv_usec = 0;
+ new_times[1].tv_sec = times->modtime;
+ new_times[1].tv_usec = 0;
+ }
+
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_utimes_continue_internal, new_times, free, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
+
+/*
+ * Async access()
+ */
+static void nfs_access_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ ACCESS3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ unsigned int nfsmode = 0;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: ACCESS of %s failed with %s(%d)", data->saved_path, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ if (data->continue_int & R_OK) {
+ nfsmode |= ACCESS3_READ;
+ }
+ if (data->continue_int & W_OK) {
+ nfsmode |= ACCESS3_MODIFY;
+ }
+ if (data->continue_int & X_OK) {
+ nfsmode |= ACCESS3_EXECUTE;
+ }
+
+ if (res->ACCESS3res_u.resok.access != nfsmode) {
+ rpc_set_error(nfs->rpc, "NFS: ACCESS denied. Required access %c%c%c. Allowed access %c%c%c",
+ nfsmode&ACCESS3_READ?'r':'-',
+ nfsmode&ACCESS3_MODIFY?'w':'-',
+ nfsmode&ACCESS3_EXECUTE?'x':'-',
+ res->ACCESS3res_u.resok.access&ACCESS3_READ?'r':'-',
+ res->ACCESS3res_u.resok.access&ACCESS3_MODIFY?'w':'-',
+ res->ACCESS3res_u.resok.access&ACCESS3_EXECUTE?'x':'-');
+ data->cb(-EACCES, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_access_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ int nfsmode = 0;
+
+ if (data->continue_int & R_OK) {
+ nfsmode |= ACCESS3_READ;
+ }
+ if (data->continue_int & W_OK) {
+ nfsmode |= ACCESS3_MODIFY;
+ }
+ if (data->continue_int & X_OK) {
+ nfsmode |= ACCESS3_EXECUTE;
+ }
+
+ if (rpc_nfs_access_async(nfs->rpc, nfs_access_cb, &data->fh, nfsmode, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send OPEN ACCESS call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_access_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data)
+{
+ if (nfs_lookuppath_async(nfs, path, cb, private_data, nfs_access_continue_internal, NULL, NULL, mode) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -2;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Async symlink()
+ */
+struct nfs_symlink_data {
+ char *oldpath;
+ char *newpathparent;
+ char *newpathobject;
+};
+
+static void free_nfs_symlink_data(void *mem)
+{
+ struct nfs_symlink_data *data = mem;
+
+ if (data->oldpath != NULL) {
+ free(data->oldpath);
+ }
+ if (data->newpathparent != NULL) {
+ free(data->newpathparent);
+ }
+ if (data->newpathobject != NULL) {
+ free(data->newpathobject);
+ }
+ free(data);
+}
+
+static void nfs_symlink_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ SYMLINK3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ struct nfs_symlink_data *symlink_data = data->continue_data;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: SYMLINK %s/%s -> %s failed with %s(%d)", symlink_data->newpathparent, symlink_data->newpathobject, symlink_data->oldpath, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_symlink_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ struct nfs_symlink_data *symlink_data = data->continue_data;
+
+ if (rpc_nfs_symlink_async(nfs->rpc, nfs_symlink_cb, &data->fh, symlink_data->newpathobject, symlink_data->oldpath, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send SYMLINK call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+int nfs_symlink_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data)
+{
+ char *ptr;
+ struct nfs_symlink_data *symlink_data;
+
+ symlink_data = malloc(sizeof(struct nfs_symlink_data));
+ if (symlink_data == NULL) {
+ printf("Out of memory, failed to allocate buffer for symlink data\n");
+ return -1;
+ }
+ bzero(symlink_data, sizeof(struct nfs_symlink_data));
+
+ symlink_data->oldpath = strdup(oldpath);
+ if (symlink_data->oldpath == NULL) {
+ printf("Out of memory, failed to allocate buffer for oldpath\n");
+ free_nfs_symlink_data(symlink_data);
+ return -2;
+ }
+
+ symlink_data->newpathparent = strdup(newpath);
+ if (symlink_data->newpathparent == NULL) {
+ printf("Out of memory, failed to allocate mode buffer for new path\n");
+ free_nfs_symlink_data(symlink_data);
+ return -3;
+ }
+
+ ptr = rindex(symlink_data->newpathparent, '/');
+ if (ptr == NULL) {
+ printf("Invalid path %s\n", oldpath);
+ free_nfs_symlink_data(symlink_data);
+ return -4;
+ }
+ *ptr = 0;
+ ptr++;
+
+ symlink_data->newpathobject = strdup(ptr);
+ if (symlink_data->newpathobject == NULL) {
+ printf("Out of memory, failed to allocate mode buffer for new path\n");
+ free_nfs_symlink_data(symlink_data);
+ return -5;
+ }
+
+ if (nfs_lookuppath_async(nfs, symlink_data->newpathparent, cb, private_data, nfs_symlink_continue_internal, symlink_data, free_nfs_symlink_data, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -6;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Async rename()
+ */
+struct nfs_rename_data {
+ char *oldpath;
+ char *oldobject;
+ struct nfs_fh3 olddir;
+ char *newpath;
+ char *newobject;
+ struct nfs_fh3 newdir;
+};
+
+static void free_nfs_rename_data(void *mem)
+{
+ struct nfs_rename_data *data = mem;
+
+ if (data->oldpath != NULL) {
+ free(data->oldpath);
+ }
+ if (data->olddir.data.data_val != NULL) {
+ free(data->olddir.data.data_val);
+ }
+ if (data->newpath != NULL) {
+ free(data->newpath);
+ }
+ if (data->newdir.data.data_val != NULL) {
+ free(data->newdir.data.data_val);
+ }
+ free(data);
+}
+
+static void nfs_rename_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ RENAME3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ struct nfs_rename_data *rename_data = data->continue_data;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: RENAME %s/%s -> %s/%s failed with %s(%d)", rename_data->oldpath, rename_data->oldobject, rename_data->newpath, rename_data->newobject, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_rename_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ struct nfs_rename_data *rename_data = data->continue_data;
+
+ /* steal the filehandle */
+ rename_data->newdir.data.data_len = data->fh.data.data_len;
+ rename_data->newdir.data.data_val = data->fh.data.data_val;
+ data->fh.data.data_val = NULL;
+
+ if (rpc_nfs_rename_async(nfs->rpc, nfs_rename_cb, &rename_data->olddir, rename_data->oldobject, &rename_data->newdir, rename_data->newobject, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send RENAME call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int nfs_rename_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ struct nfs_rename_data *rename_data = data->continue_data;
+
+ /* steal the filehandle */
+ rename_data->olddir.data.data_len = data->fh.data.data_len;
+ rename_data->olddir.data.data_val = data->fh.data.data_val;
+ data->fh.data.data_val = NULL;
+
+ if (nfs_lookuppath_async(nfs, rename_data->newpath, data->cb, data->private_data, nfs_rename_continue_2_internal, rename_data, free_nfs_rename_data, 0) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", rename_data->newpath);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ data->continue_data = NULL;
+ free_nfs_cb_data(data);
+
+ return 0;
+}
+
+
+int nfs_rename_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data)
+{
+ char *ptr;
+ struct nfs_rename_data *rename_data;
+
+ rename_data = malloc(sizeof(struct nfs_rename_data));
+ if (rename_data == NULL) {
+ printf("Out of memory, failed to allocate buffer for rename data\n");
+ return -1;
+ }
+ bzero(rename_data, sizeof(struct nfs_rename_data));
+
+ rename_data->oldpath = strdup(oldpath);
+ if (rename_data->oldpath == NULL) {
+ printf("Out of memory, failed to allocate buffer for oldpath\n");
+ free_nfs_rename_data(rename_data);
+ return -2;
+ }
+ ptr = rindex(rename_data->oldpath, '/');
+ if (ptr == NULL) {
+ printf("Invalid path %s\n", oldpath);
+ free_nfs_rename_data(rename_data);
+ return -3;
+ }
+ *ptr = 0;
+ ptr++;
+ rename_data->oldobject = ptr;
+
+
+ rename_data->newpath = strdup(newpath);
+ if (rename_data->newpath == NULL) {
+ printf("Out of memory, failed to allocate buffer for newpath\n");
+ free_nfs_rename_data(rename_data);
+ return -4;
+ }
+ ptr = rindex(rename_data->newpath, '/');
+ if (ptr == NULL) {
+ printf("Invalid path %s\n", newpath);
+ free_nfs_rename_data(rename_data);
+ return -5;
+ }
+ *ptr = 0;
+ ptr++;
+ rename_data->newobject = ptr;
+
+
+ if (nfs_lookuppath_async(nfs, rename_data->oldpath, cb, private_data, nfs_rename_continue_1_internal, rename_data, free_nfs_rename_data, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -6;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Async link()
+ */
+struct nfs_link_data {
+ char *oldpath;
+ struct nfs_fh3 oldfh;
+ char *newpath;
+ char *newobject;
+ struct nfs_fh3 newdir;
+};
+
+static void free_nfs_link_data(void *mem)
+{
+ struct nfs_link_data *data = mem;
+
+ if (data->oldpath != NULL) {
+ free(data->oldpath);
+ }
+ if (data->oldfh.data.data_val != NULL) {
+ free(data->oldfh.data.data_val);
+ }
+ if (data->newpath != NULL) {
+ free(data->newpath);
+ }
+ if (data->newdir.data.data_val != NULL) {
+ free(data->newdir.data.data_val);
+ }
+ free(data);
+}
+
+static void nfs_link_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+ LINK3res *res;
+ struct nfs_cb_data *data = private_data;
+ struct nfs_context *nfs = data->nfs;
+ struct nfs_link_data *link_data = data->continue_data;
+
+ if (status == RPC_STATUS_ERROR) {
+ data->cb(-EFAULT, nfs, command_data, data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+ if (status == RPC_STATUS_CANCEL) {
+ data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ res = command_data;
+ if (res->status != NFS3_OK) {
+ rpc_set_error(nfs->rpc, "NFS: LINK %s -> %s/%s failed with %s(%d)", link_data->oldpath, link_data->newpath, link_data->newobject, nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+ data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return;
+ }
+
+ data->cb(0, nfs, NULL, data->private_data);
+ free_nfs_cb_data(data);
+}
+
+static int nfs_link_continue_2_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ struct nfs_link_data *link_data = data->continue_data;
+
+ /* steal the filehandle */
+ link_data->newdir.data.data_len = data->fh.data.data_len;
+ link_data->newdir.data.data_val = data->fh.data.data_val;
+ data->fh.data.data_val = NULL;
+
+ if (rpc_nfs_link_async(nfs->rpc, nfs_link_cb, &link_data->oldfh, &link_data->newdir, link_data->newobject, data) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send LINK call for %s", data->path);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int nfs_link_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+ struct nfs_link_data *link_data = data->continue_data;
+
+ /* steal the filehandle */
+ link_data->oldfh.data.data_len = data->fh.data.data_len;
+ link_data->oldfh.data.data_val = data->fh.data.data_val;
+ data->fh.data.data_val = NULL;
+
+ if (nfs_lookuppath_async(nfs, link_data->newpath, data->cb, data->private_data, nfs_link_continue_2_internal, link_data, free_nfs_link_data, 0) != 0) {
+ rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", link_data->newpath);
+ data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+ free_nfs_cb_data(data);
+ return -1;
+ }
+ data->continue_data = NULL;
+ free_nfs_cb_data(data);
+
+ return 0;
+}
+
+
+int nfs_link_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data)
+{
+ char *ptr;
+ struct nfs_link_data *link_data;
+
+ link_data = malloc(sizeof(struct nfs_link_data));
+ if (link_data == NULL) {
+ printf("Out of memory, failed to allocate buffer for link data\n");
+ return -1;
+ }
+ bzero(link_data, sizeof(struct nfs_link_data));
+
+ link_data->oldpath = strdup(oldpath);
+ if (link_data->oldpath == NULL) {
+ printf("Out of memory, failed to allocate buffer for oldpath\n");
+ free_nfs_link_data(link_data);
+ return -2;
+ }
+
+ link_data->newpath = strdup(newpath);
+ if (link_data->newpath == NULL) {
+ printf("Out of memory, failed to allocate buffer for newpath\n");
+ free_nfs_link_data(link_data);
+ return -4;
+ }
+ ptr = rindex(link_data->newpath, '/');
+ if (ptr == NULL) {
+ printf("Invalid path %s\n", newpath);
+ free_nfs_link_data(link_data);
+ return -5;
+ }
+ *ptr = 0;
+ ptr++;
+ link_data->newobject = ptr;
+
+
+ if (nfs_lookuppath_async(nfs, link_data->oldpath, cb, private_data, nfs_link_continue_1_internal, link_data, free_nfs_link_data, 0) != 0) {
+ printf("Out of memory: failed to start parsing the path components\n");
+ return -6;
+ }
+
+ return 0;
+}
+
+
+//qqq replace later with lseek()
+off_t nfs_get_current_offset(struct nfsfh *nfsfh)
+{
+ return nfsfh->offset;
+}
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <errno.h>
+#include <rpc/xdr.h>
+#include "nfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-private.h"
+#include "rpc/mount.h"
+
+
+int rpc_mount_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for mount/null call");
+ return -1;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for mount/null call");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
+int rpc_mount_mnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_MNT, cb, private_data, (xdrproc_t)xdr_mountres3, sizeof(mountres3));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for mount/mnt call");
+ return -1;
+ }
+
+ if (xdr_dirpath(&pdu->xdr, &export) == 0) {
+ rpc_set_error(rpc, "XDR error. Failed to encode mount/mnt call");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for mount/mnt call");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
+int rpc_mount_dump_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_DUMP, cb, private_data, (xdrproc_t)xdr_mountlist, sizeof(mountlist));
+ if (pdu == NULL) {
+ printf("Failed to allocate pdu for mount/dump\n");
+ return -1;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ printf("Failed to queue mount/dump pdu\n");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
+int rpc_mount_umnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_UMNT, cb, private_data, (xdrproc_t)xdr_void, 0);
+ if (pdu == NULL) {
+ printf("Failed to allocate pdu for mount/umnt\n");
+ return -1;
+ }
+
+ if (xdr_dirpath(&pdu->xdr, &export) == 0) {
+ printf("failed to encode dirpath for mount/umnt\n");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ printf("Failed to queue mount/umnt pdu\n");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
+int rpc_mount_umntall_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_UMNTALL, cb, private_data, (xdrproc_t)xdr_void, 0);
+ if (pdu == NULL) {
+ printf("Failed to allocate pdu for mount/umntall\n");
+ return -1;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ printf("Failed to queue mount/umntall pdu\n");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
+int rpc_mount_export_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_EXPORT, cb, private_data, (xdrproc_t)xdr_exports, sizeof(exports));
+ if (pdu == NULL) {
+ printf("Failed to allocate pdu for mount/export\n");
+ return -1;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ printf("Failed to queue mount/export pdu\n");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
+char *mountstat3_to_str(int st)
+{
+ enum mountstat3 stat = st;
+
+ char *str = "unknown mount stat";
+ switch (stat) {
+ case MNT3_OK: str="MNT3_OK"; break;
+ case MNT3ERR_PERM: str="MNT3ERR_PERM"; break;
+ case MNT3ERR_NOENT: str="MNT3ERR_NOENT"; break;
+ case MNT3ERR_IO: str="MNT3ERR_IO"; break;
+ case MNT3ERR_ACCES: str="MNT3ERR_ACCES"; break;
+ case MNT3ERR_NOTDIR: str="MNT3ERR_NOTDIR"; break;
+ case MNT3ERR_INVAL: str="MNT3ERR_INVAL"; break;
+ case MNT3ERR_NAMETOOLONG: str="MNT3ERR_NAMETOOLONG"; break;
+ case MNT3ERR_NOTSUPP: str="MNT3ERR_NOTSUPP"; break;
+ case MNT3ERR_SERVERFAULT: str="MNT3ERR_SERVERFAULT"; break;
+ }
+ return str;
+}
+
+
+int mountstat3_to_errno(int st)
+{
+ enum mountstat3 stat = st;
+
+ switch (stat) {
+ case MNT3_OK: return 0; break;
+ case MNT3ERR_PERM: return -EPERM; break;
+ case MNT3ERR_NOENT: return -EPERM; break;
+ case MNT3ERR_IO: return -EIO; break;
+ case MNT3ERR_ACCES: return -EACCES; break;
+ case MNT3ERR_NOTDIR: return -ENOTDIR; break;
+ case MNT3ERR_INVAL: return -EINVAL; break;
+ case MNT3ERR_NAMETOOLONG: return -E2BIG; break;
+ case MNT3ERR_NOTSUPP: return -EINVAL; break;
+ case MNT3ERR_SERVERFAULT: return -EIO; break;
+ }
+ return -ERANGE;
+}
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <rpc/xdr.h>
+#include "nfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-private.h"
+#include "rpc/nfs.h"
+
+
+
+char *nfsstat3_to_str(int error)
+{
+ switch (error) {
+ case NFS3_OK: return "NFS3_OK"; break;
+ case NFS3ERR_PERM: return "NFS3ERR_PERM"; break;
+ case NFS3ERR_NOENT: return "NFS3ERR_NOENT"; break;
+ case NFS3ERR_IO: return "NFS3ERR_IO"; break;
+ case NFS3ERR_NXIO: return "NFS3ERR_NXIO"; break;
+ case NFS3ERR_ACCES: return "NFS3ERR_ACCES"; break;
+ case NFS3ERR_EXIST: return "NFS3ERR_EXIST"; break;
+ case NFS3ERR_XDEV: return "NFS3ERR_XDEV"; break;
+ case NFS3ERR_NODEV: return "NFS3ERR_NODEV"; break;
+ case NFS3ERR_NOTDIR: return "NFS3ERR_NOTDIR"; break;
+ case NFS3ERR_ISDIR: return "NFS3ERR_ISDIR"; break;
+ case NFS3ERR_INVAL: return "NFS3ERR_INVAL"; break;
+ case NFS3ERR_FBIG: return "NFS3ERR_FBIG"; break;
+ case NFS3ERR_NOSPC: return "NFS3ERR_NOSPC"; break;
+ case NFS3ERR_ROFS: return "NFS3ERR_ROFS"; break;
+ case NFS3ERR_MLINK: return "NFS3ERR_MLINK"; break;
+ case NFS3ERR_NAMETOOLONG: return "NFS3ERR_NAMETOOLONG"; break;
+ case NFS3ERR_NOTEMPTY: return "NFS3ERR_NOTEMPTY"; break;
+ case NFS3ERR_DQUOT: return "NFS3ERR_DQUOT"; break;
+ case NFS3ERR_STALE: return "NFS3ERR_STALE"; break;
+ case NFS3ERR_REMOTE: return "NFS3ERR_REMOTE"; break;
+ case NFS3ERR_BADHANDLE: return "NFS3ERR_BADHANDLE"; break;
+ case NFS3ERR_NOT_SYNC: return "NFS3ERR_NOT_SYNC"; break;
+ case NFS3ERR_BAD_COOKIE: return "NFS3ERR_BAD_COOKIE"; break;
+ case NFS3ERR_NOTSUPP: return "NFS3ERR_NOTSUPP"; break;
+ case NFS3ERR_TOOSMALL: return "NFS3ERR_TOOSMALL"; break;
+ case NFS3ERR_SERVERFAULT: return "NFS3ERR_SERVERFAULT"; break;
+ case NFS3ERR_BADTYPE: return "NFS3ERR_BADTYPE"; break;
+ case NFS3ERR_JUKEBOX: return "NFS3ERR_JUKEBOX"; break;
+ };
+ return "unknown nfs error";
+}
+
+int nfsstat3_to_errno(int error)
+{
+ switch (error) {
+ case NFS3_OK: return 0; break;
+ case NFS3ERR_PERM: return -EPERM; break;
+ case NFS3ERR_NOENT: return -ENOENT; break;
+ case NFS3ERR_IO: return -EIO; break;
+ case NFS3ERR_NXIO: return -ENXIO; break;
+ case NFS3ERR_ACCES: return -EACCES; break;
+ case NFS3ERR_EXIST: return -EEXIST; break;
+ case NFS3ERR_XDEV: return -EXDEV; break;
+ case NFS3ERR_NODEV: return -ENODEV; break;
+ case NFS3ERR_NOTDIR: return -ENOTDIR; break;
+ case NFS3ERR_ISDIR: return -EISDIR; break;
+ case NFS3ERR_INVAL: return -EINVAL; break;
+ case NFS3ERR_FBIG: return -EFBIG; break;
+ case NFS3ERR_NOSPC: return -ENOSPC; break;
+ case NFS3ERR_ROFS: return -EROFS; break;
+ case NFS3ERR_MLINK: return -EMLINK; break;
+ case NFS3ERR_NAMETOOLONG: return -ENAMETOOLONG; break;
+ case NFS3ERR_NOTEMPTY: return -EEXIST; break;
+ case NFS3ERR_DQUOT: return -ERANGE; break;
+ case NFS3ERR_STALE: return -EIO; break;
+ case NFS3ERR_REMOTE: return -EIO; break;
+ case NFS3ERR_BADHANDLE: return -EIO; break;
+ case NFS3ERR_NOT_SYNC: return -EIO; break;
+ case NFS3ERR_BAD_COOKIE: return -EIO; break;
+ case NFS3ERR_NOTSUPP: return -EINVAL; break;
+ case NFS3ERR_TOOSMALL: return -EIO; break;
+ case NFS3ERR_SERVERFAULT: return -EIO; break;
+ case NFS3ERR_BADTYPE: return -EINVAL; break;
+ case NFS3ERR_JUKEBOX: return -EAGAIN; break;
+ };
+ return -ERANGE;
+}
+
+
+int rpc_nfs_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/null call");
+ return -1;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/null call");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
+int rpc_nfs_getattr_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ GETATTR3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_GETATTR, cb, private_data, (xdrproc_t)xdr_GETATTR3res, sizeof(GETATTR3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/null call");
+ return -1;
+ }
+
+ args.object.data.data_len = fh->data.data_len;
+ args.object.data.data_val = fh->data.data_val;
+
+ if (xdr_GETATTR3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode GETATTR3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/null call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+int rpc_nfs_lookup_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ LOOKUP3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_LOOKUP, cb, private_data, (xdrproc_t)xdr_LOOKUP3res, sizeof(LOOKUP3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/lookup call");
+ return -1;
+ }
+
+ args.what.dir.data.data_len = fh->data.data_len;
+ args.what.dir.data.data_val = fh->data.data_val;
+ args.what.name = name;
+
+ if (xdr_LOOKUP3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode LOOKUP3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/lookup call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+int rpc_nfs_access_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, int access, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ ACCESS3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_ACCESS, cb, private_data, (xdrproc_t)xdr_ACCESS3res, sizeof(ACCESS3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/access call");
+ return -1;
+ }
+
+ args.object.data.data_len = fh->data.data_len;
+ args.object.data.data_val = fh->data.data_val;
+ args.access = access;
+
+ if (xdr_ACCESS3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode ACCESS3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/access call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+int rpc_nfs_read_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, off_t offset, size_t count, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ READ3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_READ, cb, private_data, (xdrproc_t)xdr_READ3res, sizeof(READ3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/read call");
+ return -1;
+ }
+
+ args.file.data.data_len = fh->data.data_len;
+ args.file.data.data_val = fh->data.data_val;
+ args.offset = offset;
+ args.count = count;
+
+ if (xdr_READ3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode READ3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/read call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+int rpc_nfs_write_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *buf, off_t offset, size_t count, int stable_how, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ WRITE3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_WRITE, cb, private_data, (xdrproc_t)xdr_WRITE3res, sizeof(WRITE3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/write call");
+ return -1;
+ }
+
+ args.file.data.data_len = fh->data.data_len;
+ args.file.data.data_val = fh->data.data_val;
+ args.offset = offset;
+ args.count = count;
+ args.stable = stable_how;;
+ args.data.data_len = count;
+ args.data.data_val = buf;
+
+ if (xdr_WRITE3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode WRITE3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/write call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+int rpc_nfs_commit_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ COMMIT3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_COMMIT, cb, private_data, (xdrproc_t)xdr_COMMIT3res, sizeof(COMMIT3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/commit call");
+ return -1;
+ }
+
+ args.file.data.data_len = fh->data.data_len;
+ args.file.data.data_val = fh->data.data_val;
+ args.offset = 0;
+ args.count = 0;
+
+ if (xdr_COMMIT3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode WRITE3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/commit call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+int rpc_nfs_setattr_async(struct rpc_context *rpc, rpc_cb cb, SETATTR3args *args, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_SETATTR, cb, private_data, (xdrproc_t)xdr_SETATTR3res, sizeof(SETATTR3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/setattr call");
+ return -1;
+ }
+
+ if (xdr_SETATTR3args(&pdu->xdr, args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode SETATTR3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/setattr call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+int rpc_nfs_mkdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ MKDIR3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_MKDIR, cb, private_data, (xdrproc_t)xdr_MKDIR3res, sizeof(MKDIR3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/setattr call");
+ return -1;
+ }
+
+ bzero(&args, sizeof(MKDIR3args));
+ args.where.dir.data.data_len = fh->data.data_len;
+ args.where.dir.data.data_val = fh->data.data_val;
+ args.where.name = dir;
+ args.attributes.mode.set_it = 1;
+ args.attributes.mode.set_mode3_u.mode = 0755;
+
+ if (xdr_MKDIR3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode MKDIR3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/mkdir call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+
+int rpc_nfs_rmdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *dir, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ RMDIR3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_RMDIR, cb, private_data, (xdrproc_t)xdr_RMDIR3res, sizeof(RMDIR3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/rmdir call");
+ return -1;
+ }
+
+ bzero(&args, sizeof(RMDIR3args));
+ args.object.dir.data.data_len = fh->data.data_len;
+ args.object.dir.data.data_val = fh->data.data_val;
+ args.object.name = dir;
+
+ if (xdr_RMDIR3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode RMDIR3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/rmdir call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+int rpc_nfs_create_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *file, int mode, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ CREATE3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_CREATE, cb, private_data, (xdrproc_t)xdr_CREATE3res, sizeof(CREATE3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/create call");
+ return -1;
+ }
+
+ bzero(&args, sizeof(CREATE3args));
+ args.where.dir.data.data_len = fh->data.data_len;
+ args.where.dir.data.data_val = fh->data.data_val;
+ args.where.name = file;
+ args.how.mode = UNCHECKED;
+ args.how.createhow3_u.obj_attributes.mode.set_it = 1;
+ args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = mode;
+
+ if (xdr_CREATE3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode CREATE3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/create call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+
+int rpc_nfs_remove_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *file, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ REMOVE3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_REMOVE, cb, private_data, (xdrproc_t)xdr_REMOVE3res, sizeof(REMOVE3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/remove call");
+ return -1;
+ }
+
+ bzero(&args, sizeof(REMOVE3args));
+ args.object.dir.data.data_len = fh->data.data_len;
+ args.object.dir.data.data_val = fh->data.data_val;
+ args.object.name = file;
+
+ if (xdr_REMOVE3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode REMOVE3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/remove call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+int rpc_nfs_readdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, uint64_t cookie, char *cookieverf, int count, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ READDIR3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_READDIR, cb, private_data, (xdrproc_t)xdr_READDIR3res, sizeof(READDIR3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/readdir call");
+ return -1;
+ }
+
+ bzero(&args, sizeof(READDIR3args));
+ args.dir.data.data_len = fh->data.data_len;
+ args.dir.data.data_val = fh->data.data_val;
+ args.cookie = cookie;
+ memcpy(&args.cookieverf, cookieverf, sizeof(cookieverf3));
+ args.count = count;
+
+ if (xdr_READDIR3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode READDIR3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/readdir call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+int rpc_nfs_fsstat_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ FSSTAT3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_FSSTAT, cb, private_data, (xdrproc_t)xdr_FSSTAT3res, sizeof(FSSTAT3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/fsstat call");
+ return -1;
+ }
+
+ args.fsroot.data.data_len = fh->data.data_len;
+ args.fsroot.data.data_val = fh->data.data_val;
+
+ if (xdr_FSSTAT3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode FSSTAT3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/fsstat call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+int rpc_nfs_readlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ READLINK3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_READLINK, cb, private_data, (xdrproc_t)xdr_READLINK3res, sizeof(READLINK3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/readlink call");
+ return -1;
+ }
+
+ args.symlink.data.data_len = fh->data.data_len;
+ args.symlink.data.data_val = fh->data.data_val;
+
+ if (xdr_READLINK3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode READLINK3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/readlink call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+int rpc_nfs_symlink_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *newname, char *oldpath, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ SYMLINK3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_SYMLINK, cb, private_data, (xdrproc_t)xdr_SYMLINK3res, sizeof(SYMLINK3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/symlink call");
+ return -1;
+ }
+
+ bzero(&args, sizeof(SYMLINK3args));
+ args.where.dir.data.data_len = fh->data.data_len;
+ args.where.dir.data.data_val = fh->data.data_val;
+ args.where.name = newname;
+ args.symlink.symlink_attributes.mode.set_it = 1;
+ args.symlink.symlink_attributes.mode.set_mode3_u.mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH;
+ args.symlink.symlink_data = oldpath;
+
+ if (xdr_SYMLINK3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode SYMLINK3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/symlink call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+
+int rpc_nfs_rename_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *olddir, char *oldname, struct nfs_fh3 *newdir, char *newname, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ RENAME3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_RENAME, cb, private_data, (xdrproc_t)xdr_RENAME3res, sizeof(RENAME3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/rename call");
+ return -1;
+ }
+
+ bzero(&args, sizeof(RENAME3args));
+ args.from.dir.data.data_len = olddir->data.data_len;
+ args.from.dir.data.data_val = olddir->data.data_val;
+ args.from.name = oldname;
+ args.to.dir.data.data_len = newdir->data.data_len;
+ args.to.dir.data.data_val = newdir->data.data_val;
+ args.to.name = newname;
+
+ if (xdr_RENAME3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode RENAME3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/rename call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+
+int rpc_nfs_link_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *file, struct nfs_fh3 *newdir, char *newname, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ LINK3args args;
+
+ pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_LINK, cb, private_data, (xdrproc_t)xdr_LINK3res, sizeof(LINK3res));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/link call");
+ return -1;
+ }
+
+ bzero(&args, sizeof(LINK3args));
+ args.file.data.data_len = file->data.data_len;
+ args.file.data.data_val = file->data.data_val;
+ args.link.dir.data.data_len = newdir->data.data_len;
+ args.link.dir.data.data_val = newdir->data.data_val;
+ args.link.name = newname;
+
+ if (xdr_LINK3args(&pdu->xdr, &args) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode LINK3args");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/link call");
+ rpc_free_pdu(rpc, pdu);
+ return -3;
+ }
+
+ return 0;
+}
+
+
+
+
--- /dev/null
+/*
+ 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 highlevel interface to access NFS resources using a posix-like interface
+ */
+#include <stdint.h>
+
+struct nfs_context;
+
+/*
+ * Used for interfacing the async version of the api into an external eventsystem
+ */
+int nfs_get_fd(struct nfs_context *nfs);
+int nfs_which_events(struct nfs_context *nfs);
+int nfs_service(struct nfs_context *nfs, int revents);
+
+/*
+ * Used if you need different credentials than the default for the current user.
+ */
+struct AUTH;
+void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth);
+
+
+/*
+ * When an operation failed, this function can extract a detailed error string.
+ */
+char *nfs_get_error(struct nfs_context *nfs);
+
+
+/*
+ * Callback for all async nfs functions
+ */
+typedef void (*nfs_cb)(int err, struct nfs_context *nfs, void *data, void *private_data);
+
+
+
+
+/*
+ * NFS CONTEXT.
+ */
+/*
+ * Create an NFS context.
+ * Function returns
+ * NULL : Failed to create a context.
+ * struct nfs_context * : A pointer to an nfs context.
+ */
+struct nfs_context *nfs_init_context(void);
+/*
+ * Destroy an nfs context.
+ */
+void nfs_destroy_context(struct nfs_context *nfs);
+
+
+/*
+ * A libnfs filehandle
+ */
+struct nfsfh;
+
+
+
+/*
+ * MOUNT THE EXPORT
+ */
+/*
+ * Async nfs mount.
+ * This function will try to connect to the server and mount the export.
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_mount_async(struct nfs_context *nfs, const char *server, const char *export, nfs_cb cb, void *private_data);
+/*
+ * Sync nfs mount.
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_mount_sync(struct nfs_context *nfs, const char *server, const char *export);
+
+
+
+
+/*
+ * STAT()
+ */
+/*
+ * Async stat(<filename>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is struct stat *
+ * -errno : An error occured.
+ * data is the error string.
+ */
+struct stat;
+int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
+/*
+ * Sync stat(<filename>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_stat_sync(struct nfs_context *nfs, const char *path, struct stat *st);
+
+
+/*
+ * FSTAT()
+ */
+/*
+ * Async fstat(nfsfh *)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is struct stat *
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_fstat_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data);
+/*
+ * Sync fstat(nfsfh *)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_fstat_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st);
+
+
+
+/*
+ * OPEN()
+ */
+/*
+ * Async open(<filename>)
+ *
+ * mode is a combination of the flags : O_RDOLNY, O_WRONLY, O_RDWR , O_SYNC
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is a struct *nfsfh;
+ * The nfsfh is close using nfs_close().
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_open_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
+/*
+ * Sync open(<filename>)
+ * Function returns
+ * 0 : The operation was successfull. *nfsfh is filled in.
+ * -errno : The command failed.
+ */
+int nfs_open_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh);
+
+
+
+
+/*
+ * CLOSE
+ */
+/*
+ * Async close(nfsfh)
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data);
+/*
+ * Sync close(nfsfh)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_close_sync(struct nfs_context *nfs, struct nfsfh *nfsfh);
+
+
+/*
+ * PREAD()
+ */
+/*
+ * Async pread()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * >=0 : Success.
+ * status is numer of bytes read.
+ * data is a pointer to the returned data.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, nfs_cb cb, void *private_data);
+/*
+ * Sync pread()
+ * Function returns
+ * >=0 : numer of bytes read.
+ * -errno : An error occured.
+ */
+int nfs_pread_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf);
+
+
+
+/*
+ * READ()
+ */
+/*
+ * Async read()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * >=0 : Success.
+ * status is numer of bytes read.
+ * data is a pointer to the returned data.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, nfs_cb cb, void *private_data);
+/*
+ * Sync read()
+ * Function returns
+ * >=0 : numer of bytes read.
+ * -errno : An error occured.
+ */
+int nfs_read_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf);
+
+
+
+
+/*
+ * PWRITE()
+ */
+/*
+ * Async pwrite()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * >=0 : Success.
+ * status is numer of bytes written.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf, nfs_cb cb, void *private_data);
+/*
+ * Sync pwrite()
+ * Function returns
+ * >=0 : numer of bytes written.
+ * -errno : An error occured.
+ */
+int nfs_pwrite_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, size_t count, char *buf);
+
+
+/*
+ * WRITE()
+ */
+/*
+ * Async write()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * >=0 : Success.
+ * status is numer of bytes written.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf, nfs_cb cb, void *private_data);
+/*
+ * Sync write()
+ * Function returns
+ * >=0 : numer of bytes written.
+ * -errno : An error occured.
+ */
+int nfs_write_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, size_t count, char *buf);
+
+
+/*
+ * LSEEK()
+ */
+/*
+ * Async lseek()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * >=0 : Success.
+ * data is off_t * for the current position.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, nfs_cb cb, void *private_data);
+/*
+ * Sync lseek()
+ * Function returns
+ * >=0 : numer of bytes read.
+ * -errno : An error occured.
+ */
+int nfs_lseek_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t offset, int whence, off_t *current_offset);
+
+
+/*
+ * FSYNC()
+ */
+/*
+ * Async fsync()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_fsync_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data);
+/*
+ * Sync fsync()
+ * Function returns
+ * 0 : Success
+ * -errno : An error occured.
+ */
+int nfs_fsync_sync(struct nfs_context *nfs, struct nfsfh *nfsfh);
+
+
+
+/*
+ * TRUNCATE()
+ */
+/*
+ * Async truncate()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_truncate_async(struct nfs_context *nfs, const char *path, off_t length, nfs_cb cb, void *private_data);
+/*
+ * Sync truncate()
+ * Function returns
+ * 0 : Success
+ * -errno : An error occured.
+ */
+int nfs_truncate_sync(struct nfs_context *nfs, const char *path, off_t length);
+
+
+
+/*
+ * FTRUNCATE()
+ */
+/*
+ * Async ftruncate()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length, nfs_cb cb, void *private_data);
+/*
+ * Sync ftruncate()
+ * Function returns
+ * 0 : Success
+ * -errno : An error occured.
+ */
+int nfs_ftruncate_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, off_t length);
+
+
+
+
+
+
+/*
+ * MKDIR()
+ */
+/*
+ * Async mkdir()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
+/*
+ * Sync mkdir()
+ * Function returns
+ * 0 : Success
+ * -errno : An error occured.
+ */
+int nfs_mkdir_sync(struct nfs_context *nfs, const char *path);
+
+
+
+/*
+ * RMDIR()
+ */
+/*
+ * Async rmdir()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
+/*
+ * Sync rmdir()
+ * Function returns
+ * 0 : Success
+ * -errno : An error occured.
+ */
+int nfs_rmdir_sync(struct nfs_context *nfs, const char *path);
+
+
+
+
+/*
+ * CREAT()
+ */
+/*
+ * Async creat()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is a struct *nfsfh;
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
+/*
+ * Sync creat()
+ * Function returns
+ * 0 : Success
+ * -errno : An error occured.
+ */
+int nfs_creat_sync(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh);
+
+
+
+
+
+/*
+ * UNLINK()
+ */
+/*
+ * Async unlink()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
+/*
+ * Sync unlink()
+ * Function returns
+ * 0 : Success
+ * -errno : An error occured.
+ */
+int nfs_unlink_sync(struct nfs_context *nfs, const char *path);
+
+
+
+
+/*
+ * OPENDIR()
+ */
+struct nfsdir;
+/*
+ * Async opendir()
+ *
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When struct nfsdir * is returned, this resource is closed/freed by calling nfs_closedir()
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is struct nfsdir *
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_opendir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
+/*
+ * Sync opendir()
+ * Function returns
+ * 0 : Success
+ * -errno : An error occured.
+ */
+int nfs_opendir_sync(struct nfs_context *nfs, const char *path, struct nfsdir **nfsdir);
+
+
+
+/*
+ * READDIR()
+ */
+struct nfsdirent {
+ struct nfsdirent *next;
+ char *name;
+ uint64_t inode;
+};
+/*
+ * nfs_readdir() never blocks, so no special sync/async versions are available
+ */
+struct nfsdirent *nfs_readdir(struct nfs_context *nfs, struct nfsdir *nfsdir);
+
+
+
+/*
+ * READDIR()
+ */
+/*
+ * nfs_closedir() never blocks, so no special sync/async versions are available
+ */
+void nfs_closedir(struct nfs_context *nfs, struct nfsdir *nfsdir);
+
+
+
+/*
+ * STATVFS()
+ */
+/*
+ * Async statvfs(<dirname>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is struct statvfs *
+ * -errno : An error occured.
+ * data is the error string.
+ */
+struct statvfs;
+int nfs_statvfs_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
+/*
+ * Sync statvfs(<dirname>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_statvfs_sync(struct nfs_context *nfs, const char *path, struct statvfs *svfs);
+
+
+/*
+ * READLINK()
+ */
+/*
+ * Async readlink(<name>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is a char *
+ * data is only valid during the callback and is automatically freed when the callback returns.
+ * -errno : An error occured.
+ * data is the error string.
+ */
+struct statvfs;
+int nfs_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data);
+/*
+ * Sync readlink(<name>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_readlink_sync(struct nfs_context *nfs, const char *path, char *buf, int bufsize);
+
+
+
+/*
+ * CHMOD()
+ */
+/*
+ * Async chmod(<name>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_chmod_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
+/*
+ * Sync chmod(<name>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_chmod_sync(struct nfs_context *nfs, const char *path, int mode);
+
+
+
+/*
+ * FCHMOD()
+ */
+/*
+ * Async fchmod(<handle>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs_cb cb, void *private_data);
+/*
+ * Sync fchmod(<handle>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_fchmod_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode);
+
+
+
+/*
+ * CHOWN()
+ */
+/*
+ * Async chown(<name>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_chown_async(struct nfs_context *nfs, const char *path, int uid, int gid, nfs_cb cb, void *private_data);
+/*
+ * Sync chown(<name>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_chown_sync(struct nfs_context *nfs, const char *path, int uid, int gid);
+
+
+
+/*
+ * FCHOWN()
+ */
+/*
+ * Async fchown(<handle>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid, nfs_cb cb, void *private_data);
+/*
+ * Sync fchown(<handle>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_fchown_sync(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int gid);
+
+
+
+
+/*
+ * UTIMES()
+ */
+/*
+ * Async utimes(<path>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_utimes_async(struct nfs_context *nfs, const char *path, struct timeval *times, nfs_cb cb, void *private_data);
+/*
+ * Sync utimes(<path>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_utimes_sync(struct nfs_context *nfs, const char *path, struct timeval *times);
+
+
+/*
+ * UTIME()
+ */
+/*
+ * Async utime(<path>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+struct utimbuf;
+int nfs_utime_async(struct nfs_context *nfs, const char *path, struct utimbuf *times, nfs_cb cb, void *private_data);
+/*
+ * Sync utime(<path>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_utime_sync(struct nfs_context *nfs, const char *path, struct utimbuf *times);
+
+
+
+
+/*
+ * ACCESS()
+ */
+/*
+ * Async access(<path>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_access_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data);
+/*
+ * Sync access(<path>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_access_sync(struct nfs_context *nfs, const char *path, int mode);
+
+
+
+
+/*
+ * SYMLINK()
+ */
+/*
+ * Async symlink(<path>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_symlink_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data);
+/*
+ * Sync symlink(<path>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_symlink_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath);
+
+
+/*
+ * RENAME()
+ */
+/*
+ * Async rename(<oldpath>, <newpath>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_rename_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data);
+/*
+ * Sync rename(<oldpath>, <newpath>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_rename_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath);
+
+
+
+/*
+ * LINK()
+ */
+/*
+ * Async link(<oldpath>, <newpath>)
+ * Function returns
+ * 0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * 0 : Success.
+ * data is NULL
+ * -errno : An error occured.
+ * data is the error string.
+ */
+int nfs_link_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data);
+/*
+ * Sync link(<oldpath>, <newpath>)
+ * Function returns
+ * 0 : The operation was successfull.
+ * -errno : The command failed.
+ */
+int nfs_link_sync(struct nfs_context *nfs, const char *oldpath, const char *newpath);
+
+
+
+//qqq replace later with lseek(cur, 0)
+off_t nfs_get_current_offset(struct nfsfh *nfsfh);
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <errno.h>
+#include <rpc/xdr.h>
+#include "nfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-private.h"
+#include "rpc/nfsacl.h"
+
+
+int rpc_nfsacl_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, NFSACL_PROGRAM, NFSACL_V3, NFSACL3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfsacl/null call");
+ return -1;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfsacl/null call");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <strings.h>
+#include <rpc/xdr.h>
+#include <rpc/rpc_msg.h>
+#include "dlinklist.h"
+#include "nfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-private.h"
+
+struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, xdrproc_t xdr_decode_fn, int xdr_decode_bufsize)
+{
+ struct rpc_pdu *pdu;
+ struct rpc_msg msg;
+
+ if (rpc == NULL) {
+ printf("trying to allocate rpc pdu on NULL context\n");
+ return NULL;
+ }
+
+ pdu = malloc(sizeof(struct rpc_pdu));
+ if (pdu == NULL) {
+ printf("Failed to allocate pdu structure\n");
+ return NULL;
+ }
+ bzero(pdu, sizeof(struct rpc_pdu));
+ pdu->xid = rpc->xid++;
+ pdu->cb = cb;
+ pdu->private_data = private_data;
+ pdu->xdr_decode_fn = xdr_decode_fn;
+ pdu->xdr_decode_bufsize = xdr_decode_bufsize;
+
+ xdrmem_create(&pdu->xdr, rpc->encodebuf, rpc->encodebuflen, XDR_ENCODE);
+ xdr_setpos(&pdu->xdr, 4); /* skip past the record marker */
+
+ bzero(&msg, sizeof(struct rpc_msg));
+ msg.rm_xid = pdu->xid;
+ msg.rm_direction = CALL;
+ msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ msg.rm_call.cb_prog = program;
+ msg.rm_call.cb_vers = version;
+ msg.rm_call.cb_proc = procedure;
+ msg.rm_call.cb_cred = rpc->auth->ah_cred;
+ msg.rm_call.cb_verf = rpc->auth->ah_verf;
+
+ if (xdr_callmsg(&pdu->xdr, &msg) == 0) {
+ printf("xdr_callmsg failed\n");
+ xdr_destroy(&pdu->xdr);
+ free(pdu);
+ return NULL;
+ }
+
+ return pdu;
+}
+
+void rpc_free_pdu(struct rpc_context *rpc _U_, struct rpc_pdu *pdu)
+{
+ if (pdu->outdata.data != NULL) {
+ free(pdu->outdata.data);
+ pdu->outdata.data = NULL;
+ }
+
+ if (pdu->xdr_decode_buf != NULL) {
+ xdr_free(pdu->xdr_decode_fn, pdu->xdr_decode_buf);
+ free(pdu->xdr_decode_buf);
+ pdu->xdr_decode_buf = NULL;
+ }
+
+ xdr_destroy(&pdu->xdr);
+ free(pdu);
+}
+
+
+int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu)
+{
+ int size, recordmarker;
+
+ size = xdr_getpos(&pdu->xdr);
+
+ /* write recordmarker */
+ xdr_setpos(&pdu->xdr, 0);
+ recordmarker = (size - 4) | 0x80000000;
+ xdr_int(&pdu->xdr, &recordmarker);
+
+ pdu->outdata.size = size;
+ pdu->outdata.data = malloc(pdu->outdata.size);
+ if (pdu->outdata.data == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate buffer for pdu\n");
+ rpc_free_pdu(rpc, pdu);
+ return -1;
+ }
+
+ memcpy(pdu->outdata.data, rpc->encodebuf, pdu->outdata.size);
+ DLIST_ADD_END(rpc->outqueue, pdu, NULL);
+
+ return 0;
+}
+
+int rpc_get_pdu_size(char *buf)
+{
+ uint32_t size;
+
+ size = ntohl(*(uint32_t *)buf);
+
+ if ((size & 0x80000000) == 0) {
+ printf("cant handle oncrpc fragments\n");
+ return -1;
+ }
+
+ return (size & 0x7fffffff) + 4;
+}
+
+static int rpc_process_reply(struct rpc_context *rpc, struct rpc_pdu *pdu, XDR *xdr)
+{
+ struct rpc_msg msg;
+
+ bzero(&msg, sizeof(struct rpc_msg));
+ msg.acpted_rply.ar_verf = _null_auth;
+ if (pdu->xdr_decode_bufsize > 0) {
+ pdu->xdr_decode_buf = malloc(pdu->xdr_decode_bufsize);
+ if (pdu->xdr_decode_buf == NULL) {
+ printf("xdr_replymsg failed in portmap_getport_reply\n");
+ pdu->cb(rpc, RPC_STATUS_ERROR, "Failed to allocate buffer for decoding of XDR reply", pdu->private_data);
+ return 0;
+ }
+ bzero(pdu->xdr_decode_buf, pdu->xdr_decode_bufsize);
+ }
+ msg.acpted_rply.ar_results.where = pdu->xdr_decode_buf;
+ msg.acpted_rply.ar_results.proc = pdu->xdr_decode_fn;
+
+ if (xdr_replymsg(xdr, &msg) == 0) {
+ printf("xdr_replymsg failed in portmap_getport_reply\n");
+ pdu->cb(rpc, RPC_STATUS_ERROR, "Message rejected by server", pdu->private_data);
+ if (pdu->xdr_decode_buf != NULL) {
+ free(pdu->xdr_decode_buf);
+ pdu->xdr_decode_buf = NULL;
+ }
+ return 0;
+ }
+ if (msg.rm_reply.rp_stat != MSG_ACCEPTED) {
+ pdu->cb(rpc, RPC_STATUS_ERROR, "RPC Packet not accepted by the server", pdu->private_data);
+ return 0;
+ }
+ switch (msg.rm_reply.rp_acpt.ar_stat) {
+ case SUCCESS:
+ pdu->cb(rpc, RPC_STATUS_SUCCESS, pdu->xdr_decode_buf, pdu->private_data);
+ break;
+ case PROG_UNAVAIL:
+ pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Program not available", pdu->private_data);
+ break;
+ case PROG_MISMATCH:
+ pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Program version mismatch", pdu->private_data);
+ break;
+ case PROC_UNAVAIL:
+ pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Procedure not available", pdu->private_data);
+ break;
+ case GARBAGE_ARGS:
+ pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: Garbage arguments", pdu->private_data);
+ break;
+ case SYSTEM_ERR:
+ pdu->cb(rpc, RPC_STATUS_ERROR, "Server responded: System Error", pdu->private_data);
+ break;
+ default:
+ pdu->cb(rpc, RPC_STATUS_ERROR, "Unknown rpc response from server", pdu->private_data);
+ break;
+ }
+
+ return 0;
+}
+
+int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size)
+{
+ struct rpc_pdu *pdu;
+ XDR xdr;
+ int pos, recordmarker;
+ unsigned int xid;
+
+ bzero(&xdr, sizeof(XDR));
+
+ xdrmem_create(&xdr, buf, size, XDR_DECODE);
+ if (xdr_int(&xdr, &recordmarker) == 0) {
+ printf("xdr_int reading recordmarker failed\n");
+ xdr_destroy(&xdr);
+ return -1;
+ }
+ pos = xdr_getpos(&xdr);
+ if (xdr_int(&xdr, (int *)&xid) == 0) {
+ printf("xdr_int reading xid failed\n");
+ xdr_destroy(&xdr);
+ return -1;
+ }
+ xdr_setpos(&xdr, pos);
+
+ for (pdu=rpc->waitpdu; pdu; pdu=pdu->next) {
+ if (pdu->xid != xid) {
+ continue;
+ }
+ DLIST_REMOVE(rpc->waitpdu, pdu);
+ if (rpc_process_reply(rpc, pdu, &xdr) != 0) {
+ printf("rpc_procdess_reply failed\n");
+ }
+ xdr_destroy(&xdr);
+ rpc_free_pdu(rpc, pdu);
+ return 0;
+ }
+ printf("No matching pdu found for xid:%d\n", xid);
+ xdr_destroy(&xdr);
+ return -1;
+}
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <rpc/xdr.h>
+#include "nfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-private.h"
+#include "rpc/portmap.h"
+
+
+int rpc_pmap_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+
+ pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for portmap/null call");
+ return -1;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Out of memory. Failed to queue pdu for portmap/null call");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
+
+int rpc_pmap_getport_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ struct mapping m;
+
+ pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_GETPORT, cb, private_data, (xdrproc_t)xdr_int, sizeof(uint32_t));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for portmap/getport call");
+ return -1;
+ }
+
+ m.prog = program;
+ m.vers = version;
+ m.prot = IPPROTO_TCP;
+ m.port = 0;
+ if (xdr_mapping(&pdu->xdr, &m) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode data for portmap/getport call");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ printf("Failed to queue portmap/getport pdu\n");
+ rpc_free_pdu(rpc, pdu);
+ return -2;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _MOUNT_H_RPCGEN
+#define _MOUNT_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+#define FHSIZE3 64
+
+typedef struct {
+ u_int fhandle3_len;
+ char *fhandle3_val;
+} fhandle3;
+
+typedef char *dirpath;
+
+typedef char *name;
+
+enum mountstat3 {
+ MNT3_OK = 0,
+ MNT3ERR_PERM = 1,
+ MNT3ERR_NOENT = 2,
+ MNT3ERR_IO = 5,
+ MNT3ERR_ACCES = 13,
+ MNT3ERR_NOTDIR = 20,
+ MNT3ERR_INVAL = 22,
+ MNT3ERR_NAMETOOLONG = 63,
+ MNT3ERR_NOTSUPP = 10004,
+ MNT3ERR_SERVERFAULT = 10006,
+};
+typedef enum mountstat3 mountstat3;
+
+typedef struct mountbody *mountlist;
+
+struct mountbody {
+ name ml_hostname;
+ dirpath ml_directory;
+ mountlist ml_next;
+};
+typedef struct mountbody mountbody;
+
+typedef struct groupnode *groups;
+
+struct groupnode {
+ name gr_name;
+ groups gr_next;
+};
+typedef struct groupnode groupnode;
+
+typedef struct exportnode *exports;
+
+struct exportnode {
+ dirpath ex_dir;
+ groups ex_groups;
+ exports ex_next;
+};
+typedef struct exportnode exportnode;
+
+struct mountres3_ok {
+ fhandle3 fhandle;
+ struct {
+ u_int auth_flavors_len;
+ int *auth_flavors_val;
+ } auth_flavors;
+};
+typedef struct mountres3_ok mountres3_ok;
+
+struct mountres3 {
+ mountstat3 fhs_status;
+ union {
+ mountres3_ok mountinfo;
+ } mountres3_u;
+};
+typedef struct mountres3 mountres3;
+
+#define MOUNT_PROGRAM 100005
+#define MOUNT_V3 3
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define MOUNT3_NULL 0
+extern void * mount3_null_3(void *, CLIENT *);
+extern void * mount3_null_3_svc(void *, struct svc_req *);
+#define MOUNT3_MNT 1
+extern mountres3 * mount3_mnt_3(dirpath *, CLIENT *);
+extern mountres3 * mount3_mnt_3_svc(dirpath *, struct svc_req *);
+#define MOUNT3_DUMP 2
+extern mountlist * mount3_dump_3(void *, CLIENT *);
+extern mountlist * mount3_dump_3_svc(void *, struct svc_req *);
+#define MOUNT3_UMNT 3
+extern void * mount3_umnt_3(dirpath *, CLIENT *);
+extern void * mount3_umnt_3_svc(dirpath *, struct svc_req *);
+#define MOUNT3_UMNTALL 4
+extern void * mount3_umntall_3(void *, CLIENT *);
+extern void * mount3_umntall_3_svc(void *, struct svc_req *);
+#define MOUNT3_EXPORT 5
+extern exports * mount3_export_3(void *, CLIENT *);
+extern exports * mount3_export_3_svc(void *, struct svc_req *);
+extern int mount_program_3_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define MOUNT3_NULL 0
+extern void * mount3_null_3();
+extern void * mount3_null_3_svc();
+#define MOUNT3_MNT 1
+extern mountres3 * mount3_mnt_3();
+extern mountres3 * mount3_mnt_3_svc();
+#define MOUNT3_DUMP 2
+extern mountlist * mount3_dump_3();
+extern mountlist * mount3_dump_3_svc();
+#define MOUNT3_UMNT 3
+extern void * mount3_umnt_3();
+extern void * mount3_umnt_3_svc();
+#define MOUNT3_UMNTALL 4
+extern void * mount3_umntall_3();
+extern void * mount3_umntall_3_svc();
+#define MOUNT3_EXPORT 5
+extern exports * mount3_export_3();
+extern exports * mount3_export_3_svc();
+extern int mount_program_3_freeresult ();
+#endif /* K&R C */
+
+/* the xdr functions */
+
+#if defined(__STDC__) || defined(__cplusplus)
+extern bool_t xdr_fhandle3 (XDR *, fhandle3*);
+extern bool_t xdr_dirpath (XDR *, dirpath*);
+extern bool_t xdr_name (XDR *, name*);
+extern bool_t xdr_mountstat3 (XDR *, mountstat3*);
+extern bool_t xdr_mountlist (XDR *, mountlist*);
+extern bool_t xdr_mountbody (XDR *, mountbody*);
+extern bool_t xdr_groups (XDR *, groups*);
+extern bool_t xdr_groupnode (XDR *, groupnode*);
+extern bool_t xdr_exports (XDR *, exports*);
+extern bool_t xdr_exportnode (XDR *, exportnode*);
+extern bool_t xdr_mountres3_ok (XDR *, mountres3_ok*);
+extern bool_t xdr_mountres3 (XDR *, mountres3*);
+
+#else /* K&R C */
+extern bool_t xdr_fhandle3 ();
+extern bool_t xdr_dirpath ();
+extern bool_t xdr_name ();
+extern bool_t xdr_mountstat3 ();
+extern bool_t xdr_mountlist ();
+extern bool_t xdr_mountbody ();
+extern bool_t xdr_groups ();
+extern bool_t xdr_groupnode ();
+extern bool_t xdr_exports ();
+extern bool_t xdr_exportnode ();
+extern bool_t xdr_mountres3_ok ();
+extern bool_t xdr_mountres3 ();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_MOUNT_H_RPCGEN */
--- /dev/null
+/* copied from RFC1813 */
+
+const MNTPATHLEN = 1024; /* Maximum bytes in a path name */
+const MNTNAMLEN = 255; /* Maximum bytes in a name */
+const FHSIZE3 = 64; /* Maximum bytes in a V3 file handle */
+
+
+typedef opaque fhandle3<FHSIZE3>;
+typedef string dirpath<MNTPATHLEN>;
+typedef string name<MNTNAMLEN>;
+
+enum mountstat3 {
+ MNT3_OK = 0, /* no error */
+ MNT3ERR_PERM = 1, /* Not owner */
+ MNT3ERR_NOENT = 2, /* No such file or directory */
+ MNT3ERR_IO = 5, /* I/O error */
+ MNT3ERR_ACCES = 13, /* Permission denied */
+ MNT3ERR_NOTDIR = 20, /* Not a directory */
+ MNT3ERR_INVAL = 22, /* Invalid argument */
+ MNT3ERR_NAMETOOLONG = 63, /* Filename too long */
+ MNT3ERR_NOTSUPP = 10004, /* Operation not supported */
+ MNT3ERR_SERVERFAULT = 10006 /* A failure on the server */
+};
+
+
+typedef struct mountbody *mountlist;
+
+struct mountbody {
+ name ml_hostname;
+ dirpath ml_directory;
+ mountlist ml_next;
+};
+
+typedef struct groupnode *groups;
+
+struct groupnode {
+ name gr_name;
+ groups gr_next;
+};
+
+
+typedef struct exportnode *exports;
+
+struct exportnode {
+ dirpath ex_dir;
+ groups ex_groups;
+ exports ex_next;
+};
+
+struct mountres3_ok {
+ fhandle3 fhandle;
+ int auth_flavors<>;
+};
+
+union mountres3 switch (mountstat3 fhs_status) {
+ case MNT3_OK:
+ mountres3_ok mountinfo;
+ default:
+ void;
+};
+
+program MOUNT_PROGRAM {
+ version MOUNT_V3 {
+ void MOUNT3_NULL(void) = 0;
+ mountres3 MOUNT3_MNT(dirpath) = 1;
+ mountlist MOUNT3_DUMP(void) = 2;
+ void MOUNT3_UMNT(dirpath) = 3;
+ void MOUNT3_UMNTALL(void) = 4;
+ exports MOUNT3_EXPORT(void) = 5;
+ } = 3;
+} = 100005;
--- /dev/null
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _NFS_H_RPCGEN
+#define _NFS_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NFS3_FHSIZE 64
+#define NFS3_WRITEVERFSIZE 8
+#define NFS3_CREATEVERFSIZE 8
+#define NFS3_COOKIEVERFSIZE 8
+
+typedef char cookieverf3[NFS3_COOKIEVERFSIZE];
+
+typedef u_quad_t uint64;
+
+typedef uint64 cookie3;
+
+struct nfs_fh3 {
+ struct {
+ u_int data_len;
+ char *data_val;
+ } data;
+};
+typedef struct nfs_fh3 nfs_fh3;
+
+typedef char *filename3;
+
+struct diropargs3 {
+ nfs_fh3 dir;
+ filename3 name;
+};
+typedef struct diropargs3 diropargs3;
+
+enum ftype3 {
+ NF3REG = 1,
+ NF3DIR = 2,
+ NF3BLK = 3,
+ NF3CHR = 4,
+ NF3LNK = 5,
+ NF3SOCK = 6,
+ NF3FIFO = 7,
+};
+typedef enum ftype3 ftype3;
+
+typedef u_long uint32;
+
+typedef long int32;
+
+typedef uint32 mode3;
+
+typedef uint32 uid3;
+
+typedef uint32 gid3;
+
+typedef uint64 size3;
+
+typedef uint64 fileid3;
+
+struct specdata3 {
+ uint32 specdata1;
+ uint32 specdata2;
+};
+typedef struct specdata3 specdata3;
+
+struct nfstime3 {
+ uint32 seconds;
+ uint32 nseconds;
+};
+typedef struct nfstime3 nfstime3;
+
+struct fattr3 {
+ ftype3 type;
+ mode3 mode;
+ uint32 nlink;
+ uid3 uid;
+ gid3 gid;
+ size3 size;
+ size3 used;
+ specdata3 rdev;
+ uint64 fsid;
+ fileid3 fileid;
+ nfstime3 atime;
+ nfstime3 mtime;
+ nfstime3 ctime;
+};
+typedef struct fattr3 fattr3;
+
+struct post_op_attr {
+ bool_t attributes_follow;
+ union {
+ fattr3 attributes;
+ } post_op_attr_u;
+};
+typedef struct post_op_attr post_op_attr;
+
+enum nfsstat3 {
+ NFS3_OK = 0,
+ NFS3ERR_PERM = 1,
+ NFS3ERR_NOENT = 2,
+ NFS3ERR_IO = 5,
+ NFS3ERR_NXIO = 6,
+ NFS3ERR_ACCES = 13,
+ NFS3ERR_EXIST = 17,
+ NFS3ERR_XDEV = 18,
+ NFS3ERR_NODEV = 19,
+ NFS3ERR_NOTDIR = 20,
+ NFS3ERR_ISDIR = 21,
+ NFS3ERR_INVAL = 22,
+ NFS3ERR_FBIG = 27,
+ NFS3ERR_NOSPC = 28,
+ NFS3ERR_ROFS = 30,
+ NFS3ERR_MLINK = 31,
+ NFS3ERR_NAMETOOLONG = 63,
+ NFS3ERR_NOTEMPTY = 66,
+ NFS3ERR_DQUOT = 69,
+ NFS3ERR_STALE = 70,
+ NFS3ERR_REMOTE = 71,
+ NFS3ERR_BADHANDLE = 10001,
+ NFS3ERR_NOT_SYNC = 10002,
+ NFS3ERR_BAD_COOKIE = 10003,
+ NFS3ERR_NOTSUPP = 10004,
+ NFS3ERR_TOOSMALL = 10005,
+ NFS3ERR_SERVERFAULT = 10006,
+ NFS3ERR_BADTYPE = 10007,
+ NFS3ERR_JUKEBOX = 10008,
+};
+typedef enum nfsstat3 nfsstat3;
+
+enum stable_how {
+ UNSTABLE = 0,
+ DATA_SYNC = 1,
+ FILE_SYNC = 2,
+};
+typedef enum stable_how stable_how;
+
+typedef uint64 offset3;
+
+typedef uint32 count3;
+
+struct wcc_attr {
+ size3 size;
+ nfstime3 mtime;
+ nfstime3 ctime;
+};
+typedef struct wcc_attr wcc_attr;
+
+struct pre_op_attr {
+ bool_t attributes_follow;
+ union {
+ wcc_attr attributes;
+ } pre_op_attr_u;
+};
+typedef struct pre_op_attr pre_op_attr;
+
+struct wcc_data {
+ pre_op_attr before;
+ post_op_attr after;
+};
+typedef struct wcc_data wcc_data;
+
+struct WRITE3args {
+ nfs_fh3 file;
+ offset3 offset;
+ count3 count;
+ stable_how stable;
+ struct {
+ u_int data_len;
+ char *data_val;
+ } data;
+};
+typedef struct WRITE3args WRITE3args;
+
+typedef char writeverf3[NFS3_WRITEVERFSIZE];
+
+struct WRITE3resok {
+ wcc_data file_wcc;
+ count3 count;
+ stable_how committed;
+ writeverf3 verf;
+};
+typedef struct WRITE3resok WRITE3resok;
+
+struct WRITE3resfail {
+ wcc_data file_wcc;
+};
+typedef struct WRITE3resfail WRITE3resfail;
+
+struct WRITE3res {
+ nfsstat3 status;
+ union {
+ WRITE3resok resok;
+ WRITE3resfail resfail;
+ } WRITE3res_u;
+};
+typedef struct WRITE3res WRITE3res;
+
+struct LOOKUP3args {
+ diropargs3 what;
+};
+typedef struct LOOKUP3args LOOKUP3args;
+
+struct LOOKUP3resok {
+ nfs_fh3 object;
+ post_op_attr obj_attributes;
+ post_op_attr dir_attributes;
+};
+typedef struct LOOKUP3resok LOOKUP3resok;
+
+struct LOOKUP3resfail {
+ post_op_attr dir_attributes;
+};
+typedef struct LOOKUP3resfail LOOKUP3resfail;
+
+struct LOOKUP3res {
+ nfsstat3 status;
+ union {
+ LOOKUP3resok resok;
+ LOOKUP3resfail resfail;
+ } LOOKUP3res_u;
+};
+typedef struct LOOKUP3res LOOKUP3res;
+
+struct COMMIT3args {
+ nfs_fh3 file;
+ offset3 offset;
+ count3 count;
+};
+typedef struct COMMIT3args COMMIT3args;
+
+struct COMMIT3resok {
+ wcc_data file_wcc;
+ writeverf3 verf;
+};
+typedef struct COMMIT3resok COMMIT3resok;
+
+struct COMMIT3resfail {
+ wcc_data file_wcc;
+};
+typedef struct COMMIT3resfail COMMIT3resfail;
+
+struct COMMIT3res {
+ nfsstat3 status;
+ union {
+ COMMIT3resok resok;
+ COMMIT3resfail resfail;
+ } COMMIT3res_u;
+};
+typedef struct COMMIT3res COMMIT3res;
+#define ACCESS3_READ 0x0001
+#define ACCESS3_LOOKUP 0x0002
+#define ACCESS3_MODIFY 0x0004
+#define ACCESS3_EXTEND 0x0008
+#define ACCESS3_DELETE 0x0010
+#define ACCESS3_EXECUTE 0x0020
+
+struct ACCESS3args {
+ nfs_fh3 object;
+ uint32 access;
+};
+typedef struct ACCESS3args ACCESS3args;
+
+struct ACCESS3resok {
+ post_op_attr obj_attributes;
+ uint32 access;
+};
+typedef struct ACCESS3resok ACCESS3resok;
+
+struct ACCESS3resfail {
+ post_op_attr obj_attributes;
+};
+typedef struct ACCESS3resfail ACCESS3resfail;
+
+struct ACCESS3res {
+ nfsstat3 status;
+ union {
+ ACCESS3resok resok;
+ ACCESS3resfail resfail;
+ } ACCESS3res_u;
+};
+typedef struct ACCESS3res ACCESS3res;
+
+struct GETATTR3args {
+ nfs_fh3 object;
+};
+typedef struct GETATTR3args GETATTR3args;
+
+struct GETATTR3resok {
+ fattr3 obj_attributes;
+};
+typedef struct GETATTR3resok GETATTR3resok;
+
+struct GETATTR3res {
+ nfsstat3 status;
+ union {
+ GETATTR3resok resok;
+ } GETATTR3res_u;
+};
+typedef struct GETATTR3res GETATTR3res;
+
+enum time_how {
+ DONT_CHANGE = 0,
+ SET_TO_SERVER_TIME = 1,
+ SET_TO_CLIENT_TIME = 2,
+};
+typedef enum time_how time_how;
+
+struct set_mode3 {
+ bool_t set_it;
+ union {
+ mode3 mode;
+ } set_mode3_u;
+};
+typedef struct set_mode3 set_mode3;
+
+struct set_uid3 {
+ bool_t set_it;
+ union {
+ uid3 uid;
+ } set_uid3_u;
+};
+typedef struct set_uid3 set_uid3;
+
+struct set_gid3 {
+ bool_t set_it;
+ union {
+ gid3 gid;
+ } set_gid3_u;
+};
+typedef struct set_gid3 set_gid3;
+
+struct set_size3 {
+ bool_t set_it;
+ union {
+ size3 size;
+ } set_size3_u;
+};
+typedef struct set_size3 set_size3;
+
+struct set_atime {
+ time_how set_it;
+ union {
+ nfstime3 atime;
+ } set_atime_u;
+};
+typedef struct set_atime set_atime;
+
+struct set_mtime {
+ time_how set_it;
+ union {
+ nfstime3 mtime;
+ } set_mtime_u;
+};
+typedef struct set_mtime set_mtime;
+
+struct sattr3 {
+ set_mode3 mode;
+ set_uid3 uid;
+ set_gid3 gid;
+ set_size3 size;
+ set_atime atime;
+ set_mtime mtime;
+};
+typedef struct sattr3 sattr3;
+
+enum createmode3 {
+ UNCHECKED = 0,
+ GUARDED = 1,
+ EXCLUSIVE = 2,
+};
+typedef enum createmode3 createmode3;
+
+typedef char createverf3[NFS3_CREATEVERFSIZE];
+
+struct createhow3 {
+ createmode3 mode;
+ union {
+ sattr3 obj_attributes;
+ createverf3 verf;
+ } createhow3_u;
+};
+typedef struct createhow3 createhow3;
+
+struct CREATE3args {
+ diropargs3 where;
+ createhow3 how;
+};
+typedef struct CREATE3args CREATE3args;
+
+struct post_op_fh3 {
+ bool_t handle_follows;
+ union {
+ nfs_fh3 handle;
+ } post_op_fh3_u;
+};
+typedef struct post_op_fh3 post_op_fh3;
+
+struct CREATE3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+typedef struct CREATE3resok CREATE3resok;
+
+struct CREATE3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct CREATE3resfail CREATE3resfail;
+
+struct CREATE3res {
+ nfsstat3 status;
+ union {
+ CREATE3resok resok;
+ CREATE3resfail resfail;
+ } CREATE3res_u;
+};
+typedef struct CREATE3res CREATE3res;
+
+struct REMOVE3args {
+ diropargs3 object;
+};
+typedef struct REMOVE3args REMOVE3args;
+
+struct REMOVE3resok {
+ wcc_data dir_wcc;
+};
+typedef struct REMOVE3resok REMOVE3resok;
+
+struct REMOVE3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct REMOVE3resfail REMOVE3resfail;
+
+struct REMOVE3res {
+ nfsstat3 status;
+ union {
+ REMOVE3resok resok;
+ REMOVE3resfail resfail;
+ } REMOVE3res_u;
+};
+typedef struct REMOVE3res REMOVE3res;
+
+struct READ3args {
+ nfs_fh3 file;
+ offset3 offset;
+ count3 count;
+};
+typedef struct READ3args READ3args;
+
+struct READ3resok {
+ post_op_attr file_attributes;
+ count3 count;
+ bool_t eof;
+ struct {
+ u_int data_len;
+ char *data_val;
+ } data;
+};
+typedef struct READ3resok READ3resok;
+
+struct READ3resfail {
+ post_op_attr file_attributes;
+};
+typedef struct READ3resfail READ3resfail;
+
+struct READ3res {
+ nfsstat3 status;
+ union {
+ READ3resok resok;
+ READ3resfail resfail;
+ } READ3res_u;
+};
+typedef struct READ3res READ3res;
+#define FSF3_LINK 0x0001
+#define FSF3_SYMLINK 0x0002
+#define FSF3_HOMOGENEOUS 0x0008
+#define FSF3_CANSETTIME 0x0010
+
+struct FSINFO3args {
+ nfs_fh3 fsroot;
+};
+typedef struct FSINFO3args FSINFO3args;
+
+struct FSINFO3resok {
+ post_op_attr obj_attributes;
+ uint32 rtmax;
+ uint32 rtpref;
+ uint32 rtmult;
+ uint32 wtmax;
+ uint32 wtpref;
+ uint32 wtmult;
+ uint32 dtpref;
+ size3 maxfilesize;
+ nfstime3 time_delta;
+ uint32 properties;
+};
+typedef struct FSINFO3resok FSINFO3resok;
+
+struct FSINFO3resfail {
+ post_op_attr obj_attributes;
+};
+typedef struct FSINFO3resfail FSINFO3resfail;
+
+struct FSINFO3res {
+ nfsstat3 status;
+ union {
+ FSINFO3resok resok;
+ FSINFO3resfail resfail;
+ } FSINFO3res_u;
+};
+typedef struct FSINFO3res FSINFO3res;
+
+struct FSSTAT3args {
+ nfs_fh3 fsroot;
+};
+typedef struct FSSTAT3args FSSTAT3args;
+
+struct FSSTAT3resok {
+ post_op_attr obj_attributes;
+ size3 tbytes;
+ size3 fbytes;
+ size3 abytes;
+ size3 tfiles;
+ size3 ffiles;
+ size3 afiles;
+ uint32 invarsec;
+};
+typedef struct FSSTAT3resok FSSTAT3resok;
+
+struct FSSTAT3resfail {
+ post_op_attr obj_attributes;
+};
+typedef struct FSSTAT3resfail FSSTAT3resfail;
+
+struct FSSTAT3res {
+ nfsstat3 status;
+ union {
+ FSSTAT3resok resok;
+ FSSTAT3resfail resfail;
+ } FSSTAT3res_u;
+};
+typedef struct FSSTAT3res FSSTAT3res;
+
+struct PATHCONF3args {
+ nfs_fh3 object;
+};
+typedef struct PATHCONF3args PATHCONF3args;
+
+struct PATHCONF3resok {
+ post_op_attr obj_attributes;
+ uint32 linkmax;
+ uint32 name_max;
+ bool_t no_trunc;
+ bool_t chown_restricted;
+ bool_t case_insensitive;
+ bool_t case_preserving;
+};
+typedef struct PATHCONF3resok PATHCONF3resok;
+
+struct PATHCONF3resfail {
+ post_op_attr obj_attributes;
+};
+typedef struct PATHCONF3resfail PATHCONF3resfail;
+
+struct PATHCONF3res {
+ nfsstat3 status;
+ union {
+ PATHCONF3resok resok;
+ PATHCONF3resfail resfail;
+ } PATHCONF3res_u;
+};
+typedef struct PATHCONF3res PATHCONF3res;
+
+typedef char *nfspath3;
+
+struct symlinkdata3 {
+ sattr3 symlink_attributes;
+ nfspath3 symlink_data;
+};
+typedef struct symlinkdata3 symlinkdata3;
+
+struct SYMLINK3args {
+ diropargs3 where;
+ symlinkdata3 symlink;
+};
+typedef struct SYMLINK3args SYMLINK3args;
+
+struct SYMLINK3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+typedef struct SYMLINK3resok SYMLINK3resok;
+
+struct SYMLINK3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct SYMLINK3resfail SYMLINK3resfail;
+
+struct SYMLINK3res {
+ nfsstat3 status;
+ union {
+ SYMLINK3resok resok;
+ SYMLINK3resfail resfail;
+ } SYMLINK3res_u;
+};
+typedef struct SYMLINK3res SYMLINK3res;
+
+struct READLINK3args {
+ nfs_fh3 symlink;
+};
+typedef struct READLINK3args READLINK3args;
+
+struct READLINK3resok {
+ post_op_attr symlink_attributes;
+ nfspath3 data;
+};
+typedef struct READLINK3resok READLINK3resok;
+
+struct READLINK3resfail {
+ post_op_attr symlink_attributes;
+};
+typedef struct READLINK3resfail READLINK3resfail;
+
+struct READLINK3res {
+ nfsstat3 status;
+ union {
+ READLINK3resok resok;
+ READLINK3resfail resfail;
+ } READLINK3res_u;
+};
+typedef struct READLINK3res READLINK3res;
+
+struct devicedata3 {
+ sattr3 dev_attributes;
+ specdata3 spec;
+};
+typedef struct devicedata3 devicedata3;
+
+struct mknoddata3 {
+ ftype3 type;
+ union {
+ devicedata3 device;
+ sattr3 pipe_attributes;
+ } mknoddata3_u;
+};
+typedef struct mknoddata3 mknoddata3;
+
+struct MKNOD3args {
+ diropargs3 where;
+ mknoddata3 what;
+};
+typedef struct MKNOD3args MKNOD3args;
+
+struct MKNOD3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+typedef struct MKNOD3resok MKNOD3resok;
+
+struct MKNOD3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct MKNOD3resfail MKNOD3resfail;
+
+struct MKNOD3res {
+ nfsstat3 status;
+ union {
+ MKNOD3resok resok;
+ MKNOD3resfail resfail;
+ } MKNOD3res_u;
+};
+typedef struct MKNOD3res MKNOD3res;
+
+struct MKDIR3args {
+ diropargs3 where;
+ sattr3 attributes;
+};
+typedef struct MKDIR3args MKDIR3args;
+
+struct MKDIR3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+typedef struct MKDIR3resok MKDIR3resok;
+
+struct MKDIR3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct MKDIR3resfail MKDIR3resfail;
+
+struct MKDIR3res {
+ nfsstat3 status;
+ union {
+ MKDIR3resok resok;
+ MKDIR3resfail resfail;
+ } MKDIR3res_u;
+};
+typedef struct MKDIR3res MKDIR3res;
+
+struct RMDIR3args {
+ diropargs3 object;
+};
+typedef struct RMDIR3args RMDIR3args;
+
+struct RMDIR3resok {
+ wcc_data dir_wcc;
+};
+typedef struct RMDIR3resok RMDIR3resok;
+
+struct RMDIR3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct RMDIR3resfail RMDIR3resfail;
+
+struct RMDIR3res {
+ nfsstat3 status;
+ union {
+ RMDIR3resok resok;
+ RMDIR3resfail resfail;
+ } RMDIR3res_u;
+};
+typedef struct RMDIR3res RMDIR3res;
+
+struct RENAME3args {
+ diropargs3 from;
+ diropargs3 to;
+};
+typedef struct RENAME3args RENAME3args;
+
+struct RENAME3resok {
+ wcc_data fromdir_wcc;
+ wcc_data todir_wcc;
+};
+typedef struct RENAME3resok RENAME3resok;
+
+struct RENAME3resfail {
+ wcc_data fromdir_wcc;
+ wcc_data todir_wcc;
+};
+typedef struct RENAME3resfail RENAME3resfail;
+
+struct RENAME3res {
+ nfsstat3 status;
+ union {
+ RENAME3resok resok;
+ RENAME3resfail resfail;
+ } RENAME3res_u;
+};
+typedef struct RENAME3res RENAME3res;
+
+struct READDIRPLUS3args {
+ nfs_fh3 dir;
+ cookie3 cookie;
+ cookieverf3 cookieverf;
+ count3 dircount;
+ count3 maxcount;
+};
+typedef struct READDIRPLUS3args READDIRPLUS3args;
+
+struct entryplus3 {
+ fileid3 fileid;
+ filename3 name;
+ cookie3 cookie;
+ post_op_attr name_attributes;
+ post_op_fh3 name_handle;
+ struct entryplus3 *nextentry;
+};
+typedef struct entryplus3 entryplus3;
+
+struct dirlistplus3 {
+ entryplus3 *entries;
+ bool_t eof;
+};
+typedef struct dirlistplus3 dirlistplus3;
+
+struct READDIRPLUS3resok {
+ post_op_attr dir_attributes;
+ cookieverf3 cookieverf;
+ dirlistplus3 reply;
+};
+typedef struct READDIRPLUS3resok READDIRPLUS3resok;
+
+struct READDIRPLUS3resfail {
+ post_op_attr dir_attributes;
+};
+typedef struct READDIRPLUS3resfail READDIRPLUS3resfail;
+
+struct READDIRPLUS3res {
+ nfsstat3 status;
+ union {
+ READDIRPLUS3resok resok;
+ READDIRPLUS3resfail resfail;
+ } READDIRPLUS3res_u;
+};
+typedef struct READDIRPLUS3res READDIRPLUS3res;
+
+struct READDIR3args {
+ nfs_fh3 dir;
+ cookie3 cookie;
+ cookieverf3 cookieverf;
+ count3 count;
+};
+typedef struct READDIR3args READDIR3args;
+
+struct entry3 {
+ fileid3 fileid;
+ filename3 name;
+ cookie3 cookie;
+ struct entry3 *nextentry;
+};
+typedef struct entry3 entry3;
+
+struct dirlist3 {
+ entry3 *entries;
+ bool_t eof;
+};
+typedef struct dirlist3 dirlist3;
+
+struct READDIR3resok {
+ post_op_attr dir_attributes;
+ cookieverf3 cookieverf;
+ dirlist3 reply;
+};
+typedef struct READDIR3resok READDIR3resok;
+
+struct READDIR3resfail {
+ post_op_attr dir_attributes;
+};
+typedef struct READDIR3resfail READDIR3resfail;
+
+struct READDIR3res {
+ nfsstat3 status;
+ union {
+ READDIR3resok resok;
+ READDIR3resfail resfail;
+ } READDIR3res_u;
+};
+typedef struct READDIR3res READDIR3res;
+
+struct LINK3args {
+ nfs_fh3 file;
+ diropargs3 link;
+};
+typedef struct LINK3args LINK3args;
+
+struct LINK3resok {
+ post_op_attr file_attributes;
+ wcc_data linkdir_wcc;
+};
+typedef struct LINK3resok LINK3resok;
+
+struct LINK3resfail {
+ post_op_attr file_attributes;
+ wcc_data linkdir_wcc;
+};
+typedef struct LINK3resfail LINK3resfail;
+
+struct LINK3res {
+ nfsstat3 status;
+ union {
+ LINK3resok resok;
+ LINK3resfail resfail;
+ } LINK3res_u;
+};
+typedef struct LINK3res LINK3res;
+
+struct sattrguard3 {
+ bool_t check;
+ union {
+ nfstime3 obj_ctime;
+ } sattrguard3_u;
+};
+typedef struct sattrguard3 sattrguard3;
+
+struct SETATTR3args {
+ nfs_fh3 object;
+ sattr3 new_attributes;
+ sattrguard3 guard;
+};
+typedef struct SETATTR3args SETATTR3args;
+
+struct SETATTR3resok {
+ wcc_data obj_wcc;
+};
+typedef struct SETATTR3resok SETATTR3resok;
+
+struct SETATTR3resfail {
+ wcc_data obj_wcc;
+};
+typedef struct SETATTR3resfail SETATTR3resfail;
+
+struct SETATTR3res {
+ nfsstat3 status;
+ union {
+ SETATTR3resok resok;
+ SETATTR3resfail resfail;
+ } SETATTR3res_u;
+};
+typedef struct SETATTR3res SETATTR3res;
+
+#define NFS_PROGRAM 100003
+#define NFS_V3 3
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define NFS3_NULL 0
+extern void * nfs3_null_3(void *, CLIENT *);
+extern void * nfs3_null_3_svc(void *, struct svc_req *);
+#define NFS3_GETATTR 1
+extern GETATTR3res * nfs3_getattr_3(GETATTR3args *, CLIENT *);
+extern GETATTR3res * nfs3_getattr_3_svc(GETATTR3args *, struct svc_req *);
+#define NFS3_SETATTR 2
+extern SETATTR3res * nfs3_setattr_3(SETATTR3args *, CLIENT *);
+extern SETATTR3res * nfs3_setattr_3_svc(SETATTR3args *, struct svc_req *);
+#define NFS3_LOOKUP 3
+extern LOOKUP3res * nfs3_lookup_3(LOOKUP3args *, CLIENT *);
+extern LOOKUP3res * nfs3_lookup_3_svc(LOOKUP3args *, struct svc_req *);
+#define NFS3_ACCESS 4
+extern ACCESS3res * nfs3_access_3(ACCESS3args *, CLIENT *);
+extern ACCESS3res * nfs3_access_3_svc(ACCESS3args *, struct svc_req *);
+#define NFS3_READLINK 5
+extern READLINK3res * nfs3_readlink_3(READLINK3args *, CLIENT *);
+extern READLINK3res * nfs3_readlink_3_svc(READLINK3args *, struct svc_req *);
+#define NFS3_READ 6
+extern READ3res * nfs3_read_3(READ3args *, CLIENT *);
+extern READ3res * nfs3_read_3_svc(READ3args *, struct svc_req *);
+#define NFS3_WRITE 7
+extern WRITE3res * nfs3_write_3(WRITE3args *, CLIENT *);
+extern WRITE3res * nfs3_write_3_svc(WRITE3args *, struct svc_req *);
+#define NFS3_CREATE 8
+extern CREATE3res * nfs3_create_3(CREATE3args *, CLIENT *);
+extern CREATE3res * nfs3_create_3_svc(CREATE3args *, struct svc_req *);
+#define NFS3_MKDIR 9
+extern MKDIR3res * nfs3_mkdir_3(MKDIR3args *, CLIENT *);
+extern MKDIR3res * nfs3_mkdir_3_svc(MKDIR3args *, struct svc_req *);
+#define NFS3_SYMLINK 10
+extern SYMLINK3res * nfs3_symlink_3(SYMLINK3args *, CLIENT *);
+extern SYMLINK3res * nfs3_symlink_3_svc(SYMLINK3args *, struct svc_req *);
+#define NFS3_REMOVE 12
+extern REMOVE3res * nfs3_remove_3(REMOVE3args *, CLIENT *);
+extern REMOVE3res * nfs3_remove_3_svc(REMOVE3args *, struct svc_req *);
+#define NFS3_RMDIR 13
+extern RMDIR3res * nfs3_rmdir_3(RMDIR3args *, CLIENT *);
+extern RMDIR3res * nfs3_rmdir_3_svc(RMDIR3args *, struct svc_req *);
+#define NFS3_RENAME 14
+extern RENAME3res * nfs3_rename_3(RENAME3args *, CLIENT *);
+extern RENAME3res * nfs3_rename_3_svc(RENAME3args *, struct svc_req *);
+#define NFS3_LINK 15
+extern LINK3res * nfs3_link_3(LINK3args *, CLIENT *);
+extern LINK3res * nfs3_link_3_svc(LINK3args *, struct svc_req *);
+#define NFS3_READDIR 16
+extern READDIR3res * nfs3_readdir_3(READDIR3args *, CLIENT *);
+extern READDIR3res * nfs3_readdir_3_svc(READDIR3args *, struct svc_req *);
+#define NFS3_READDIRPLUS 17
+extern READDIRPLUS3res * nfs3_readdirplus_3(READDIRPLUS3args *, CLIENT *);
+extern READDIRPLUS3res * nfs3_readdirplus_3_svc(READDIRPLUS3args *, struct svc_req *);
+#define NFS3_FSSTAT 18
+extern FSSTAT3res * nfs3_fsstat_3(FSSTAT3args *, CLIENT *);
+extern FSSTAT3res * nfs3_fsstat_3_svc(FSSTAT3args *, struct svc_req *);
+#define NFS3_FSINFO 19
+extern FSINFO3res * nfs3_fsinfo_3(FSINFO3args *, CLIENT *);
+extern FSINFO3res * nfs3_fsinfo_3_svc(FSINFO3args *, struct svc_req *);
+#define NFS3_PATHCONF 20
+extern PATHCONF3res * nfs3_pathconf_3(PATHCONF3args *, CLIENT *);
+extern PATHCONF3res * nfs3_pathconf_3_svc(PATHCONF3args *, struct svc_req *);
+#define NFS3_COMMIT 21
+extern COMMIT3res * nfs3_commit_3(COMMIT3args *, CLIENT *);
+extern COMMIT3res * nfs3_commit_3_svc(COMMIT3args *, struct svc_req *);
+extern int nfs_program_3_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define NFS3_NULL 0
+extern void * nfs3_null_3();
+extern void * nfs3_null_3_svc();
+#define NFS3_GETATTR 1
+extern GETATTR3res * nfs3_getattr_3();
+extern GETATTR3res * nfs3_getattr_3_svc();
+#define NFS3_SETATTR 2
+extern SETATTR3res * nfs3_setattr_3();
+extern SETATTR3res * nfs3_setattr_3_svc();
+#define NFS3_LOOKUP 3
+extern LOOKUP3res * nfs3_lookup_3();
+extern LOOKUP3res * nfs3_lookup_3_svc();
+#define NFS3_ACCESS 4
+extern ACCESS3res * nfs3_access_3();
+extern ACCESS3res * nfs3_access_3_svc();
+#define NFS3_READLINK 5
+extern READLINK3res * nfs3_readlink_3();
+extern READLINK3res * nfs3_readlink_3_svc();
+#define NFS3_READ 6
+extern READ3res * nfs3_read_3();
+extern READ3res * nfs3_read_3_svc();
+#define NFS3_WRITE 7
+extern WRITE3res * nfs3_write_3();
+extern WRITE3res * nfs3_write_3_svc();
+#define NFS3_CREATE 8
+extern CREATE3res * nfs3_create_3();
+extern CREATE3res * nfs3_create_3_svc();
+#define NFS3_MKDIR 9
+extern MKDIR3res * nfs3_mkdir_3();
+extern MKDIR3res * nfs3_mkdir_3_svc();
+#define NFS3_SYMLINK 10
+extern SYMLINK3res * nfs3_symlink_3();
+extern SYMLINK3res * nfs3_symlink_3_svc();
+#define NFS3_REMOVE 12
+extern REMOVE3res * nfs3_remove_3();
+extern REMOVE3res * nfs3_remove_3_svc();
+#define NFS3_RMDIR 13
+extern RMDIR3res * nfs3_rmdir_3();
+extern RMDIR3res * nfs3_rmdir_3_svc();
+#define NFS3_RENAME 14
+extern RENAME3res * nfs3_rename_3();
+extern RENAME3res * nfs3_rename_3_svc();
+#define NFS3_LINK 15
+extern LINK3res * nfs3_link_3();
+extern LINK3res * nfs3_link_3_svc();
+#define NFS3_READDIR 16
+extern READDIR3res * nfs3_readdir_3();
+extern READDIR3res * nfs3_readdir_3_svc();
+#define NFS3_READDIRPLUS 17
+extern READDIRPLUS3res * nfs3_readdirplus_3();
+extern READDIRPLUS3res * nfs3_readdirplus_3_svc();
+#define NFS3_FSSTAT 18
+extern FSSTAT3res * nfs3_fsstat_3();
+extern FSSTAT3res * nfs3_fsstat_3_svc();
+#define NFS3_FSINFO 19
+extern FSINFO3res * nfs3_fsinfo_3();
+extern FSINFO3res * nfs3_fsinfo_3_svc();
+#define NFS3_PATHCONF 20
+extern PATHCONF3res * nfs3_pathconf_3();
+extern PATHCONF3res * nfs3_pathconf_3_svc();
+#define NFS3_COMMIT 21
+extern COMMIT3res * nfs3_commit_3();
+extern COMMIT3res * nfs3_commit_3_svc();
+extern int nfs_program_3_freeresult ();
+#endif /* K&R C */
+
+/* the xdr functions */
+
+#if defined(__STDC__) || defined(__cplusplus)
+extern bool_t xdr_cookieverf3 (XDR *, cookieverf3);
+extern bool_t xdr_uint64 (XDR *, uint64*);
+extern bool_t xdr_cookie3 (XDR *, cookie3*);
+extern bool_t xdr_nfs_fh3 (XDR *, nfs_fh3*);
+extern bool_t xdr_filename3 (XDR *, filename3*);
+extern bool_t xdr_diropargs3 (XDR *, diropargs3*);
+extern bool_t xdr_ftype3 (XDR *, ftype3*);
+extern bool_t xdr_uint32 (XDR *, uint32*);
+extern bool_t xdr_int32 (XDR *, int32*);
+extern bool_t xdr_mode3 (XDR *, mode3*);
+extern bool_t xdr_uid3 (XDR *, uid3*);
+extern bool_t xdr_gid3 (XDR *, gid3*);
+extern bool_t xdr_size3 (XDR *, size3*);
+extern bool_t xdr_fileid3 (XDR *, fileid3*);
+extern bool_t xdr_specdata3 (XDR *, specdata3*);
+extern bool_t xdr_nfstime3 (XDR *, nfstime3*);
+extern bool_t xdr_fattr3 (XDR *, fattr3*);
+extern bool_t xdr_post_op_attr (XDR *, post_op_attr*);
+extern bool_t xdr_nfsstat3 (XDR *, nfsstat3*);
+extern bool_t xdr_stable_how (XDR *, stable_how*);
+extern bool_t xdr_offset3 (XDR *, offset3*);
+extern bool_t xdr_count3 (XDR *, count3*);
+extern bool_t xdr_wcc_attr (XDR *, wcc_attr*);
+extern bool_t xdr_pre_op_attr (XDR *, pre_op_attr*);
+extern bool_t xdr_wcc_data (XDR *, wcc_data*);
+extern bool_t xdr_WRITE3args (XDR *, WRITE3args*);
+extern bool_t xdr_writeverf3 (XDR *, writeverf3);
+extern bool_t xdr_WRITE3resok (XDR *, WRITE3resok*);
+extern bool_t xdr_WRITE3resfail (XDR *, WRITE3resfail*);
+extern bool_t xdr_WRITE3res (XDR *, WRITE3res*);
+extern bool_t xdr_LOOKUP3args (XDR *, LOOKUP3args*);
+extern bool_t xdr_LOOKUP3resok (XDR *, LOOKUP3resok*);
+extern bool_t xdr_LOOKUP3resfail (XDR *, LOOKUP3resfail*);
+extern bool_t xdr_LOOKUP3res (XDR *, LOOKUP3res*);
+extern bool_t xdr_COMMIT3args (XDR *, COMMIT3args*);
+extern bool_t xdr_COMMIT3resok (XDR *, COMMIT3resok*);
+extern bool_t xdr_COMMIT3resfail (XDR *, COMMIT3resfail*);
+extern bool_t xdr_COMMIT3res (XDR *, COMMIT3res*);
+extern bool_t xdr_ACCESS3args (XDR *, ACCESS3args*);
+extern bool_t xdr_ACCESS3resok (XDR *, ACCESS3resok*);
+extern bool_t xdr_ACCESS3resfail (XDR *, ACCESS3resfail*);
+extern bool_t xdr_ACCESS3res (XDR *, ACCESS3res*);
+extern bool_t xdr_GETATTR3args (XDR *, GETATTR3args*);
+extern bool_t xdr_GETATTR3resok (XDR *, GETATTR3resok*);
+extern bool_t xdr_GETATTR3res (XDR *, GETATTR3res*);
+extern bool_t xdr_time_how (XDR *, time_how*);
+extern bool_t xdr_set_mode3 (XDR *, set_mode3*);
+extern bool_t xdr_set_uid3 (XDR *, set_uid3*);
+extern bool_t xdr_set_gid3 (XDR *, set_gid3*);
+extern bool_t xdr_set_size3 (XDR *, set_size3*);
+extern bool_t xdr_set_atime (XDR *, set_atime*);
+extern bool_t xdr_set_mtime (XDR *, set_mtime*);
+extern bool_t xdr_sattr3 (XDR *, sattr3*);
+extern bool_t xdr_createmode3 (XDR *, createmode3*);
+extern bool_t xdr_createverf3 (XDR *, createverf3);
+extern bool_t xdr_createhow3 (XDR *, createhow3*);
+extern bool_t xdr_CREATE3args (XDR *, CREATE3args*);
+extern bool_t xdr_post_op_fh3 (XDR *, post_op_fh3*);
+extern bool_t xdr_CREATE3resok (XDR *, CREATE3resok*);
+extern bool_t xdr_CREATE3resfail (XDR *, CREATE3resfail*);
+extern bool_t xdr_CREATE3res (XDR *, CREATE3res*);
+extern bool_t xdr_REMOVE3args (XDR *, REMOVE3args*);
+extern bool_t xdr_REMOVE3resok (XDR *, REMOVE3resok*);
+extern bool_t xdr_REMOVE3resfail (XDR *, REMOVE3resfail*);
+extern bool_t xdr_REMOVE3res (XDR *, REMOVE3res*);
+extern bool_t xdr_READ3args (XDR *, READ3args*);
+extern bool_t xdr_READ3resok (XDR *, READ3resok*);
+extern bool_t xdr_READ3resfail (XDR *, READ3resfail*);
+extern bool_t xdr_READ3res (XDR *, READ3res*);
+extern bool_t xdr_FSINFO3args (XDR *, FSINFO3args*);
+extern bool_t xdr_FSINFO3resok (XDR *, FSINFO3resok*);
+extern bool_t xdr_FSINFO3resfail (XDR *, FSINFO3resfail*);
+extern bool_t xdr_FSINFO3res (XDR *, FSINFO3res*);
+extern bool_t xdr_FSSTAT3args (XDR *, FSSTAT3args*);
+extern bool_t xdr_FSSTAT3resok (XDR *, FSSTAT3resok*);
+extern bool_t xdr_FSSTAT3resfail (XDR *, FSSTAT3resfail*);
+extern bool_t xdr_FSSTAT3res (XDR *, FSSTAT3res*);
+extern bool_t xdr_PATHCONF3args (XDR *, PATHCONF3args*);
+extern bool_t xdr_PATHCONF3resok (XDR *, PATHCONF3resok*);
+extern bool_t xdr_PATHCONF3resfail (XDR *, PATHCONF3resfail*);
+extern bool_t xdr_PATHCONF3res (XDR *, PATHCONF3res*);
+extern bool_t xdr_nfspath3 (XDR *, nfspath3*);
+extern bool_t xdr_symlinkdata3 (XDR *, symlinkdata3*);
+extern bool_t xdr_SYMLINK3args (XDR *, SYMLINK3args*);
+extern bool_t xdr_SYMLINK3resok (XDR *, SYMLINK3resok*);
+extern bool_t xdr_SYMLINK3resfail (XDR *, SYMLINK3resfail*);
+extern bool_t xdr_SYMLINK3res (XDR *, SYMLINK3res*);
+extern bool_t xdr_READLINK3args (XDR *, READLINK3args*);
+extern bool_t xdr_READLINK3resok (XDR *, READLINK3resok*);
+extern bool_t xdr_READLINK3resfail (XDR *, READLINK3resfail*);
+extern bool_t xdr_READLINK3res (XDR *, READLINK3res*);
+extern bool_t xdr_devicedata3 (XDR *, devicedata3*);
+extern bool_t xdr_mknoddata3 (XDR *, mknoddata3*);
+extern bool_t xdr_MKNOD3args (XDR *, MKNOD3args*);
+extern bool_t xdr_MKNOD3resok (XDR *, MKNOD3resok*);
+extern bool_t xdr_MKNOD3resfail (XDR *, MKNOD3resfail*);
+extern bool_t xdr_MKNOD3res (XDR *, MKNOD3res*);
+extern bool_t xdr_MKDIR3args (XDR *, MKDIR3args*);
+extern bool_t xdr_MKDIR3resok (XDR *, MKDIR3resok*);
+extern bool_t xdr_MKDIR3resfail (XDR *, MKDIR3resfail*);
+extern bool_t xdr_MKDIR3res (XDR *, MKDIR3res*);
+extern bool_t xdr_RMDIR3args (XDR *, RMDIR3args*);
+extern bool_t xdr_RMDIR3resok (XDR *, RMDIR3resok*);
+extern bool_t xdr_RMDIR3resfail (XDR *, RMDIR3resfail*);
+extern bool_t xdr_RMDIR3res (XDR *, RMDIR3res*);
+extern bool_t xdr_RENAME3args (XDR *, RENAME3args*);
+extern bool_t xdr_RENAME3resok (XDR *, RENAME3resok*);
+extern bool_t xdr_RENAME3resfail (XDR *, RENAME3resfail*);
+extern bool_t xdr_RENAME3res (XDR *, RENAME3res*);
+extern bool_t xdr_READDIRPLUS3args (XDR *, READDIRPLUS3args*);
+extern bool_t xdr_entryplus3 (XDR *, entryplus3*);
+extern bool_t xdr_dirlistplus3 (XDR *, dirlistplus3*);
+extern bool_t xdr_READDIRPLUS3resok (XDR *, READDIRPLUS3resok*);
+extern bool_t xdr_READDIRPLUS3resfail (XDR *, READDIRPLUS3resfail*);
+extern bool_t xdr_READDIRPLUS3res (XDR *, READDIRPLUS3res*);
+extern bool_t xdr_READDIR3args (XDR *, READDIR3args*);
+extern bool_t xdr_entry3 (XDR *, entry3*);
+extern bool_t xdr_dirlist3 (XDR *, dirlist3*);
+extern bool_t xdr_READDIR3resok (XDR *, READDIR3resok*);
+extern bool_t xdr_READDIR3resfail (XDR *, READDIR3resfail*);
+extern bool_t xdr_READDIR3res (XDR *, READDIR3res*);
+extern bool_t xdr_LINK3args (XDR *, LINK3args*);
+extern bool_t xdr_LINK3resok (XDR *, LINK3resok*);
+extern bool_t xdr_LINK3resfail (XDR *, LINK3resfail*);
+extern bool_t xdr_LINK3res (XDR *, LINK3res*);
+extern bool_t xdr_sattrguard3 (XDR *, sattrguard3*);
+extern bool_t xdr_SETATTR3args (XDR *, SETATTR3args*);
+extern bool_t xdr_SETATTR3resok (XDR *, SETATTR3resok*);
+extern bool_t xdr_SETATTR3resfail (XDR *, SETATTR3resfail*);
+extern bool_t xdr_SETATTR3res (XDR *, SETATTR3res*);
+
+#else /* K&R C */
+extern bool_t xdr_cookieverf3 ();
+extern bool_t xdr_uint64 ();
+extern bool_t xdr_cookie3 ();
+extern bool_t xdr_nfs_fh3 ();
+extern bool_t xdr_filename3 ();
+extern bool_t xdr_diropargs3 ();
+extern bool_t xdr_ftype3 ();
+extern bool_t xdr_uint32 ();
+extern bool_t xdr_int32 ();
+extern bool_t xdr_mode3 ();
+extern bool_t xdr_uid3 ();
+extern bool_t xdr_gid3 ();
+extern bool_t xdr_size3 ();
+extern bool_t xdr_fileid3 ();
+extern bool_t xdr_specdata3 ();
+extern bool_t xdr_nfstime3 ();
+extern bool_t xdr_fattr3 ();
+extern bool_t xdr_post_op_attr ();
+extern bool_t xdr_nfsstat3 ();
+extern bool_t xdr_stable_how ();
+extern bool_t xdr_offset3 ();
+extern bool_t xdr_count3 ();
+extern bool_t xdr_wcc_attr ();
+extern bool_t xdr_pre_op_attr ();
+extern bool_t xdr_wcc_data ();
+extern bool_t xdr_WRITE3args ();
+extern bool_t xdr_writeverf3 ();
+extern bool_t xdr_WRITE3resok ();
+extern bool_t xdr_WRITE3resfail ();
+extern bool_t xdr_WRITE3res ();
+extern bool_t xdr_LOOKUP3args ();
+extern bool_t xdr_LOOKUP3resok ();
+extern bool_t xdr_LOOKUP3resfail ();
+extern bool_t xdr_LOOKUP3res ();
+extern bool_t xdr_COMMIT3args ();
+extern bool_t xdr_COMMIT3resok ();
+extern bool_t xdr_COMMIT3resfail ();
+extern bool_t xdr_COMMIT3res ();
+extern bool_t xdr_ACCESS3args ();
+extern bool_t xdr_ACCESS3resok ();
+extern bool_t xdr_ACCESS3resfail ();
+extern bool_t xdr_ACCESS3res ();
+extern bool_t xdr_GETATTR3args ();
+extern bool_t xdr_GETATTR3resok ();
+extern bool_t xdr_GETATTR3res ();
+extern bool_t xdr_time_how ();
+extern bool_t xdr_set_mode3 ();
+extern bool_t xdr_set_uid3 ();
+extern bool_t xdr_set_gid3 ();
+extern bool_t xdr_set_size3 ();
+extern bool_t xdr_set_atime ();
+extern bool_t xdr_set_mtime ();
+extern bool_t xdr_sattr3 ();
+extern bool_t xdr_createmode3 ();
+extern bool_t xdr_createverf3 ();
+extern bool_t xdr_createhow3 ();
+extern bool_t xdr_CREATE3args ();
+extern bool_t xdr_post_op_fh3 ();
+extern bool_t xdr_CREATE3resok ();
+extern bool_t xdr_CREATE3resfail ();
+extern bool_t xdr_CREATE3res ();
+extern bool_t xdr_REMOVE3args ();
+extern bool_t xdr_REMOVE3resok ();
+extern bool_t xdr_REMOVE3resfail ();
+extern bool_t xdr_REMOVE3res ();
+extern bool_t xdr_READ3args ();
+extern bool_t xdr_READ3resok ();
+extern bool_t xdr_READ3resfail ();
+extern bool_t xdr_READ3res ();
+extern bool_t xdr_FSINFO3args ();
+extern bool_t xdr_FSINFO3resok ();
+extern bool_t xdr_FSINFO3resfail ();
+extern bool_t xdr_FSINFO3res ();
+extern bool_t xdr_FSSTAT3args ();
+extern bool_t xdr_FSSTAT3resok ();
+extern bool_t xdr_FSSTAT3resfail ();
+extern bool_t xdr_FSSTAT3res ();
+extern bool_t xdr_PATHCONF3args ();
+extern bool_t xdr_PATHCONF3resok ();
+extern bool_t xdr_PATHCONF3resfail ();
+extern bool_t xdr_PATHCONF3res ();
+extern bool_t xdr_nfspath3 ();
+extern bool_t xdr_symlinkdata3 ();
+extern bool_t xdr_SYMLINK3args ();
+extern bool_t xdr_SYMLINK3resok ();
+extern bool_t xdr_SYMLINK3resfail ();
+extern bool_t xdr_SYMLINK3res ();
+extern bool_t xdr_READLINK3args ();
+extern bool_t xdr_READLINK3resok ();
+extern bool_t xdr_READLINK3resfail ();
+extern bool_t xdr_READLINK3res ();
+extern bool_t xdr_devicedata3 ();
+extern bool_t xdr_mknoddata3 ();
+extern bool_t xdr_MKNOD3args ();
+extern bool_t xdr_MKNOD3resok ();
+extern bool_t xdr_MKNOD3resfail ();
+extern bool_t xdr_MKNOD3res ();
+extern bool_t xdr_MKDIR3args ();
+extern bool_t xdr_MKDIR3resok ();
+extern bool_t xdr_MKDIR3resfail ();
+extern bool_t xdr_MKDIR3res ();
+extern bool_t xdr_RMDIR3args ();
+extern bool_t xdr_RMDIR3resok ();
+extern bool_t xdr_RMDIR3resfail ();
+extern bool_t xdr_RMDIR3res ();
+extern bool_t xdr_RENAME3args ();
+extern bool_t xdr_RENAME3resok ();
+extern bool_t xdr_RENAME3resfail ();
+extern bool_t xdr_RENAME3res ();
+extern bool_t xdr_READDIRPLUS3args ();
+extern bool_t xdr_entryplus3 ();
+extern bool_t xdr_dirlistplus3 ();
+extern bool_t xdr_READDIRPLUS3resok ();
+extern bool_t xdr_READDIRPLUS3resfail ();
+extern bool_t xdr_READDIRPLUS3res ();
+extern bool_t xdr_READDIR3args ();
+extern bool_t xdr_entry3 ();
+extern bool_t xdr_dirlist3 ();
+extern bool_t xdr_READDIR3resok ();
+extern bool_t xdr_READDIR3resfail ();
+extern bool_t xdr_READDIR3res ();
+extern bool_t xdr_LINK3args ();
+extern bool_t xdr_LINK3resok ();
+extern bool_t xdr_LINK3resfail ();
+extern bool_t xdr_LINK3res ();
+extern bool_t xdr_sattrguard3 ();
+extern bool_t xdr_SETATTR3args ();
+extern bool_t xdr_SETATTR3resok ();
+extern bool_t xdr_SETATTR3resfail ();
+extern bool_t xdr_SETATTR3res ();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_NFS_H_RPCGEN */
--- /dev/null
+/* copied from rfc 1813 */
+
+const NFS3_FHSIZE = 64; /* Maximum bytes in a V3 file handle */
+const NFS3_WRITEVERFSIZE = 8;
+const NFS3_CREATEVERFSIZE = 8;
+const NFS3_COOKIEVERFSIZE = 8;
+
+typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
+
+typedef unsigned hyper uint64;
+typedef uint64 cookie3;
+
+struct nfs_fh3 {
+ opaque data<NFS3_FHSIZE>;
+};
+
+typedef string filename3<>;
+
+struct diropargs3 {
+ nfs_fh3 dir;
+ filename3 name;
+};
+
+enum ftype3 {
+ NF3REG = 1,
+ NF3DIR = 2,
+ NF3BLK = 3,
+ NF3CHR = 4,
+ NF3LNK = 5,
+ NF3SOCK = 6,
+ NF3FIFO = 7
+};
+
+typedef unsigned long uint32;
+
+typedef long int32;
+
+typedef uint32 mode3;
+
+typedef uint32 uid3;
+
+typedef uint32 gid3;
+
+typedef uint64 size3;
+
+typedef uint64 fileid3;
+
+struct specdata3 {
+ uint32 specdata1;
+ uint32 specdata2;
+};
+
+struct nfstime3 {
+ uint32 seconds;
+ uint32 nseconds;
+};
+
+struct fattr3 {
+ ftype3 type;
+ mode3 mode;
+ uint32 nlink;
+ uid3 uid;
+ gid3 gid;
+ size3 size;
+ size3 used;
+ specdata3 rdev;
+ uint64 fsid;
+ fileid3 fileid;
+ nfstime3 atime;
+ nfstime3 mtime;
+ nfstime3 ctime;
+};
+
+union post_op_attr switch (bool attributes_follow) {
+ case TRUE:
+ fattr3 attributes;
+ case FALSE:
+ void;
+};
+
+
+enum nfsstat3 {
+ NFS3_OK = 0,
+ NFS3ERR_PERM = 1,
+ NFS3ERR_NOENT = 2,
+ NFS3ERR_IO = 5,
+ NFS3ERR_NXIO = 6,
+ NFS3ERR_ACCES = 13,
+ NFS3ERR_EXIST = 17,
+ NFS3ERR_XDEV = 18,
+ NFS3ERR_NODEV = 19,
+ NFS3ERR_NOTDIR = 20,
+ NFS3ERR_ISDIR = 21,
+ NFS3ERR_INVAL = 22,
+ NFS3ERR_FBIG = 27,
+ NFS3ERR_NOSPC = 28,
+ NFS3ERR_ROFS = 30,
+ NFS3ERR_MLINK = 31,
+ NFS3ERR_NAMETOOLONG = 63,
+ NFS3ERR_NOTEMPTY = 66,
+ NFS3ERR_DQUOT = 69,
+ NFS3ERR_STALE = 70,
+ NFS3ERR_REMOTE = 71,
+ NFS3ERR_BADHANDLE = 10001,
+ NFS3ERR_NOT_SYNC = 10002,
+ NFS3ERR_BAD_COOKIE = 10003,
+ NFS3ERR_NOTSUPP = 10004,
+ NFS3ERR_TOOSMALL = 10005,
+ NFS3ERR_SERVERFAULT = 10006,
+ NFS3ERR_BADTYPE = 10007,
+ NFS3ERR_JUKEBOX = 10008
+};
+
+enum stable_how {
+ UNSTABLE = 0,
+ DATA_SYNC = 1,
+ FILE_SYNC = 2
+};
+
+typedef uint64 offset3;
+
+typedef uint32 count3;
+
+struct wcc_attr {
+ size3 size;
+ nfstime3 mtime;
+ nfstime3 ctime;
+};
+
+union pre_op_attr switch (bool attributes_follow) {
+ case TRUE:
+ wcc_attr attributes;
+ case FALSE:
+ void;
+};
+
+struct wcc_data {
+ pre_op_attr before;
+ post_op_attr after;
+};
+
+struct WRITE3args {
+ nfs_fh3 file;
+ offset3 offset;
+ count3 count;
+ stable_how stable;
+ opaque data<>;
+};
+
+typedef opaque writeverf3[NFS3_WRITEVERFSIZE];
+
+struct WRITE3resok {
+ wcc_data file_wcc;
+ count3 count;
+ stable_how committed;
+ writeverf3 verf;
+};
+
+struct WRITE3resfail {
+ wcc_data file_wcc;
+};
+
+union WRITE3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ WRITE3resok resok;
+ default:
+ WRITE3resfail resfail;
+};
+
+struct LOOKUP3args {
+ diropargs3 what;
+};
+
+struct LOOKUP3resok {
+ nfs_fh3 object;
+ post_op_attr obj_attributes;
+ post_op_attr dir_attributes;
+};
+
+struct LOOKUP3resfail {
+ post_op_attr dir_attributes;
+};
+
+
+
+union LOOKUP3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ LOOKUP3resok resok;
+ default:
+ LOOKUP3resfail resfail;
+};
+
+struct COMMIT3args {
+ nfs_fh3 file;
+ offset3 offset;
+ count3 count;
+};
+
+struct COMMIT3resok {
+ wcc_data file_wcc;
+ writeverf3 verf;
+};
+
+struct COMMIT3resfail {
+ wcc_data file_wcc;
+};
+
+union COMMIT3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ COMMIT3resok resok;
+ default:
+ COMMIT3resfail resfail;
+};
+
+const ACCESS3_READ = 0x0001;
+const ACCESS3_LOOKUP = 0x0002;
+const ACCESS3_MODIFY = 0x0004;
+const ACCESS3_EXTEND = 0x0008;
+const ACCESS3_DELETE = 0x0010;
+const ACCESS3_EXECUTE = 0x0020;
+
+struct ACCESS3args {
+ nfs_fh3 object;
+ uint32 access;
+};
+
+struct ACCESS3resok {
+ post_op_attr obj_attributes;
+ uint32 access;
+};
+
+struct ACCESS3resfail {
+ post_op_attr obj_attributes;
+};
+
+union ACCESS3res switch (nfsstat3 status) {
+case NFS3_OK:
+ ACCESS3resok resok;
+default:
+ ACCESS3resfail resfail;
+};
+
+struct GETATTR3args {
+ nfs_fh3 object;
+};
+
+struct GETATTR3resok {
+ fattr3 obj_attributes;
+};
+
+union GETATTR3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ GETATTR3resok resok;
+ default:
+ void;
+};
+
+
+
+enum time_how {
+ DONT_CHANGE = 0,
+ SET_TO_SERVER_TIME = 1,
+ SET_TO_CLIENT_TIME = 2
+};
+
+union set_mode3 switch (bool set_it) {
+ case TRUE:
+ mode3 mode;
+ default:
+ void;
+};
+
+union set_uid3 switch (bool set_it) {
+ case TRUE:
+ uid3 uid;
+ default:
+ void;
+};
+
+union set_gid3 switch (bool set_it) {
+ case TRUE:
+ gid3 gid;
+ default:
+ void;
+};
+
+union set_size3 switch (bool set_it) {
+ case TRUE:
+ size3 size;
+ default:
+ void;
+};
+
+union set_atime switch (time_how set_it) {
+ case SET_TO_CLIENT_TIME:
+ nfstime3 atime;
+ default:
+ void;
+};
+
+union set_mtime switch (time_how set_it) {
+ case SET_TO_CLIENT_TIME:
+ nfstime3 mtime;
+ default:
+ void;
+};
+
+struct sattr3 {
+ set_mode3 mode;
+ set_uid3 uid;
+ set_gid3 gid;
+ set_size3 size;
+ set_atime atime;
+ set_mtime mtime;
+};
+
+enum createmode3 {
+ UNCHECKED = 0,
+ GUARDED = 1,
+ EXCLUSIVE = 2
+};
+
+
+typedef opaque createverf3[NFS3_CREATEVERFSIZE];
+
+union createhow3 switch (createmode3 mode) {
+ case UNCHECKED:
+ case GUARDED:
+ sattr3 obj_attributes;
+ case EXCLUSIVE:
+ createverf3 verf;
+};
+
+struct CREATE3args {
+ diropargs3 where;
+ createhow3 how;
+};
+
+union post_op_fh3 switch (bool handle_follows) {
+ case TRUE:
+ nfs_fh3 handle;
+ case FALSE:
+ void;
+};
+
+struct CREATE3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+
+struct CREATE3resfail {
+ wcc_data dir_wcc;
+ };
+
+union CREATE3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ CREATE3resok resok;
+ default:
+ CREATE3resfail resfail;
+};
+
+struct REMOVE3args {
+ diropargs3 object;
+};
+
+struct REMOVE3resok {
+ wcc_data dir_wcc;
+};
+
+struct REMOVE3resfail {
+ wcc_data dir_wcc;
+};
+
+union REMOVE3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ REMOVE3resok resok;
+ default:
+ REMOVE3resfail resfail;
+};
+
+struct READ3args {
+ nfs_fh3 file;
+ offset3 offset;
+ count3 count;
+};
+
+struct READ3resok {
+ post_op_attr file_attributes;
+ count3 count;
+ bool eof;
+ opaque data<>;
+};
+
+struct READ3resfail {
+ post_op_attr file_attributes;
+};
+
+union READ3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ READ3resok resok;
+ default:
+ READ3resfail resfail;
+};
+
+
+const FSF3_LINK = 0x0001;
+const FSF3_SYMLINK = 0x0002;
+const FSF3_HOMOGENEOUS = 0x0008;
+const FSF3_CANSETTIME = 0x0010;
+
+struct FSINFO3args {
+ nfs_fh3 fsroot;
+};
+
+struct FSINFO3resok {
+ post_op_attr obj_attributes;
+ uint32 rtmax;
+ uint32 rtpref;
+ uint32 rtmult;
+ uint32 wtmax;
+ uint32 wtpref;
+ uint32 wtmult;
+ uint32 dtpref;
+ size3 maxfilesize;
+ nfstime3 time_delta;
+ uint32 properties;
+};
+
+struct FSINFO3resfail {
+ post_op_attr obj_attributes;
+};
+
+union FSINFO3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ FSINFO3resok resok;
+ default:
+ FSINFO3resfail resfail;
+};
+
+
+struct FSSTAT3args {
+ nfs_fh3 fsroot;
+};
+
+struct FSSTAT3resok {
+ post_op_attr obj_attributes;
+ size3 tbytes;
+ size3 fbytes;
+ size3 abytes;
+ size3 tfiles;
+ size3 ffiles;
+ size3 afiles;
+ uint32 invarsec;
+};
+
+struct FSSTAT3resfail {
+ post_op_attr obj_attributes;
+};
+
+union FSSTAT3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ FSSTAT3resok resok;
+ default:
+ FSSTAT3resfail resfail;
+};
+
+struct PATHCONF3args {
+ nfs_fh3 object;
+};
+
+struct PATHCONF3resok {
+ post_op_attr obj_attributes;
+ uint32 linkmax;
+ uint32 name_max;
+ bool no_trunc;
+ bool chown_restricted;
+ bool case_insensitive;
+ bool case_preserving;
+};
+
+struct PATHCONF3resfail {
+ post_op_attr obj_attributes;
+};
+
+union PATHCONF3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ PATHCONF3resok resok;
+ default:
+ PATHCONF3resfail resfail;
+};
+
+typedef string nfspath3<>;
+
+struct symlinkdata3 {
+ sattr3 symlink_attributes;
+ nfspath3 symlink_data;
+};
+
+struct SYMLINK3args {
+ diropargs3 where;
+ symlinkdata3 symlink;
+};
+
+struct SYMLINK3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+
+struct SYMLINK3resfail {
+ wcc_data dir_wcc;
+};
+
+union SYMLINK3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ SYMLINK3resok resok;
+ default:
+ SYMLINK3resfail resfail;
+};
+
+
+struct READLINK3args {
+ nfs_fh3 symlink;
+};
+
+struct READLINK3resok {
+ post_op_attr symlink_attributes;
+ nfspath3 data;
+};
+
+struct READLINK3resfail {
+ post_op_attr symlink_attributes;
+};
+
+union READLINK3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ READLINK3resok resok;
+ default:
+ READLINK3resfail resfail;
+};
+
+
+struct devicedata3 {
+ sattr3 dev_attributes;
+ specdata3 spec;
+};
+
+union mknoddata3 switch (ftype3 type) {
+ case NF3CHR:
+ case NF3BLK:
+ devicedata3 device;
+ case NF3SOCK:
+ case NF3FIFO:
+ sattr3 pipe_attributes;
+ default:
+ void;
+};
+
+struct MKNOD3args {
+ diropargs3 where;
+ mknoddata3 what;
+};
+
+struct MKNOD3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+
+struct MKNOD3resfail {
+ wcc_data dir_wcc;
+};
+
+union MKNOD3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ MKNOD3resok resok;
+ default:
+ MKNOD3resfail resfail;
+};
+
+
+struct MKDIR3args {
+ diropargs3 where;
+ sattr3 attributes;
+};
+
+struct MKDIR3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+
+struct MKDIR3resfail {
+ wcc_data dir_wcc;
+};
+
+union MKDIR3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ MKDIR3resok resok;
+ default:
+ MKDIR3resfail resfail;
+};
+
+struct RMDIR3args {
+ diropargs3 object;
+};
+
+struct RMDIR3resok {
+ wcc_data dir_wcc;
+};
+
+struct RMDIR3resfail {
+ wcc_data dir_wcc;
+};
+
+union RMDIR3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ RMDIR3resok resok;
+ default:
+ RMDIR3resfail resfail;
+};
+
+struct RENAME3args {
+ diropargs3 from;
+ diropargs3 to;
+};
+
+struct RENAME3resok {
+ wcc_data fromdir_wcc;
+ wcc_data todir_wcc;
+};
+
+struct RENAME3resfail {
+ wcc_data fromdir_wcc;
+ wcc_data todir_wcc;
+};
+
+union RENAME3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ RENAME3resok resok;
+ default:
+ RENAME3resfail resfail;
+};
+
+struct READDIRPLUS3args {
+ nfs_fh3 dir;
+ cookie3 cookie;
+ cookieverf3 cookieverf;
+ count3 dircount;
+ count3 maxcount;
+};
+
+struct entryplus3 {
+ fileid3 fileid;
+ filename3 name;
+ cookie3 cookie;
+ post_op_attr name_attributes;
+ post_op_fh3 name_handle;
+ entryplus3 *nextentry;
+};
+
+struct dirlistplus3 {
+ entryplus3 *entries;
+ bool eof;
+};
+
+struct READDIRPLUS3resok {
+ post_op_attr dir_attributes;
+ cookieverf3 cookieverf;
+ dirlistplus3 reply;
+};
+
+
+struct READDIRPLUS3resfail {
+ post_op_attr dir_attributes;
+};
+
+union READDIRPLUS3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ READDIRPLUS3resok resok;
+ default:
+ READDIRPLUS3resfail resfail;
+};
+
+struct READDIR3args {
+ nfs_fh3 dir;
+ cookie3 cookie;
+ cookieverf3 cookieverf;
+ count3 count;
+};
+
+
+struct entry3 {
+ fileid3 fileid;
+ filename3 name;
+ cookie3 cookie;
+ entry3 *nextentry;
+};
+
+struct dirlist3 {
+ entry3 *entries;
+ bool eof;
+};
+
+struct READDIR3resok {
+ post_op_attr dir_attributes;
+ cookieverf3 cookieverf;
+ dirlist3 reply;
+};
+
+struct READDIR3resfail {
+ post_op_attr dir_attributes;
+};
+
+union READDIR3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ READDIR3resok resok;
+ default:
+ READDIR3resfail resfail;
+};
+
+struct LINK3args {
+ nfs_fh3 file;
+ diropargs3 link;
+};
+
+struct LINK3resok {
+ post_op_attr file_attributes;
+ wcc_data linkdir_wcc;
+};
+
+struct LINK3resfail {
+ post_op_attr file_attributes;
+ wcc_data linkdir_wcc;
+};
+
+union LINK3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ LINK3resok resok;
+ default:
+ LINK3resfail resfail;
+};
+
+union sattrguard3 switch (bool check) {
+ case TRUE:
+ nfstime3 obj_ctime;
+ case FALSE:
+ void;
+};
+
+struct SETATTR3args {
+ nfs_fh3 object;
+ sattr3 new_attributes;
+ sattrguard3 guard;
+};
+
+struct SETATTR3resok {
+ wcc_data obj_wcc;
+};
+
+struct SETATTR3resfail {
+ wcc_data obj_wcc;
+};
+
+union SETATTR3res switch (nfsstat3 status) {
+ case NFS3_OK:
+ SETATTR3resok resok;
+ default:
+ SETATTR3resfail resfail;
+};
+
+program NFS_PROGRAM {
+ version NFS_V3 {
+ void
+ NFS3_NULL(void) = 0;
+
+ GETATTR3res
+ NFS3_GETATTR(GETATTR3args) = 1;
+
+ SETATTR3res
+ NFS3_SETATTR(SETATTR3args) = 2;
+
+ LOOKUP3res
+ NFS3_LOOKUP(LOOKUP3args) = 3;
+
+ ACCESS3res
+ NFS3_ACCESS(ACCESS3args) = 4;
+
+ READLINK3res
+ NFS3_READLINK(READLINK3args) = 5;
+
+ READ3res
+ NFS3_READ(READ3args) = 6;
+
+ WRITE3res
+ NFS3_WRITE(WRITE3args) = 7;
+
+ CREATE3res
+ NFS3_CREATE(CREATE3args) = 8;
+
+ MKDIR3res
+ NFS3_MKDIR(MKDIR3args) = 9;
+
+ SYMLINK3res
+ NFS3_SYMLINK(SYMLINK3args) = 10;
+
+/* MKNOD3res NFSPROC3_MKNOD(MKNOD3args) = 11;*/
+
+ REMOVE3res
+ NFS3_REMOVE(REMOVE3args) = 12;
+
+ RMDIR3res
+ NFS3_RMDIR(RMDIR3args) = 13;
+
+ RENAME3res
+ NFS3_RENAME(RENAME3args) = 14;
+
+ LINK3res
+ NFS3_LINK(LINK3args) = 15;
+
+ READDIR3res
+ NFS3_READDIR(READDIR3args) = 16;
+
+ READDIRPLUS3res
+ NFS3_READDIRPLUS(READDIRPLUS3args) = 17;
+
+ FSSTAT3res
+ NFS3_FSSTAT(FSSTAT3args) = 18;
+
+ FSINFO3res
+ NFS3_FSINFO(FSINFO3args) = 19;
+
+ PATHCONF3res
+ NFS3_PATHCONF(PATHCONF3args) = 20;
+
+ COMMIT3res
+ NFS3_COMMIT(COMMIT3args) = 21;
+ } = 3;
+} = 100003;
--- /dev/null
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _NFSACL_H_RPCGEN
+#define _NFSACL_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define NFSACL_PROGRAM 100227
+#define NFSACL_V3 3
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define NFSACL3_NULL 0
+extern void * nfsacl3_null_3(void *, CLIENT *);
+extern void * nfsacl3_null_3_svc(void *, struct svc_req *);
+extern int nfsacl_program_3_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define NFSACL3_NULL 0
+extern void * nfsacl3_null_3();
+extern void * nfsacl3_null_3_svc();
+extern int nfsacl_program_3_freeresult ();
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_NFSACL_H_RPCGEN */
--- /dev/null
+/* deducted from wireshark traces */
+
+
+program NFSACL_PROGRAM {
+ version NFSACL_V3 {
+ void NFSACL3_NULL(void) = 0;
+ } = 3;
+} = 100227;
--- /dev/null
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _PORTMAP_H_RPCGEN
+#define _PORTMAP_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PMAP_PORT 111
+
+struct mapping {
+ u_int prog;
+ u_int vers;
+ u_int prot;
+ u_int port;
+};
+typedef struct mapping mapping;
+
+struct call_args {
+ u_int prog;
+ u_int vers;
+ u_int proc;
+ struct {
+ u_int args_len;
+ char *args_val;
+ } args;
+};
+typedef struct call_args call_args;
+
+#define PMAP_PROGRAM 100000
+#define PMAP_V2 2
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define PMAP_NULL 0
+extern void * pmap_null_2(void *, CLIENT *);
+extern void * pmap_null_2_svc(void *, struct svc_req *);
+#define PMAP_SET 1
+extern bool_t * pmap_set_2(mapping *, CLIENT *);
+extern bool_t * pmap_set_2_svc(mapping *, struct svc_req *);
+#define PMAP_UNSET 2
+extern bool_t * pmap_unset_2(mapping *, CLIENT *);
+extern bool_t * pmap_unset_2_svc(mapping *, struct svc_req *);
+#define PMAP_GETPORT 3
+extern u_int * pmap_getport_2(mapping *, CLIENT *);
+extern u_int * pmap_getport_2_svc(mapping *, struct svc_req *);
+extern int pmap_program_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define PMAP_NULL 0
+extern void * pmap_null_2();
+extern void * pmap_null_2_svc();
+#define PMAP_SET 1
+extern bool_t * pmap_set_2();
+extern bool_t * pmap_set_2_svc();
+#define PMAP_UNSET 2
+extern bool_t * pmap_unset_2();
+extern bool_t * pmap_unset_2_svc();
+#define PMAP_GETPORT 3
+extern u_int * pmap_getport_2();
+extern u_int * pmap_getport_2_svc();
+extern int pmap_program_2_freeresult ();
+#endif /* K&R C */
+
+/* the xdr functions */
+
+#if defined(__STDC__) || defined(__cplusplus)
+extern bool_t xdr_mapping (XDR *, mapping*);
+extern bool_t xdr_call_args (XDR *, call_args*);
+
+#else /* K&R C */
+extern bool_t xdr_mapping ();
+extern bool_t xdr_call_args ();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_PORTMAP_H_RPCGEN */
--- /dev/null
+/*
+ * From RFC1833
+ */
+
+const PMAP_PORT = 111; /* portmapper port number */
+
+struct mapping {
+ unsigned int prog;
+ unsigned int vers;
+ unsigned int prot;
+ unsigned int port;
+};
+
+struct call_args {
+ unsigned int prog;
+ unsigned int vers;
+ unsigned int proc;
+ opaque args<>;
+};
+
+
+program PMAP_PROGRAM {
+ version PMAP_V2 {
+ void
+ PMAP_NULL(void) = 0;
+
+ bool
+ PMAP_SET(mapping) = 1;
+
+ bool
+ PMAP_UNSET(mapping) = 2;
+
+ unsigned int
+ PMAP_GETPORT(mapping) = 3;
+ } = 2;
+} = 100000;
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <rpc/xdr.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include "nfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-private.h"
+#include "dlinklist.h"
+
+static void set_nonblocking(int fd)
+{
+ unsigned v;
+ v = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, v | O_NONBLOCK);
+}
+
+int rpc_get_fd(struct rpc_context *rpc)
+{
+ return rpc->fd;
+}
+
+int rpc_which_events(struct rpc_context *rpc)
+{
+ int events = POLLIN;
+
+ if (rpc->is_connected == 0) {
+ events |= POLLOUT;
+ }
+
+ if (rpc->outqueue) {
+ events |= POLLOUT;
+ }
+ return events;
+}
+
+static int rpc_write_to_socket(struct rpc_context *rpc)
+{
+ ssize_t count;
+
+ if (rpc == NULL) {
+ printf("trying to write to socket for NULL context\n");
+ return -1;
+ }
+ if (rpc->fd == -1) {
+ printf("trying to write but not connected\n");
+ return -2;
+ }
+
+ while (rpc->outqueue != NULL) {
+ ssize_t total;
+
+ total = rpc->outqueue->outdata.size;
+
+ count = write(rpc->fd, rpc->outqueue->outdata.data + rpc->outqueue->written, total - rpc->outqueue->written);
+ if (count == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ printf("socket would block, return from write to socket\n");
+ return 0;
+ }
+ printf("Error when writing to socket :%s(%d)\n", strerror(errno), errno);
+ return -3;
+ }
+
+ rpc->outqueue->written += count;
+ if (rpc->outqueue->written == total) {
+ struct rpc_pdu *pdu = rpc->outqueue;
+
+ DLIST_REMOVE(rpc->outqueue, pdu);
+ DLIST_ADD_END(rpc->waitpdu, pdu, NULL);
+ }
+ }
+ return 0;
+}
+
+static int rpc_read_from_socket(struct rpc_context *rpc)
+{
+ int available;
+ int size;
+ unsigned char *buf;
+ ssize_t count;
+
+ if (ioctl(rpc->fd, FIONREAD, &available) != 0) {
+ rpc_set_error(rpc, "Ioctl FIONREAD returned error : %d. Closing socket.", errno);
+ return -1;
+ }
+ if (available == 0) {
+ rpc_set_error(rpc, "Socket has been closed");
+ return -2;
+ }
+ size = rpc->insize - rpc->inpos + available;
+ buf = malloc(size);
+ if (buf == NULL) {
+ rpc_set_error(rpc, "Out of memory: failed to allocate %d bytes for input buffer. Closing socket.", size);
+ return -3;
+ }
+ if (rpc->insize > rpc->inpos) {
+ memcpy(buf, rpc->inbuf + rpc->inpos, rpc->insize - rpc->inpos);
+ rpc->insize -= rpc->inpos;
+ rpc->inpos = 0;
+ }
+
+ count = read(rpc->fd, buf + rpc->insize, available);
+ if (count == -1) {
+ if (errno == EINTR) {
+ free(buf);
+ buf = NULL;
+ return 0;
+ }
+ rpc_set_error(rpc, "Read from socket failed, errno:%d. Closing socket.", errno);
+ free(buf);
+ buf = NULL;
+ return -4;
+ }
+
+ if (rpc->inbuf != NULL) {
+ free(rpc->inbuf);
+ }
+ rpc->inbuf = (char *)buf;
+ rpc->insize += count;
+
+ while (1) {
+ if (rpc->insize - rpc->inpos < 4) {
+ return 0;
+ }
+ count = rpc_get_pdu_size(rpc->inbuf + rpc->inpos);
+ if (rpc->insize + rpc->inpos < count) {
+ return 0;
+ }
+ if (rpc_process_pdu(rpc, rpc->inbuf + rpc->inpos, count) != 0) {
+ rpc_set_error(rpc, "Invalid/garbage pdu received from server. Closing socket");
+ return -5;
+ }
+ rpc->inpos += count;
+ if (rpc->inpos == rpc->insize) {
+ free(rpc->inbuf);
+ rpc->inbuf = NULL;
+ rpc->insize = 0;
+ rpc->inpos = 0;
+ }
+ }
+ return 0;
+}
+
+
+
+int rpc_service(struct rpc_context *rpc, int revents)
+{
+ if (revents & POLLERR) {
+ printf("rpc_service: POLLERR, socket error\n");
+ if (rpc->is_connected == 0) {
+ rpc_set_error(rpc, "Failed to connect to server socket.");
+ } else {
+ rpc_set_error(rpc, "Socket closed with POLLERR");
+ }
+ rpc->connect_cb(rpc, RPC_STATUS_ERROR, rpc->error_string, rpc->connect_data);
+ return -1;
+ }
+ if (revents & POLLHUP) {
+ printf("rpc_service: POLLHUP, socket error\n");
+ rpc_set_error(rpc, "Socket failed with POLLHUP");
+ rpc->connect_cb(rpc, RPC_STATUS_ERROR, rpc->error_string, rpc->connect_data);
+ return -2;
+ }
+
+ if (rpc->is_connected == 0 && rpc->fd != -1 && revents&POLLOUT) {
+ rpc->is_connected = 1;
+ rpc->connect_cb(rpc, RPC_STATUS_SUCCESS, NULL, rpc->connect_data);
+ return 0;
+ }
+
+ if (revents & POLLOUT && rpc->outqueue != NULL) {
+ if (rpc_write_to_socket(rpc) != 0) {
+ printf("write to socket failed\n");
+ return -3;
+ }
+ }
+
+ if (revents & POLLIN) {
+ if (rpc_read_from_socket(rpc) != 0) {
+ rpc_disconnect(rpc, rpc_get_error(rpc));
+ return -4;
+ }
+ }
+
+ return 0;
+}
+
+
+int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, int use_privileged_port, rpc_cb cb, void *private_data)
+{
+ struct sockaddr_storage s;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&s;
+ int socksize;
+
+ if (rpc->fd != -1) {
+ rpc_set_error(rpc, "Trying to connect while already connected");
+ printf("%s\n", rpc->error_string);
+ return -1;
+ }
+
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(port);
+ if (inet_pton(AF_INET, server, &sin->sin_addr) != 1) {
+ rpc_set_error(rpc, "Not a valid server ip address");
+ printf("%s\n", rpc->error_string);
+ return -2;
+ }
+
+ switch (s.ss_family) {
+ case AF_INET:
+ rpc->fd = socket(AF_INET, SOCK_STREAM, 0);
+ socksize = sizeof(struct sockaddr_in);
+ break;
+ }
+
+ if (rpc->fd == -1) {
+ rpc_set_error(rpc, "Failed to open socket");
+ printf("%s\n", rpc->error_string);
+ return -3;
+ }
+
+ /* if we are root, try to find a privileged port to use (512 - 1023) */
+ if (geteuid() == 0 && use_privileged_port != 0) {
+ struct sockaddr_storage ls;
+ int ret, count = 0;
+ static int local_port = 0;
+
+ if (local_port == 0) {
+ srandom(getpid() ^ time(NULL));
+ local_port = random()%512 + 512;
+ }
+
+ do {
+ count ++;
+ if (local_port >= 1024) {
+ local_port = 512;
+ }
+ switch (s.ss_family) {
+ case AF_INET:
+ bzero(&ls, socksize);
+ ((struct sockaddr_in *)&ls)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&ls)->sin_addr.s_addr = INADDR_ANY;
+ ((struct sockaddr_in *)&ls)->sin_port = htons(local_port++);
+ break;
+ }
+
+ ret = bind(rpc->fd, (struct sockaddr *)&ls, socksize);
+ } while (ret != 0 && count < 50);
+ }
+
+ rpc->connect_cb = cb;
+ rpc->connect_data = private_data;
+
+ set_nonblocking(rpc->fd);
+
+ if (connect(rpc->fd, (struct sockaddr *)&s, socksize) != 0 && errno != EINPROGRESS) {
+ rpc_set_error(rpc, "connect() to server failed");
+ printf("%s\n", rpc->error_string);
+ return -4;
+ }
+
+ return 0;
+}
+
+int rpc_disconnect(struct rpc_context *rpc, char *error)
+{
+ if (rpc->fd != -1) {
+ close(rpc->fd);
+ }
+ rpc->fd = -1;
+
+ rpc->is_connected = 0;
+
+ rpc_error_all_pdus(rpc, error);
+
+ return 0;
+}
--- /dev/null
+/*
+ Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Example program using the highlevel async interface.
+ */
+
+#define SERVER "10.1.1.27"
+#define EXPORT "/VIRTUAL"
+#define NFSFILE "/BOOKS/Classics/Dracula.djvu"
+#define NFSDIR "/BOOKS/Classics/"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <ccan/nfs/nfs.h>
+
+struct client {
+ char *server;
+ char *export;
+ uint32_t mount_port;
+ struct nfsfh *nfsfh;
+ int is_finished;
+};
+
+void nfs_opendir_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct client *client = private_data;
+ struct nfsdir *nfsdir = data;
+ struct nfsdirent *nfsdirent;
+
+ if (status < 0) {
+ printf("opendir failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+
+ printf("opendir successful\n");
+ while((nfsdirent = nfs_readdir(nfs, nfsdir)) != NULL) {
+ printf("Inode:%d Name:%s\n", (int)nfsdirent->inode, nfsdirent->name);
+ }
+ nfs_closedir(nfs, nfsdir);
+
+ client->is_finished = 1;
+}
+
+void nfs_close_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct client *client = private_data;
+
+ if (status < 0) {
+ printf("close failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+
+ printf("close successful\n");
+ printf("call opendir(%s)\n", NFSDIR);
+ if (nfs_opendir_async(nfs, NFSDIR, nfs_opendir_cb, client) != 0) {
+ printf("Failed to start async nfs close\n");
+ exit(10);
+ }
+}
+
+void nfs_fstat_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct client *client = private_data;
+ struct stat *st;
+
+ if (status < 0) {
+ printf("fstat call failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+
+ printf("Got reply from server for fstat(%s).\n", NFSFILE);
+ st = (struct stat *)data;
+ printf("Mode %04o\n", st->st_mode);
+ printf("Size %d\n", (int)st->st_size);
+ printf("Inode %04o\n", (int)st->st_ino);
+
+ printf("Close file\n");
+ if (nfs_close_async(nfs, client->nfsfh, nfs_close_cb, client) != 0) {
+ printf("Failed to start async nfs close\n");
+ exit(10);
+ }
+}
+
+void nfs_read_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct client *client = private_data;
+ char *read_data;
+ int i;
+
+ if (status < 0) {
+ printf("read failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+
+ printf("read successful with %d bytes of data\n", status);
+ read_data = data;
+ for (i=0;i<16;i++) {
+ printf("%02x ", read_data[i]&0xff);
+ }
+ printf("\n");
+ printf("Fstat file :%s\n", NFSFILE);
+ if (nfs_fstat_async(nfs, client->nfsfh, nfs_fstat_cb, client) != 0) {
+ printf("Failed to start async nfs fstat\n");
+ exit(10);
+ }
+}
+
+void nfs_open_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct client *client = private_data;
+ struct nfsfh *nfsfh;
+
+ if (status < 0) {
+ printf("open call failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+
+ nfsfh = data;
+ client->nfsfh = nfsfh;
+ printf("Got reply from server for open(%s). Handle:%p\n", NFSFILE, data);
+ printf("Read first 16 bytes\n");
+ if (nfs_pread_async(nfs, nfsfh, 0, 16, nfs_read_cb, client) != 0) {
+ printf("Failed to start async nfs open\n");
+ exit(10);
+ }
+}
+
+void nfs_stat_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct client *client = private_data;
+ struct stat *st;
+
+ if (status < 0) {
+ printf("stat call failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+
+ printf("Got reply from server for stat(%s).\n", NFSFILE);
+ st = (struct stat *)data;
+ printf("Mode %04o\n", st->st_mode);
+ printf("Size %d\n", (int)st->st_size);
+ printf("Inode %04o\n", (int)st->st_ino);
+
+ printf("Open file for reading :%s\n", NFSFILE);
+ if (nfs_open_async(nfs, NFSFILE, O_RDONLY, nfs_open_cb, client) != 0) {
+ printf("Failed to start async nfs open\n");
+ exit(10);
+ }
+}
+
+void nfs_mount_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct client *client = private_data;
+
+ if (status < 0) {
+ printf("mount/mnt call failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+
+ printf("Got reply from server for MOUNT/MNT procedure.\n");
+ printf("Stat file :%s\n", NFSFILE);
+ if (nfs_stat_async(nfs, NFSFILE, nfs_stat_cb, client) != 0) {
+ printf("Failed to start async nfs stat\n");
+ exit(10);
+ }
+}
+
+
+
+int main(int argc, char *argv[])
+{
+ struct nfs_context *nfs;
+ struct pollfd pfd;
+ int ret;
+ struct client client;
+
+ client.server = SERVER;
+ client.export = EXPORT;
+ client.is_finished = 0;
+
+ nfs = nfs_init_context();
+ if (nfs == NULL) {
+ printf("failed to init context\n");
+ exit(10);
+ }
+
+ ret = nfs_mount_async(nfs, client.server, client.export, nfs_mount_cb, &client);
+ if (ret != 0) {
+ printf("Failed to start async nfs mount\n");
+ exit(10);
+ }
+
+ for (;;) {
+ pfd.fd = nfs_get_fd(nfs);
+ pfd.events = nfs_which_events(nfs);
+
+ if (poll(&pfd, 1, -1) < 0) {
+ printf("Poll failed");
+ exit(10);
+ }
+ if (nfs_service(nfs, pfd.revents) < 0) {
+ printf("nfs_service failed\n");
+ break;
+ }
+ if (client.is_finished) {
+ break;
+ }
+ }
+
+ nfs_destroy_context(nfs);
+ printf("nfsclient finished\n");
+ return 0;
+}
--- /dev/null
+/*
+ Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Example program using the lowlevel raw interface.
+ * This allow accurate control of the exact commands that are being used.
+ */
+
+#define SERVER "10.1.1.27"
+#define EXPORT "/VIRTUAL"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <ccan/nfs/nfs.h>
+#include <ccan/nfs/libnfs-raw.h>
+#include <ccan/nfs/rpc/mount.h>
+
+struct client {
+ char *server;
+ char *export;
+ uint32_t mount_port;
+ int is_finished;
+};
+
+void mount_mnt_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
+{
+ mountres3 *res;
+ struct client *client = private_data;
+
+ if (status == RPC_STATUS_ERROR) {
+ printf("mount/mnt call failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+ if (status != RPC_STATUS_SUCCESS) {
+ printf("mount/mnt call to server %s failed, status:%d\n", client->server, status);
+ exit(10);
+ }
+
+ res = data;
+ if (res->fhs_status != MNT3_OK) {
+ printf("RPC error: Mount failed with error %s(%d) %s(%d)", mountstat3_to_str(res->fhs_status), res->fhs_status, strerror(-mountstat3_to_errno(res->fhs_status)), -mountstat3_to_errno(res->fhs_status));
+ exit(10);
+ }
+
+ printf("Got reply from server for MOUNT/MNT procedure.\n");
+ client->is_finished = 1;
+}
+
+
+void mount_null_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
+{
+ struct client *client = private_data;
+
+ if (status == RPC_STATUS_ERROR) {
+ printf("mount null call failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+ if (status != RPC_STATUS_SUCCESS) {
+ printf("mount null call to server %s failed, status:%d\n", client->server, status);
+ exit(10);
+ }
+
+ printf("Got reply from server for MOUNT/NULL procedure.\n");
+ printf("Send MOUNT/MNT command for %s\n", client->export);
+ if (rpc_mount_mnt_async(rpc, mount_mnt_cb, client->export, client) != 0) {
+ printf("Failed to send mnt request\n");
+ exit(10);
+ }
+
+}
+
+void mount_connect_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
+{
+ struct client *client = private_data;
+
+ if (status != RPC_STATUS_SUCCESS) {
+ printf("connection to RPC.MOUNTD on server %s failed\n", client->server);
+ exit(10);
+ }
+
+ printf("Connected to RPC.MOUNTD on %s:%d\n", client->server, client->mount_port);
+ printf("Send NULL request to check if RPC.MOUNTD is actually running\n");
+ if (rpc_mount_null_async(rpc, mount_null_cb, client) != 0) {
+ printf("Failed to send null request\n");
+ exit(10);
+ }
+}
+
+
+void pmap_getport_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
+{
+ struct client *client = private_data;
+ uint32_t port;
+
+ if (status == RPC_STATUS_ERROR) {
+ printf("portmapper getport call failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+ if (status != RPC_STATUS_SUCCESS) {
+ printf("portmapper getport call to server %s failed, status:%d\n", client->server, status);
+ exit(10);
+ }
+
+ client->mount_port = *(uint32_t *)data;
+ printf("GETPORT returned Port:%d\n", client->mount_port);
+ if (client->mount_port == 0) {
+ printf("RPC.MOUNTD is not available on server : %s\n", client->server, client->mount_port);
+ exit(10);
+ }
+
+ printf("Disconnect socket from portmap server\n");
+ if (rpc_disconnect(rpc, "normal disconnect") != 0) {
+ printf("Failed to disconnect socket to portmapper\n");
+ exit(10);
+ }
+
+ printf("Connect to RPC.MOUNTD on %s:%d\n", client->server, client->mount_port);
+ if (rpc_connect_async(rpc, client->server, client->mount_port, 1, mount_connect_cb, client) != 0) {
+ printf("Failed to start connection\n");
+ exit(10);
+ }
+}
+
+
+void pmap_null_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
+{
+ struct client *client = private_data;
+
+ if (status == RPC_STATUS_ERROR) {
+ printf("portmapper null call failed with \"%s\"\n", (char *)data);
+ exit(10);
+ }
+ if (status != RPC_STATUS_SUCCESS) {
+ printf("portmapper null call to server %s failed, status:%d\n", client->server, status);
+ exit(10);
+ }
+
+ printf("Got reply from server for PORTMAP/NULL procedure.\n");
+ printf("Send getport request asking for MOUNT port\n");
+ if (rpc_pmap_getport_async(rpc, MOUNT_PROGRAM, MOUNT_V3, pmap_getport_cb, client) != 0) {
+ printf("Failed to send getport request\n");
+ exit(10);
+ }
+}
+
+void pmap_connect_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
+{
+ struct client *client = private_data;
+
+ printf("pmap_connect_cb status:%d.\n", status);
+ if (status != RPC_STATUS_SUCCESS) {
+ printf("connection to portmapper on server %s failed\n", client->server);
+ exit(10);
+ }
+
+ printf("Send NULL request to check if portmapper is actually running\n");
+ if (rpc_pmap_null_async(rpc, pmap_null_cb, client) != 0) {
+ printf("Failed to send null request\n");
+ exit(10);
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct rpc_context *rpc;
+ struct pollfd pfd;
+ int ret;
+ struct client client;
+
+ rpc = rpc_init_context();
+ if (rpc == NULL) {
+ printf("failed to init context\n");
+ exit(10);
+ }
+
+ client.server = SERVER;
+ client.export = EXPORT;
+ client.is_finished = 0;
+ if (rpc_connect_async(rpc, client.server, 111, 0, pmap_connect_cb, &client) != 0) {
+ printf("Failed to start connection\n");
+ exit(10);
+ }
+
+ for (;;) {
+ pfd.fd = rpc_get_fd(rpc);
+ pfd.events = rpc_which_events(rpc);
+
+ if (poll(&pfd, 1, -1) < 0) {
+ printf("Poll failed");
+ exit(10);
+ }
+ if (rpc_service(rpc, pfd.revents) < 0) {
+ printf("rpc_service failed\n");
+ break;
+ }
+ if (client.is_finished) {
+ break;
+ }
+ }
+
+ rpc_destroy_context(rpc);
+ rpc=NULL;
+ printf("nfsclient finished\n");
+ return 0;
+}
--- /dev/null
+/*
+ Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Example program using the highlevel sync interface
+ */
+
+#define SERVER "10.1.1.27"
+#define EXPORT "/VIRTUAL"
+#define NFSFILE "/BOOKS/Classics/Dracula.djvu"
+#define NFSFILER "/BOOKS/Classics/Dracula.djvu.renamed"
+#define NFSFILEW "/BOOKS/Classics/foo"
+#define NFSDIR "/BOOKS/Classics/"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ccan/nfs/nfs.h>
+#include <rpc/rpc.h> /* for authunix_create() */
+
+struct client {
+ char *server;
+ char *export;
+ uint32_t mount_port;
+ int is_finished;
+};
+
+
+int main(int argc, char *argv[])
+{
+ struct nfs_context *nfs;
+ int i, ret;
+ struct client client;
+ struct stat st;
+ struct nfsfh *nfsfh;
+ struct nfsdir *nfsdir;
+ struct nfsdirent *nfsdirent;
+ client.server = SERVER;
+ client.export = EXPORT;
+ client.is_finished = 0;
+ char buf[16];
+ off_t offset;
+ struct statvfs svfs;
+
+ nfs = nfs_init_context();
+ if (nfs == NULL) {
+ printf("failed to init context\n");
+ exit(10);
+ }
+
+ ret = nfs_mount_sync(nfs, client.server, client.export);
+ if (ret != 0) {
+ printf("Failed to mount nfs share : %s\n", nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("mounted share successfully\n");
+exit(10);
+
+ ret = nfs_stat_sync(nfs, NFSFILE, &st);
+ if (ret != 0) {
+ printf("Failed to stat(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("Mode %04o\n", st.st_mode);
+ printf("Size %d\n", (int)st.st_size);
+ printf("Inode %04o\n", (int)st.st_ino);
+
+ ret = nfs_open_sync(nfs, NFSFILE, O_RDONLY, &nfsfh);
+ if (ret != 0) {
+ printf("Failed to open(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+
+ ret = nfs_read_sync(nfs, nfsfh, 16, buf);
+ if (ret < 0) {
+ printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("read %d bytes\n", ret);
+ for (i=0;i<16;i++) {
+ printf("%02x ", buf[i]&0xff);
+ }
+ printf("\n");
+ ret = nfs_read_sync(nfs, nfsfh, 16, buf);
+ if (ret < 0) {
+ printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("read %d bytes\n", ret);
+ for (i=0;i<16;i++) {
+ printf("%02x ", buf[i]&0xff);
+ }
+ printf("\n");
+
+ ret = (int)nfs_lseek_sync(nfs, nfsfh, 0, SEEK_CUR, &offset);
+ if (ret < 0) {
+ printf("Failed to lseek(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("File position is %d\n", (int)offset);
+
+ printf("seek to end of file\n");
+ ret = (int)nfs_lseek_sync(nfs, nfsfh, 0, SEEK_END, &offset);
+ if (ret < 0) {
+ printf("Failed to lseek(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("File position is %d\n", (int)offset);
+
+ ret = nfs_fstat_sync(nfs, nfsfh, &st);
+ if (ret != 0) {
+ printf("Failed to stat(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("Mode %04o\n", st.st_mode);
+ printf("Size %d\n", (int)st.st_size);
+ printf("Inode %04o\n", (int)st.st_ino);
+
+
+ ret = nfs_close_sync(nfs, nfsfh);
+ if (ret < 0) {
+ printf("Failed to close(%s)\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+
+ ret = nfs_opendir_sync(nfs, NFSDIR, &nfsdir);
+ if (ret != 0) {
+ printf("Failed to open(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ while((nfsdirent = nfs_readdir(nfs, nfsdir)) != NULL) {
+ printf("Inode:%d Name:%s\n", (int)nfsdirent->inode, nfsdirent->name);
+ }
+ nfs_closedir(nfs, nfsdir);
+
+
+ ret = nfs_open_sync(nfs, NFSFILEW, O_WRONLY, &nfsfh);
+ if (ret != 0) {
+ printf("Failed to open(%s) %s\n", NFSFILEW, nfs_get_error(nfs));
+ exit(10);
+ }
+ ret = nfs_pwrite_sync(nfs, nfsfh, 0, 16, buf);
+ if (ret < 0) {
+ printf("Failed to pwrite(%s) %s\n", NFSFILEW, nfs_get_error(nfs));
+ exit(10);
+ }
+ ret = nfs_fsync_sync(nfs, nfsfh);
+ if (ret < 0) {
+ printf("Failed to fsync(%s) %s\n", NFSFILEW, nfs_get_error(nfs));
+ exit(10);
+ }
+ ret = nfs_close_sync(nfs, nfsfh);
+ if (ret < 0) {
+ printf("Failed to close(%s)\n", NFSFILEW, nfs_get_error(nfs));
+ exit(10);
+ }
+
+
+ ret = nfs_statvfs_sync(nfs, NFSDIR, &svfs);
+ if (ret < 0) {
+ printf("Failed to statvfs(%s)\n", NFSDIR, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("files %d/%d/%d\n", (int)svfs.f_files, (int)svfs.f_ffree, (int)svfs.f_favail);
+
+
+ ret = nfs_access_sync(nfs, NFSFILE, R_OK);
+ if (ret != 0) {
+ printf("Failed to access(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ }
+
+ /* become root */
+ nfs_set_auth(nfs, authunix_create("Ronnies-Laptop", 0, 0, 0, NULL));
+
+ ret = nfs_link_sync(nfs, NFSFILE, NFSFILER);
+ if (ret != 0) {
+ printf("Failed to link(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ }
+
+
+ nfs_destroy_context(nfs);
+ printf("nfsclient finished\n");
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * rbtree - talloc-aware Red Black Tree
+ *
+ * This is an implementation of a red-black tree based on talloc.
+ * Talloc objects that are stored in the tree have nice properties
+ * such as when the object is talloc_free()d, the object is also
+ * automatically removed from the tree. This is done by making the
+ * nodes of the tree child objects of the talloc object stored in the
+ * tree, so that destructors are called to automatically remove the
+ * node from the tree.
+ *
+ * The object stored in the tree does NOT become a child object of the
+ * tree itself, so the same object can be stored under several keys at
+ * the same time, and even in several different trees at the same
+ * time.
+ *
+ * The example below is a trivial example program that shows how to
+ * use trees that are keyed by a uint32_t. The rb_tree code also contains
+ * support for managing trees that are keyed by an array of uint32. It
+ * is trivial to expand this to "key as string". Just pad the string with
+ * 0 to be a multiple of uint32_t and then chop it up as an array of
+ * uint32_t.
+ *
+ * This code originates from ctdb, where talloc based trees keyed are
+ * used in several places.
+ *
+ * License: GPL (3 or any later version)
+ * Author: Ronnie Sahlberg <ronniesahlberg@gmail.com>
+ *
+ * Example:
+ * #include <stdio.h>
+ * #include <ccan/talloc/talloc.h>
+ * #include <ccan/rbtree/rbtree.h>
+ *
+ * static void printtree(trbt_node_t *node, int levels)
+ * {
+ * int i;
+ * if(node==NULL)return;
+ * printtree(node->left, levels+1);
+ * for(i=0;i<levels;i++)printf(" ");
+ * printf("key:%d COLOR:%s\n",
+ * node->key32, node->rb_color==TRBT_BLACK?"BLACK":"RED");
+ * printtree(node->right, levels+1);
+ * }
+ *
+ * static void print_tree(trbt_tree_t *tree)
+ * {
+ * if(tree->root==NULL){
+ * printf("tree is empty\n");
+ * return;
+ * }
+ * printf("---\n");
+ * printtree(tree->root->left, 1);
+ * printf("key:%d COLOR:%s\n", tree->root->key32,
+ * tree->root->rb_color==TRBT_BLACK?"BLACK":"RED");
+ * printtree(tree->root->right, 1);
+ * printf("===\n");
+ * }
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * TALLOC_CTX *mem_ctx;
+ * TALLOC_CTX *val;
+ * int i;
+ *
+ * trbt_tree_t *tree;
+ *
+ * printf("Example of tree keyed by UINT32\n");
+ * mem_ctx = talloc_new(NULL);
+ *
+ * // create a tree and store some talloc objects there
+ * tree=trbt_create(mem_ctx, 0);
+ * for (i=0; i<10; i++) {
+ * val = talloc_asprintf(mem_ctx,
+ * "Value string for key %d", i);
+ * trbt_insert32(tree, i, val);
+ * }
+ * // show what the tree looks like
+ * print_tree(tree);
+ *
+ * printf("Lookup item with key 7\n");
+ * val = trbt_lookup32(tree, 7);
+ * printf("Item with key:7 has value:%s\n", (char *)val);
+ * printf("Talloc_free this item\n");
+ * talloc_free(val);
+ * printf("Item is automagically removed from the tree\n");
+ * print_tree(tree);
+ *
+ * talloc_free(mem_ctx);
+ * return 0;
+ * }
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/talloc\n");
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+/*
+ a talloc based red-black tree
+
+ Copyright (C) Ronnie Sahlberg 2007
+
+ 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 <ccan/rbtree/rbtree.h>
+
+static void
+tree_destructor_traverse_node(TALLOC_CTX *mem_ctx, trbt_node_t *node)
+{
+ talloc_set_destructor(node, NULL);
+ if (node->left) {
+ tree_destructor_traverse_node(mem_ctx, node->left);
+ }
+ if (node->right) {
+ tree_destructor_traverse_node(mem_ctx, node->right);
+ }
+ talloc_steal(mem_ctx, node);
+}
+
+/*
+ destroy a tree and remove all its nodes
+ */
+static int tree_destructor(trbt_tree_t *tree)
+{
+ TALLOC_CTX *tmp_ctx;
+ trbt_node_t *node;
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ node=tree->root;
+ if (node == NULL) {
+ return 0;
+ }
+
+ /* traverse the tree and remove the node destructor and steal
+ the node to the temporary context.
+ we dont want to use the existing destructor for the node
+ since that will remove the nodes one by one from the tree.
+ since the entire tree will be completely destroyed we dont care
+ if it is inconsistent or unbalanced while freeing the
+ individual nodes
+ */
+ tmp_ctx = talloc_new(NULL);
+ tree_destructor_traverse_node(tmp_ctx, node);
+ talloc_free(tmp_ctx);
+
+ return 0;
+}
+
+
+/* create a red black tree */
+trbt_tree_t *
+trbt_create(TALLOC_CTX *memctx, uint32_t flags)
+{
+ trbt_tree_t *tree;
+
+ tree = talloc_zero(memctx, trbt_tree_t);
+ if (tree == NULL) {
+ fprintf(stderr, "Failed to allocate memory for rb tree\n");
+ return NULL;
+ }
+
+ /* If the tree is freed, we must walk over all entries and steal the
+ node from the stored data pointer and release the node.
+ Note, when we free the tree we only free the tree and not any of
+ the data stored in the tree.
+ */
+ talloc_set_destructor(tree, tree_destructor);
+ tree->flags = flags;
+
+ return tree;
+}
+
+static inline trbt_node_t *
+trbt_parent(trbt_node_t *node)
+{
+ return node->parent;
+}
+
+static inline trbt_node_t *
+trbt_grandparent(trbt_node_t *node)
+{
+ trbt_node_t *parent;
+
+ parent=trbt_parent(node);
+ if(parent){
+ return parent->parent;
+ }
+ return NULL;
+}
+
+static inline trbt_node_t *
+trbt_uncle(trbt_node_t *node)
+{
+ trbt_node_t *parent, *grandparent;
+
+ parent=trbt_parent(node);
+ if(!parent){
+ return NULL;
+ }
+ grandparent=trbt_parent(parent);
+ if(!grandparent){
+ return NULL;
+ }
+ if(parent==grandparent->left){
+ return grandparent->right;
+ }
+ return grandparent->left;
+}
+
+
+static inline void trbt_insert_case1(trbt_tree_t *tree, trbt_node_t *node);
+static inline void trbt_insert_case2(trbt_tree_t *tree, trbt_node_t *node);
+
+static inline void
+trbt_rotate_left(trbt_node_t *node)
+{
+ trbt_tree_t *tree = node->tree;
+
+ if(node->parent){
+ if(node->parent->left==node){
+ node->parent->left=node->right;
+ } else {
+ node->parent->right=node->right;
+ }
+ } else {
+ tree->root=node->right;
+ }
+ node->right->parent=node->parent;
+ node->parent=node->right;
+ node->right=node->right->left;
+ if(node->right){
+ node->right->parent=node;
+ }
+ node->parent->left=node;
+}
+
+static inline void
+trbt_rotate_right(trbt_node_t *node)
+{
+ trbt_tree_t *tree = node->tree;
+
+ if(node->parent){
+ if(node->parent->left==node){
+ node->parent->left=node->left;
+ } else {
+ node->parent->right=node->left;
+ }
+ } else {
+ tree->root=node->left;
+ }
+ node->left->parent=node->parent;
+ node->parent=node->left;
+ node->left=node->left->right;
+ if(node->left){
+ node->left->parent=node;
+ }
+ node->parent->right=node;
+}
+
+/* NULL nodes are black by definition */
+static inline int trbt_get_color(trbt_node_t *node)
+{
+ if (node==NULL) {
+ return TRBT_BLACK;
+ }
+ return node->rb_color;
+}
+static inline int trbt_get_color_left(trbt_node_t *node)
+{
+ if (node==NULL) {
+ return TRBT_BLACK;
+ }
+ if (node->left==NULL) {
+ return TRBT_BLACK;
+ }
+ return node->left->rb_color;
+}
+static inline int trbt_get_color_right(trbt_node_t *node)
+{
+ if (node==NULL) {
+ return TRBT_BLACK;
+ }
+ if (node->right==NULL) {
+ return TRBT_BLACK;
+ }
+ return node->right->rb_color;
+}
+/* setting a NULL node to black is a nop */
+static inline void trbt_set_color(trbt_node_t *node, int color)
+{
+ if ( (node==NULL) && (color==TRBT_BLACK) ) {
+ return;
+ }
+ node->rb_color = color;
+}
+static inline void trbt_set_color_left(trbt_node_t *node, int color)
+{
+ if ( ((node==NULL)||(node->left==NULL)) && (color==TRBT_BLACK) ) {
+ return;
+ }
+ node->left->rb_color = color;
+}
+static inline void trbt_set_color_right(trbt_node_t *node, int color)
+{
+ if ( ((node==NULL)||(node->right==NULL)) && (color==TRBT_BLACK) ) {
+ return;
+ }
+ node->right->rb_color = color;
+}
+
+static inline void
+trbt_insert_case5(trbt_node_t *node)
+{
+ trbt_node_t *grandparent;
+ trbt_node_t *parent;
+
+ parent=trbt_parent(node);
+ grandparent=trbt_parent(parent);
+ parent->rb_color=TRBT_BLACK;
+ grandparent->rb_color=TRBT_RED;
+ if( (node==parent->left) && (parent==grandparent->left) ){
+ trbt_rotate_right(grandparent);
+ } else {
+ trbt_rotate_left(grandparent);
+ }
+}
+
+static inline void
+trbt_insert_case4(trbt_node_t *node)
+{
+ trbt_node_t *grandparent;
+ trbt_node_t *parent;
+
+ parent=trbt_parent(node);
+ grandparent=trbt_parent(parent);
+ if(!grandparent){
+ return;
+ }
+ if( (node==parent->right) && (parent==grandparent->left) ){
+ trbt_rotate_left(parent);
+ node=node->left;
+ } else if( (node==parent->left) && (parent==grandparent->right) ){
+ trbt_rotate_right(parent);
+ node=node->right;
+ }
+ trbt_insert_case5(node);
+}
+
+static inline void
+trbt_insert_case3(trbt_tree_t *tree, trbt_node_t *node)
+{
+ trbt_node_t *grandparent;
+ trbt_node_t *parent;
+ trbt_node_t *uncle;
+
+ uncle=trbt_uncle(node);
+ if(uncle && (uncle->rb_color==TRBT_RED)){
+ parent=trbt_parent(node);
+ parent->rb_color=TRBT_BLACK;
+ uncle->rb_color=TRBT_BLACK;
+ grandparent=trbt_grandparent(node);
+ grandparent->rb_color=TRBT_RED;
+ trbt_insert_case1(tree, grandparent);
+ } else {
+ trbt_insert_case4(node);
+ }
+}
+
+static inline void
+trbt_insert_case2(trbt_tree_t *tree, trbt_node_t *node)
+{
+ trbt_node_t *parent;
+
+ parent=trbt_parent(node);
+ /* parent is always non-NULL here */
+ if(parent->rb_color==TRBT_BLACK){
+ return;
+ }
+ trbt_insert_case3(tree, node);
+}
+
+static inline void
+trbt_insert_case1(trbt_tree_t *tree, trbt_node_t *node)
+{
+ trbt_node_t *parent;
+
+ parent=trbt_parent(node);
+ if(!parent){
+ node->rb_color=TRBT_BLACK;
+ return;
+ }
+ trbt_insert_case2(tree, node);
+}
+
+static inline trbt_node_t *
+trbt_sibling(trbt_node_t *node)
+{
+ trbt_node_t *parent;
+
+ parent=trbt_parent(node);
+ if(!parent){
+ return NULL;
+ }
+
+ if (node == parent->left) {
+ return parent->right;
+ } else {
+ return parent->left;
+ }
+}
+
+static inline void
+trbt_delete_case6(trbt_node_t *node)
+{
+ trbt_node_t *sibling, *parent;
+
+ sibling = trbt_sibling(node);
+ parent = trbt_parent(node);
+
+ trbt_set_color(sibling, parent->rb_color);
+ trbt_set_color(parent, TRBT_BLACK);
+ if (node == parent->left) {
+ trbt_set_color_right(sibling, TRBT_BLACK);
+ trbt_rotate_left(parent);
+ } else {
+ trbt_set_color_left(sibling, TRBT_BLACK);
+ trbt_rotate_right(parent);
+ }
+}
+
+
+static inline void
+trbt_delete_case5(trbt_node_t *node)
+{
+ trbt_node_t *parent, *sibling;
+
+ parent = trbt_parent(node);
+ sibling = trbt_sibling(node);
+ if ( (node == parent->left)
+ &&(trbt_get_color(sibling) == TRBT_BLACK)
+ &&(trbt_get_color_left(sibling) == TRBT_RED)
+ &&(trbt_get_color_right(sibling) == TRBT_BLACK) ){
+ trbt_set_color(sibling, TRBT_RED);
+ trbt_set_color_left(sibling, TRBT_BLACK);
+ trbt_rotate_right(sibling);
+ trbt_delete_case6(node);
+ return;
+ }
+ if ( (node == parent->right)
+ &&(trbt_get_color(sibling) == TRBT_BLACK)
+ &&(trbt_get_color_right(sibling) == TRBT_RED)
+ &&(trbt_get_color_left(sibling) == TRBT_BLACK) ){
+ trbt_set_color(sibling, TRBT_RED);
+ trbt_set_color_right(sibling, TRBT_BLACK);
+ trbt_rotate_left(sibling);
+ trbt_delete_case6(node);
+ return;
+ }
+
+ trbt_delete_case6(node);
+}
+
+static inline void
+trbt_delete_case4(trbt_node_t *node)
+{
+ trbt_node_t *sibling;
+
+ sibling = trbt_sibling(node);
+ if ( (trbt_get_color(node->parent) == TRBT_RED)
+ &&(trbt_get_color(sibling) == TRBT_BLACK)
+ &&(trbt_get_color_left(sibling) == TRBT_BLACK)
+ &&(trbt_get_color_right(sibling) == TRBT_BLACK) ){
+ trbt_set_color(sibling, TRBT_RED);
+ trbt_set_color(node->parent, TRBT_BLACK);
+ } else {
+ trbt_delete_case5(node);
+ }
+}
+
+static void trbt_delete_case1(trbt_node_t *node);
+
+static inline void
+trbt_delete_case3(trbt_node_t *node)
+{
+ trbt_node_t *sibling;
+
+ sibling = trbt_sibling(node);
+ if ( (trbt_get_color(node->parent) == TRBT_BLACK)
+ &&(trbt_get_color(sibling) == TRBT_BLACK)
+ &&(trbt_get_color_left(sibling) == TRBT_BLACK)
+ &&(trbt_get_color_right(sibling) == TRBT_BLACK) ){
+ trbt_set_color(sibling, TRBT_RED);
+ trbt_delete_case1(node->parent);
+ } else {
+ trbt_delete_case4(node);
+ }
+}
+
+static inline void
+trbt_delete_case2(trbt_node_t *node)
+{
+ trbt_node_t *sibling;
+
+ sibling = trbt_sibling(node);
+ if (trbt_get_color(sibling) == TRBT_RED) {
+ trbt_set_color(node->parent, TRBT_RED);
+ trbt_set_color(sibling, TRBT_BLACK);
+ if (node == node->parent->left) {
+ trbt_rotate_left(node->parent);
+ } else {
+ trbt_rotate_right(node->parent);
+ }
+ }
+ trbt_delete_case3(node);
+}
+
+static void
+trbt_delete_case1(trbt_node_t *node)
+{
+ if (!node->parent) {
+ return;
+ } else {
+ trbt_delete_case2(node);
+ }
+}
+
+static void
+delete_node(trbt_node_t *node, int from_destructor)
+{
+ trbt_node_t *parent, *child, dc;
+ trbt_node_t *temp = NULL;
+
+ /* This node has two child nodes, then just copy the content
+ from the next smaller node with this node and delete the
+ predecessor instead.
+ The predecessor is guaranteed to have at most one child
+ node since its right arm must be NULL
+ (It must be NULL since we are its sucessor and we are above
+ it in the tree)
+ */
+ if (node->left != NULL && node->right != NULL) {
+ /* This node has two children, just copy the data */
+ /* find the predecessor */
+ temp = node->left;
+
+ while (temp->right != NULL) {
+ temp = temp->right;
+ }
+
+ /* swap the predecessor data and key with the node to
+ be deleted.
+ */
+ node->key32 = temp->key32;
+ node->data = temp->data;
+ /* now we let node hang off the new data */
+ talloc_steal(node->data, node);
+
+ temp->data = NULL;
+ temp->key32 = -1;
+ /* then delete the temp node.
+ this node is guaranteed to have at least one leaf
+ child */
+ delete_node(temp, from_destructor);
+ goto finished;
+ }
+
+
+ /* There is at most one child to this node to be deleted */
+ child = node->left;
+ if (node->right) {
+ child = node->right;
+ }
+
+ /* If the node to be deleted did not have any child at all we
+ create a temporary dummy node for the child and mark it black.
+ Once the delete of the node is finished, we remove this dummy
+ node, which is simple to do since it is guaranteed that it will
+ still not have any children after the delete operation.
+ This is because we dont represent the leaf-nodes as actual nodes
+ in this implementation.
+ */
+ if (!child) {
+ child = &dc;
+ child->tree = node->tree;
+ child->left=NULL;
+ child->right=NULL;
+ child->rb_color=TRBT_BLACK;
+ child->data=NULL;
+ }
+
+ /* replace node with child */
+ parent = trbt_parent(node);
+ if (parent) {
+ if (parent->left == node) {
+ parent->left = child;
+ } else {
+ parent->right = child;
+ }
+ } else {
+ node->tree->root = child;
+ }
+ child->parent = node->parent;
+
+
+ if (node->rb_color == TRBT_BLACK) {
+ if (trbt_get_color(child) == TRBT_RED) {
+ child->rb_color = TRBT_BLACK;
+ } else {
+ trbt_delete_case1(child);
+ }
+ }
+
+ /* If we had to create a temporary dummy node to represent a black
+ leaf child we now has to delete it.
+ This is simple since this dummy node originally had no children
+ and we are guaranteed that it will also not have any children
+ after the node has been deleted and any possible rotations
+ have occured.
+
+ The only special case is if this was the last node of the tree
+ in which case we have to reset the root to NULL as well.
+ Othervise it is enough to just unlink the child from its new
+ parent.
+ */
+ if (child == &dc) {
+ if (child->parent == NULL) {
+ node->tree->root = NULL;
+ } else if (child == child->parent->left) {
+ child->parent->left = NULL;
+ } else {
+ child->parent->right = NULL;
+ }
+ }
+
+finished:
+ if (!from_destructor) {
+ talloc_free(node);
+ }
+
+ /* if we came from a destructor and temp!=NULL this means we
+ did the node-swap but now the tree still contains the old
+ node which was freed in the destructor. Not good.
+ */
+ if (from_destructor && temp) {
+ temp->key32 = node->key32;
+ temp->rb_color = node->rb_color;
+
+ temp->data = node->data;
+ talloc_steal(temp->data, temp);
+
+ temp->parent = node->parent;
+ if (temp->parent) {
+ if (temp->parent->left == node) {
+ temp->parent->left = temp;
+ } else {
+ temp->parent->right = temp;
+ }
+ }
+
+ temp->left = node->left;
+ if (temp->left) {
+ temp->left->parent = temp;
+ }
+ temp->right = node->right;
+ if (temp->right) {
+ temp->right->parent = temp;
+ }
+
+ if (temp->tree->root == node) {
+ temp->tree->root = temp;
+ }
+ }
+
+ if ( (node->tree->flags & TRBT_AUTOFREE)
+ && (node->tree->root == NULL) ) {
+ talloc_free(node->tree);
+ }
+
+ return;
+}
+
+/*
+ destroy a node and remove it from its tree
+ */
+static int node_destructor(trbt_node_t *node)
+{
+ delete_node(node, 1);
+
+ return 0;
+}
+
+static inline trbt_node_t *
+trbt_create_node(trbt_tree_t *tree, trbt_node_t *parent, uint32_t key, void *data)
+{
+ trbt_node_t *node;
+
+ node=talloc_zero(tree, trbt_node_t);
+ if (node == NULL) {
+ fprintf(stderr, "Failed to allocate memory for rb node\n");
+ return NULL;
+ }
+
+ node->tree=tree;
+ node->rb_color=TRBT_BLACK;
+ node->parent=parent;
+ node->left=NULL;
+ node->right=NULL;
+ node->key32=key;
+ node->data = data;
+
+ /* let this node hang off data so that it is removed when
+ data is freed
+ */
+ talloc_steal(data, node);
+ talloc_set_destructor(node, node_destructor);
+
+ return node;
+}
+
+/* insert a new node in the tree.
+ if there is already a node with a matching key in the tree
+ we replace it with the new data and return a pointer to the old data
+ in case the caller wants to take any special action
+ */
+void *
+trbt_insert32(trbt_tree_t *tree, uint32_t key, void *data)
+{
+ trbt_node_t *node;
+
+ node=tree->root;
+
+ /* is this the first node ?*/
+ if(!node){
+ node = trbt_create_node(tree, NULL, key, data);
+
+ tree->root=node;
+ return NULL;
+ }
+
+ /* it was not the new root so walk the tree until we find where to
+ * insert this new leaf.
+ */
+ while(1){
+ /* this node already exists, replace data and return the
+ old data
+ */
+ if(key==node->key32){
+ void *old_data;
+
+ old_data = node->data;
+ node->data = data;
+ /* Let the node now be owned by the new data
+ so the node is freed when the enw data is released
+ */
+ talloc_steal(node->data, node);
+
+ return old_data;
+ }
+ if(key<node->key32) {
+ if(!node->left){
+ /* new node to the left */
+ trbt_node_t *new_node;
+
+ new_node = trbt_create_node(tree, node, key, data);
+ node->left=new_node;
+ node=new_node;
+
+ break;
+ }
+ node=node->left;
+ continue;
+ }
+ if(key>node->key32) {
+ if(!node->right){
+ /* new node to the right */
+ trbt_node_t *new_node;
+
+ new_node = trbt_create_node(tree, node, key, data);
+ node->right=new_node;
+ node=new_node;
+ break;
+ }
+ node=node->right;
+ continue;
+ }
+ }
+
+ /* node will now point to the newly created node */
+ node->rb_color=TRBT_RED;
+ trbt_insert_case1(tree, node);
+ return NULL;
+}
+
+void *
+trbt_lookup32(trbt_tree_t *tree, uint32_t key)
+{
+ trbt_node_t *node;
+
+ node=tree->root;
+
+ while(node){
+ if(key==node->key32){
+ return node->data;
+ }
+ if(key<node->key32){
+ node=node->left;
+ continue;
+ }
+ if(key>node->key32){
+ node=node->right;
+ continue;
+ }
+ }
+ return NULL;
+}
+
+
+/* This deletes a node from the tree.
+ Note that this does not release the data that the node points to
+*/
+void
+trbt_delete32(trbt_tree_t *tree, uint32_t key)
+{
+ trbt_node_t *node;
+
+ node=tree->root;
+
+ while(node){
+ if(key==node->key32){
+ delete_node(node, 0);
+ return;
+ }
+ if(key<node->key32){
+ node=node->left;
+ continue;
+ }
+ if(key>node->key32){
+ node=node->right;
+ continue;
+ }
+ }
+}
+
+
+void
+trbt_insert32_callback(trbt_tree_t *tree, uint32_t key, void *(*callback)(void *param, void *data), void *param)
+{
+ trbt_node_t *node;
+
+ node=tree->root;
+
+ /* is this the first node ?*/
+ if(!node){
+ node = trbt_create_node(tree, NULL, key,
+ callback(param, NULL));
+
+ tree->root=node;
+ return;
+ }
+
+ /* it was not the new root so walk the tree until we find where to
+ * insert this new leaf.
+ */
+ while(1){
+ /* this node already exists, replace it
+ */
+ if(key==node->key32){
+ node->data = callback(param, node->data);
+ talloc_steal(node->data, node);
+
+ return;
+ }
+ if(key<node->key32) {
+ if(!node->left){
+ /* new node to the left */
+ trbt_node_t *new_node;
+
+ new_node = trbt_create_node(tree, node, key,
+ callback(param, NULL));
+ node->left=new_node;
+ node=new_node;
+
+ break;
+ }
+ node=node->left;
+ continue;
+ }
+ if(key>node->key32) {
+ if(!node->right){
+ /* new node to the right */
+ trbt_node_t *new_node;
+
+ new_node = trbt_create_node(tree, node, key,
+ callback(param, NULL));
+ node->right=new_node;
+ node=new_node;
+ break;
+ }
+ node=node->right;
+ continue;
+ }
+ }
+
+ /* node will now point to the newly created node */
+ node->rb_color=TRBT_RED;
+ trbt_insert_case1(tree, node);
+ return;
+}
+
+
+struct trbt_array_param {
+ void *(*callback)(void *param, void *data);
+ void *param;
+ uint32_t keylen;
+ uint32_t *key;
+ trbt_tree_t *tree;
+};
+static void *array_insert_callback(void *p, void *data)
+{
+ struct trbt_array_param *param = (struct trbt_array_param *)p;
+ trbt_tree_t *tree = NULL;
+
+
+ /* if keylen has reached 0 we are done and can call the users
+ callback function with the users parameters
+ */
+ if (param->keylen == 0) {
+ return param->callback(param->param, data);
+ }
+
+
+ /* keylen is not zero yes so we must create/process more subtrees */
+ /* if data is NULL this means we did not yet have a subtree here
+ and we must create one.
+ */
+ if (data == NULL) {
+ /* create a new subtree and hang it off our current tree
+ set it to autofree so that the tree is freed when
+ the last node in it has been released.
+ */
+ tree = trbt_create(param->tree, TRBT_AUTOFREE);
+ } else {
+ /* we already have a subtree for this path */
+ tree = (trbt_tree_t *)data;
+ }
+
+ trbt_insertarray32_callback(tree, param->keylen, param->key, param->callback, param->param);
+
+ /* now return either the old tree we got in *data or the new tree
+ we created to our caller so he can update his pointer in his
+ tree to point to our subtree
+ */
+ return tree;
+}
+
+
+
+/* insert into the tree using an array of uint32 as a key */
+void
+trbt_insertarray32_callback(trbt_tree_t *tree, uint32_t keylen, uint32_t *key, void *(*cb)(void *param, void *data), void *pm)
+{
+ struct trbt_array_param tap;
+
+ /* keylen-1 and key[1] since the call to insert32 will consume the
+ first part of the key.
+ */
+ tap.callback= cb;
+ tap.param = pm;
+ tap.keylen = keylen-1;
+ tap.key = &key[1];
+ tap.tree = tree;
+
+ trbt_insert32_callback(tree, key[0], array_insert_callback, &tap);
+}
+
+/* lookup the tree using an array of uint32 as a key */
+void *
+trbt_lookuparray32(trbt_tree_t *tree, uint32_t keylen, uint32_t *key)
+{
+ /* if keylen is 1 we can do a regular lookup and return this to the
+ user
+ */
+ if (keylen == 1) {
+ return trbt_lookup32(tree, key[0]);
+ }
+
+ /* we need to lookup the next subtree */
+ tree = trbt_lookup32(tree, key[0]);
+ if (tree == NULL) {
+ /* the key does not exist, return NULL */
+ return NULL;
+ }
+
+ /* now lookup the next part of the key in our new tree */
+ return trbt_lookuparray32(tree, keylen-1, &key[1]);
+}
+
+
+/* traverse a tree starting at node */
+static void
+trbt_traversearray32_node(trbt_node_t *node, uint32_t keylen,
+ void (*callback)(void *param, void *data),
+ void *param)
+{
+ if (node->left) {
+ trbt_traversearray32_node(node->left, keylen, callback, param);
+ }
+
+ /* this is the smallest node in this subtree
+ if keylen is 0 this means we can just call the callback
+ otherwise we must pull the next subtree and traverse that one as well
+ */
+ if (keylen == 0) {
+ callback(param, node->data);
+ } else {
+ trbt_traversearray32(node->data, keylen, callback, param);
+ }
+
+ if (node->right) {
+ trbt_traversearray32_node(node->right, keylen, callback, param);
+ }
+}
+
+
+/* traverse the tree using an array of uint32 as a key */
+void
+trbt_traversearray32(trbt_tree_t *tree, uint32_t keylen,
+ void (*callback)(void *param, void *data),
+ void *param)
+{
+ trbt_node_t *node;
+
+ if (tree == NULL) {
+ return;
+ }
+
+ node=tree->root;
+ if (node == NULL) {
+ return;
+ }
+
+ trbt_traversearray32_node(node, keylen-1, callback, param);
+}
+
+
+/* this function will return the first node in a tree where
+ the key is an array of uint32_t
+*/
+void *
+trbt_findfirstarray32(trbt_tree_t *tree, uint32_t keylen)
+{
+ trbt_node_t *node;
+
+ if (keylen < 1) {
+ return NULL;
+ }
+
+ if (tree == NULL) {
+ return NULL;
+ }
+
+ node=tree->root;
+ if (node == NULL) {
+ return NULL;
+ }
+
+ while (node->left) {
+ node = node->left;
+ }
+
+ /* we found our node so return the data */
+ if (keylen == 1) {
+ return node->data;
+ }
+
+ /* we are still traversing subtrees so find the first node in the
+ next level of trees
+ */
+ return trbt_findfirstarray32(node->data, keylen-1);
+}
+
+
--- /dev/null
+/*
+ a talloc based red-black tree
+
+ Copyright (C) Ronnie Sahlberg 2007
+
+ 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_RBTREE_H
+#define CCAN_RBTREE_H
+#include <stdint.h>
+#include <ccan/talloc/talloc.h>
+
+#define TRBT_RED 0x00
+#define TRBT_BLACK 0x01
+typedef struct trbt_node {
+ struct trbt_tree *tree;
+ struct trbt_node *parent;
+ struct trbt_node *left;
+ struct trbt_node *right;
+ uint32_t rb_color;
+ uint32_t key32;
+ void *data;
+} trbt_node_t;
+
+typedef struct trbt_tree {
+ trbt_node_t *root;
+/* automatically free the tree when the last node has been deleted */
+#define TRBT_AUTOFREE 0x00000001
+ uint32_t flags;
+} trbt_tree_t;
+
+
+
+/* Create a RB tree */
+trbt_tree_t *trbt_create(TALLOC_CTX *memctx, uint32_t flags);
+
+/* Lookup a node in the tree and return a pointer to data or NULL */
+void *trbt_lookup32(trbt_tree_t *tree, uint32_t key);
+
+/* Insert a new node into the tree. If there was already a node with this
+ key the pointer to the previous data is returned.
+ The tree will talloc_steal() the data inserted into the tree .
+*/
+void *trbt_insert32(trbt_tree_t *tree, uint32_t key, void *data);
+
+/* Insert a new node into the tree.
+ If this is a new node:
+ callback is called with data==NULL and param=param
+ the returned value from the callback is talloc_stolen and inserted in the
+ tree.
+ If a node already exists for this key then:
+ callback is called with data==existing data and param=param
+ the returned calue is talloc_stolen and inserted in the tree
+*/
+void trbt_insert32_callback(trbt_tree_t *tree, uint32_t key, void *(*callback)(void *param, void *data), void *param);
+
+/* Delete a node from the tree and free all data associated with it */
+void trbt_delete32(trbt_tree_t *tree, uint32_t key);
+
+
+/* insert into the tree with a key based on an array of uint32 */
+void trbt_insertarray32_callback(trbt_tree_t *tree, uint32_t keylen, uint32_t *key, void *(*callback)(void *param, void *data), void *param);
+
+/* Lookup a node in the tree with a key based on an array of uint32
+ and return a pointer to data or NULL */
+void *trbt_lookuparray32(trbt_tree_t *tree, uint32_t keylen, uint32_t *key);
+
+/* Traverse a tree with a key based on an array of uint32 */
+void trbt_traversearray32(trbt_tree_t *tree, uint32_t keylen, void (*callback)(void *param, void *data), void *param);
+
+/* Lookup the first node in the tree with a key based on an array of uint32
+ and return a pointer to data or NULL */
+void *trbt_findfirstarray32(trbt_tree_t *tree, uint32_t keylen);
+
+#endif /* CCAN_RBTREE_H */
--- /dev/null
+#include <ccan/rbtree/rbtree.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ /* This is how many tests you plan to run */
+ plan_tests(3);
+
+ /* Simple thing we expect to succeed */
+ ok1(some_test())
+ /* Same, with an explicit description of the test. */
+ ok(some_test(), "%s with no args should return 1", "some_test")
+ /* How to print out messages for debugging. */
+ diag("Address of some_test is %p", &some_test)
+ /* Conditional tests must be explicitly skipped. */
+#if HAVE_SOME_FEATURE
+ ok1(test_some_feature())
+#else
+ skip(1, "Don't have SOME_FEATURE")
+#endif
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
/*
add a name to an existing pointer - va_list version
*/
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_FMT(2,0);
static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
{
*/
#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
+/**
+ * TALLOC_CTX - indicate that a pointer is used as a talloc parent.
+ *
+ * As talloc is a hierarchial memory allocator, every talloc chunk is a
+ * potential parent to other talloc chunks. So defining a separate type for a
+ * talloc chunk is not strictly necessary. TALLOC_CTX is defined nevertheless,
+ * as it provides an indicator for function arguments.
+ *
+ * Example:
+ * struct foo {
+ * int val;
+ * };
+ *
+ * static struct foo *foo_new(TALLOC_CTX *mem_ctx)
+ * {
+ * struct foo *foo = talloc(mem_ctx, struct foo);
+ * if (foo)
+ * foo->val = 0;
+ * return foo;
+ * }
+ */
+typedef void TALLOC_CTX;
+
/**
* talloc_set - allocate dynamic memory for a type, into a pointer
* @ptr: pointer to the pointer to assign.
*
* talloc_set_name_const(ptr, ptr)
*/
-char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_FMT(2,3);
/**
* talloc_append_string - concatenate onto a tallocated string
* talloc_set_name_const(ptr, ptr)
*/
char *WARN_UNUSED_RESULT talloc_asprintf_append(char *s, const char *fmt, ...)
- PRINTF_ATTRIBUTE(2,3);
+ PRINTF_FMT(2,3);
/**
* talloc_vasprintf - vsprintf into a talloc buffer.
*
* talloc_set_name_const(ptr, ptr)
*/
-char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
+ PRINTF_FMT(2,0);
/**
* talloc_vasprintf_append - sprintf onto the end of a talloc buffer.
* talloc_asprintf_append(), except it takes a va_list.
*/
char *WARN_UNUSED_RESULT talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
- PRINTF_ATTRIBUTE(2,0);
+ PRINTF_FMT(2,0);
/**
* talloc_set_type - force the name of a pointer to a particular type
* without releasing the name. All of the memory is released when the ptr is
* freed using talloc_free().
*/
-const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+const char *talloc_set_name(const void *ptr, const char *fmt, ...)
+ PRINTF_FMT(2,3);
/**
* talloc_set_name_const - set a talloc pointer name to a string constant
* talloc_set_name(ptr, fmt, ....);
*/
void *talloc_named(const void *context, size_t size,
- const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+ const char *fmt, ...) PRINTF_FMT(3,4);
/**
* talloc_named_const - create a specifically-named talloc pointer
*
* talloc_named(NULL, 0, fmt, ...);
*/
-void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+void *talloc_init(const char *fmt, ...) PRINTF_FMT(1,2);
/**
* talloc_total_size - get the bytes used by the pointer and its children
# define skip_end } while(0)
unsigned int _gen_result(int, const char *, const char *, unsigned int,
- const char *, ...) PRINTF_ATTRIBUTE(5, 6);
+ const char *, ...) PRINTF_FMT(5, 6);
/**
* diag - print a diagnostic message (use instead of printf/fprintf)
* Example:
* diag("Now running complex tests");
*/
-void diag(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+void diag(const char *fmt, ...) PRINTF_FMT(1, 2);
/**
* skip - print a diagnostic message (use instead of printf/fprintf)
* skip(1, "Don't have SOME_FEATURE");
* #endif
*/
-void skip(unsigned int n, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3);
+void skip(unsigned int n, const char *fmt, ...) PRINTF_FMT(2, 3);
/**
* todo_start - mark tests that you expect to fail.
* ok(dwim(), "Did what the user wanted");
* todo_end();
*/
-void todo_start(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+void todo_start(const char *fmt, ...) PRINTF_FMT(1, 2);
/**
* todo_end - end of tests you expect to fail.
}
/* a default logging function */
-static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
+static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_FMT(3, 4);
static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
{
}
typedef struct tdb_context TDB_CONTEXT;
typedef int (*tdb_traverse_func)(struct tdb_context *, TDB_DATA, TDB_DATA, void *);
-typedef void (*tdb_log_func)(struct tdb_context *, enum tdb_debug_level, const char *, ...) PRINTF_ATTRIBUTE(3, 4);
+typedef void (*tdb_log_func)(struct tdb_context *, enum tdb_debug_level, const char *, ...) PRINTF_FMT(3, 4);
typedef unsigned int (*tdb_hash_func)(TDB_DATA *key);
struct tdb_logging_context {
static int count_pipe;
static struct tdb_logging_context log_ctx;
-#ifdef PRINTF_ATTRIBUTE
-static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+#ifdef PRINTF_FMT
+static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_FMT(3,4);
#endif
static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
{
/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
static struct tdb_context *tdbs = NULL;
-PRINTF_ATTRIBUTE(4, 5) static void
+PRINTF_FMT(4, 5) static void
null_log_fn(struct tdb_context *tdb,
enum tdb_debug_level level, void *priv,
const char *fmt, ...)
/* FIXME: Make typesafe */
typedef int (*tdb_traverse_func)(struct tdb_context *, TDB_DATA, TDB_DATA, void *);
-typedef void (*tdb_logfn_t)(struct tdb_context *, enum tdb_debug_level, void *priv, const char *, ...) PRINTF_ATTRIBUTE(4, 5);
+typedef void (*tdb_logfn_t)(struct tdb_context *, enum tdb_debug_level, void *priv, const char *, ...) PRINTF_FMT(4, 5);
typedef uint64_t (*tdb_hashfn_t)(const void *key, size_t len, uint64_t seed,
void *priv);
static union tdb_attribute log_attr;
static union tdb_attribute seed_attr;
-#ifdef PRINTF_ATTRIBUTE
-static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, void *private, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
+#ifdef PRINTF_FMT
+static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, void *private, const char *format, ...) PRINTF_FMT(4,5);
#endif
static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, void *private, const char *format, ...)
{