Added initial unfs3 sources for version 0.9.22+dfsg-1maemo2
authorHallyson Melo <melunko@ubuntu.(none)>
Wed, 18 Nov 2009 20:36:24 +0000 (17:36 -0300)
committerHallyson Melo <melunko@ubuntu.(none)>
Wed, 18 Nov 2009 20:36:24 +0000 (17:36 -0300)
80 files changed:
unfs3/BRANCHES [new file with mode: 0644]
unfs3/CREDITS [new file with mode: 0644]
unfs3/Config/Makefile.in [new file with mode: 0644]
unfs3/Config/exports.h [new file with mode: 0644]
unfs3/Config/exports.l [new file with mode: 0644]
unfs3/Config/exports.y [new file with mode: 0644]
unfs3/Extras/Makefile.in [new file with mode: 0644]
unfs3/Extras/cluster.c [new file with mode: 0644]
unfs3/Extras/cluster.h [new file with mode: 0644]
unfs3/Extras/tags.7 [new file with mode: 0644]
unfs3/LICENSE [new file with mode: 0644]
unfs3/Makefile.in [new file with mode: 0644]
unfs3/NEWS [new file with mode: 0644]
unfs3/README [new file with mode: 0644]
unfs3/README.nfsroot [new file with mode: 0644]
unfs3/aclocal.m4 [new file with mode: 0644]
unfs3/attr.c [new file with mode: 0644]
unfs3/attr.h [new file with mode: 0644]
unfs3/backend.h [new file with mode: 0644]
unfs3/backend_unix.h [new file with mode: 0644]
unfs3/backend_win32.h [new file with mode: 0644]
unfs3/bootstrap [new file with mode: 0755]
unfs3/config.h.in [new file with mode: 0644]
unfs3/configure [new file with mode: 0755]
unfs3/configure.ac [new file with mode: 0644]
unfs3/contrib/nfsotpclient/README [new file with mode: 0644]
unfs3/contrib/nfsotpclient/mountclient/__init__.py [new file with mode: 0644]
unfs3/contrib/nfsotpclient/mountclient/mountconstants.py [new file with mode: 0644]
unfs3/contrib/nfsotpclient/mountclient/mountpacker.py [new file with mode: 0644]
unfs3/contrib/nfsotpclient/mountclient/mounttypes.py [new file with mode: 0644]
unfs3/contrib/nfsotpclient/nfsotpclient.py [new file with mode: 0755]
unfs3/contrib/nfsotpclient/rpc.py [new file with mode: 0644]
unfs3/contrib/rpcproxy/rpcproxy [new file with mode: 0755]
unfs3/daemon.c [new file with mode: 0644]
unfs3/daemon.h [new file with mode: 0644]
unfs3/debian/changelog [new file with mode: 0644]
unfs3/debian/compat [new file with mode: 0644]
unfs3/debian/control [new file with mode: 0644]
unfs3/debian/copyright [new file with mode: 0644]
unfs3/debian/rules [new file with mode: 0755]
unfs3/debian/unfs3.default [new file with mode: 0644]
unfs3/debian/unfs3.docs [new file with mode: 0644]
unfs3/debian/unfs3.init [new file with mode: 0644]
unfs3/debian/unfs3.postinst [new file with mode: 0644]
unfs3/debian/unfs3.postrm [new file with mode: 0644]
unfs3/doc/README.win [new file with mode: 0644]
unfs3/doc/TODO [new file with mode: 0644]
unfs3/doc/kirch1.txt [new file with mode: 0644]
unfs3/doc/passwords.txt [new file with mode: 0644]
unfs3/error.c [new file with mode: 0644]
unfs3/error.h [new file with mode: 0644]
unfs3/fd_cache.c [new file with mode: 0644]
unfs3/fd_cache.h [new file with mode: 0644]
unfs3/fh.c [new file with mode: 0644]
unfs3/fh.h [new file with mode: 0644]
unfs3/fh_cache.c [new file with mode: 0644]
unfs3/fh_cache.h [new file with mode: 0644]
unfs3/indent-all [new file with mode: 0755]
unfs3/install-sh [new file with mode: 0755]
unfs3/locate.c [new file with mode: 0644]
unfs3/locate.h [new file with mode: 0644]
unfs3/md5.c [new file with mode: 0644]
unfs3/md5.h [new file with mode: 0644]
unfs3/mount.c [new file with mode: 0644]
unfs3/mount.h [new file with mode: 0644]
unfs3/nfs.c [new file with mode: 0644]
unfs3/nfs.h [new file with mode: 0644]
unfs3/password.c [new file with mode: 0644]
unfs3/password.h [new file with mode: 0644]
unfs3/readdir.c [new file with mode: 0644]
unfs3/readdir.h [new file with mode: 0644]
unfs3/unfs3.spec [new file with mode: 0644]
unfs3/unfsd.8 [new file with mode: 0644]
unfs3/unfsd.init [new file with mode: 0755]
unfs3/user.c [new file with mode: 0644]
unfs3/user.h [new file with mode: 0644]
unfs3/winsupport.c [new file with mode: 0644]
unfs3/winsupport.h [new file with mode: 0644]
unfs3/xdr.c [new file with mode: 0644]
unfs3/xdr.h [new file with mode: 0644]

diff --git a/unfs3/BRANCHES b/unfs3/BRANCHES
new file mode 100644 (file)
index 0000000..f16282a
--- /dev/null
@@ -0,0 +1,21 @@
+uidmapping
+----------
+Unused. 
+
+
+removable-fsidhash
+------------------
+This branch has been used for work on removable support. All changes
+has been merged with the HEAD branch now. 
+
+
+removable-support
+-----------------
+Experimental ASCII-filehandles work. These changes will probably not
+be merged with HEAD; they are pretty much obsoleted by the work on the 
+"removable-fsidhash" branch. 
+
+
+pharao90
+--------
+Main vendor branch. 
diff --git a/unfs3/CREDITS b/unfs3/CREDITS
new file mode 100644 (file)
index 0000000..9f44fd1
--- /dev/null
@@ -0,0 +1,37 @@
+CREDITS
+=======
+
+The design of unfs3's filehandle code owes much to the original NFSv2
+universal user-space NFS server. Thanks to Mark Shand and Don Becker
+for coming up with those concepts.
+
+We also give credit to Gregory R. Warnes for the idea of file
+tagging as used by ClusterNFS and the unfs3 cluster extension.
+
+People who contributed ideas, suggestions, or code to UNFS3:
+
+- Bernd Schubert <bernd-schubert@web.de>
+- Avery Pennarun <apenwarr@nit.ca>
+- Juergen Brendel <juergen@brendel.com>
+- Peter Astrand <astrand@cendio.se>
+- Igor Durdanovic <idurdanovic@comcast.net>
+- Jan P. Springer <jsd01@users.sourceforge.net>
+- Thomas Schwinge <schwinge@nic-nac-project.de>
+- Simon Matter <simix@users.sourceforge.net>
+- Brian <bohalloran@users.sourceforge.net>
+- Jean Aumont <JeanAumont@videotron.ca>
+- Constantin Scheder <scheder@nirvanastorage.com>
+- Sam Sharma <sam.sharma@gat.com>
+- Holger Wolf <Holger.Wolf@de.ibm.com>
+- Frank v Waveren <fvw@var.cx>
+- Matthew Bloch <matthew@bytemark.co.uk>
+- Nick S. Grechukh <gns@altlinux.org>
+- Michael Shigorin <mike@osdn.org.ua>
+- Tim Weippert <tim.weippert@tds.de>
+- Hanpen-san <hanpen@lopox.com>
+- Sergey Bolshakov <sbolshakov@altlinux.ru>
+- Phill Bertolus <phillb@webwombat.com>
+- Brent A Nelson <brent@phys.ufl.edu>
+- Garrett Cooper <garrcoop@cisco.com>
+- Wesley Shields <wxs@FreeBSD.org>
+- Bernhard Duebi <Bernhard.Duebi@telekurs.com>
diff --git a/unfs3/Config/Makefile.in b/unfs3/Config/Makefile.in
new file mode 100644 (file)
index 0000000..45c56e7
--- /dev/null
@@ -0,0 +1,33 @@
+CC = @CC@
+CFLAGS = @CFLAGS@
+AR = ar
+RM = rm -f
+LEX = @LEX@
+YACC = @YACC@
+
+OBJS = @LEX_OUTPUT_ROOT@.o y.tab.o
+
+all: lib.a
+
+lib.a: $(OBJS)
+       $(AR) crs lib.a $(OBJS)
+
+y.tab.h y.tab.c: exports.y
+       $(YACC) -d exports.y
+
+y.tab.o: y.tab.c exports.h ../nfs.h ../mount.h ../daemon.h
+
+@LEX_OUTPUT_ROOT@.c: exports.l
+       $(LEX) exports.l
+
+@LEX_OUTPUT_ROOT@.o: @LEX_OUTPUT_ROOT@.c y.tab.h ../nfs.h
+
+dep:
+       :
+
+clean:
+       $(RM) $(OBJS) lib.a
+       $(RM) @LEX_OUTPUT_ROOT@.c y.tab.h y.tab.c
+
+distclean:
+       $(RM) Makefile
diff --git a/unfs3/Config/exports.h b/unfs3/Config/exports.h
new file mode 100644 (file)
index 0000000..2c5edec
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * UNFS3 export controls
+ * (C) 2003, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_EXPORTS_H
+#define UNFS3_EXPORTS_H
+
+#include "../mount.h" /* exports type */
+
+#define OPT_NO_ROOT_SQUASH     1
+#define OPT_ALL_SQUASH         2
+#define OPT_RW                 4
+#define OPT_REMOVABLE          8
+#define OPT_INSECURE           16
+
+#define PASSWORD_MAXLEN   64
+
+#define ANON_NOTSPECIAL 0xffffffff
+
+extern exports exports_nfslist;
+/* Options cache */
+extern int     exports_opts;
+const char      *export_path; 
+extern uint32  export_fsid;
+extern uint32   export_password_hash;
+
+extern unsigned char password[PASSWORD_MAXLEN+1];
+
+int            exports_parse(void);
+int            exports_options(const char *path, struct svc_req *rqstp, char **password, uint32 *fsid);
+int             export_point(const char *path);
+char            *export_point_from_fsid(uint32 fsid, time_t **last_mtime, uint32 **dir_hash);
+nfsstat3       exports_compat(const char *path, struct svc_req *rqstp);
+nfsstat3       exports_rw(void);
+uint32         exports_anonuid(void);
+uint32         exports_anongid(void);
+uint32          fnv1a_32(const char *str, uint32 hval);
+#ifdef WIN32
+uint32          wfnv1a_32(const wchar_t *str, uint32 hval);
+#endif /* WIN32 */
+char            *normpath(const char *path, char *normpath);
+
+#endif
diff --git a/unfs3/Config/exports.l b/unfs3/Config/exports.l
new file mode 100644 (file)
index 0000000..8a2d6d1
--- /dev/null
@@ -0,0 +1,70 @@
+%{
+/*
+ * UNFS3 exports lexer
+ * (C) 2003, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+#include "../config.h"
+
+#include <rpc/rpc.h>
+#include <string.h>
+
+#include "../nfs.h"
+#include "y.tab.h"
+
+#define YY_NO_UNPUT
+
+YYSTYPE yylval;
+extern int e_error;
+
+/*
+ * copy matching text to yylval.text
+ */
+static void copy_text(const char *from, int leng)
+{
+       int len;
+
+       /* check length to prevent buffer overflow */   
+       if (leng + 1 > NFS_MAXPATHLEN) {
+               len = NFS_MAXPATHLEN - 1;
+               e_error = 1;
+       } else
+               len = leng;
+
+       memcpy(yylval.text, from, len);
+       yylval.text[len] = 0;
+}
+%}
+
+WHITE [ \t]
+NOWHITE [^ \n\t]
+PATH "/"{NOWHITE}*
+ID [a-z][\.\-_a-z0-9]*
+OPTVALUE [^ \n\t,)]*
+
+IPCOMP [0-9]{1,3}
+IP {IPCOMP}"."{IPCOMP}"."{IPCOMP}"."{IPCOMP}
+NETCOMP [0-9]{1,2}
+NET {IP}"/"{NETCOMP}
+OLDNET {IP}"/"{IP}
+
+%%
+
+^{WHITE}*\n            { /* eat empty line */ }
+^{WHITE}*              { /* eat whitespace at beginning of line */ }
+"\\"\n                 { /* eat escaped newline */ }
+^{WHITE}*"#".*\n       { /* eat one-line comment */ }
+{WHITE}*"#".*          { /* eat trailing comment */ }
+
+{IP}                   { copy_text(yytext, yyleng); return IP; }
+{NET}                  { copy_text(yytext, yyleng); return NET; }
+{OLDNET}               { copy_text(yytext, yyleng); return OLDNET; }
+"\"".*"\""             { copy_text(yytext+1, yyleng-2); return PATH; }
+"="{OPTVALUE}          { copy_text(yytext+1, yyleng-1); return OPTVALUE; }
+{PATH}                 { copy_text(yytext, yyleng); return PATH; }
+{WHITE}+               { copy_text(yytext, yyleng); return WHITE; }
+{ID}                   { copy_text(yytext, yyleng); return ID; }
+
+.                      { return *yytext; }
+\n                     { return '\n'; }
+   
\ No newline at end of file
diff --git a/unfs3/Config/exports.y b/unfs3/Config/exports.y
new file mode 100644 (file)
index 0000000..f30e8af
--- /dev/null
@@ -0,0 +1,848 @@
+%{
+/*
+ * UNFS3 exports parser and export controls
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+#include "../config.h"
+
+#include <rpc/rpc.h>
+#include <limits.h>
+
+#ifdef WIN32
+#include "../winsupport.h"
+#else
+#include <netdb.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* WIN32 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../nfs.h"
+#include "../mount.h"
+#include "../daemon.h"
+#include "../backend.h"
+#include "exports.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX      4096
+#endif
+
+/* for lack of a better place */
+#ifdef __GNUC__
+#define U(x) x __attribute__ ((unused))
+#else
+#define U(x) x
+#endif
+
+/* lexer stuff, to avoid compiler warnings */
+int yylex(void);
+extern FILE *yyin;
+
+/*
+ * C code used by yacc parser
+ */
+
+typedef struct {
+       char            orig[NFS_MAXPATHLEN];
+       int             options;
+        char            password[PASSWORD_MAXLEN+1];
+        uint32          password_hash;
+       struct in_addr  addr;
+       struct in_addr  mask;
+       uint32          anonuid;
+       uint32          anongid;
+       struct e_host   *next;
+} e_host;
+
+typedef struct {
+       char            path[NFS_MAXPATHLEN];
+       char            orig[NFS_MAXPATHLEN];
+       e_host          *hosts;
+       uint32          fsid; /* export point fsid (for removables) */
+       time_t          last_mtime; /* Last returned mtime (for removables) */
+       uint32          dir_hash; /* Hash of dir contents (for removables) */
+       struct e_item   *next;
+} e_item;
+
+/* export list, item, and host filled during parse */
+static e_item *e_list = NULL;
+static e_item cur_item;
+static e_host cur_host;
+
+/* last looked-up anonuid and anongid */
+static uint32 last_anonuid = ANON_NOTSPECIAL;
+static uint32 last_anongid = ANON_NOTSPECIAL;
+
+/* mount protocol compatible variants */
+static exports ne_list = NULL;
+static struct exportnode ne_item;
+static struct groupnode ne_host;
+
+/* error status of last parse */
+int e_error = FALSE;
+
+/*
+ * The FNV1a-32 hash algorithm
+ * (http://www.isthe.com/chongo/tech/comp/fnv/)
+ */
+uint32 fnv1a_32(const char *str, uint32 hval)
+{
+    static const uint32 fnv_32_prime = 0x01000193;
+    
+    while (*str) {
+       hval ^= *str++;
+       hval *= fnv_32_prime;
+    }
+    return hval;
+}
+
+#ifdef WIN32
+uint32 wfnv1a_32(const wchar_t *str, uint32 hval)
+{
+    static const uint32 fnv_32_prime = 0x01000193;
+    
+    while (*str) {
+       hval ^= *str++;
+       hval *= fnv_32_prime;
+    }
+    return hval;
+}
+#endif
+
+/*
+ * get static fsid, for use with removable media export points
+ */
+static uint32 get_free_fsid(const char *path)
+{
+    uint32 hval;
+
+    /* The 32:th bit is set to one on all special filehandles. The
+       last 31 bits are hashed from the export point path. */
+    hval = fnv1a_32(path, 0);
+    hval |= 0x80000000;
+    return hval;
+}
+
+
+/*
+ * clear current host
+ */
+static void clear_host(void)
+{
+       memset(&cur_host, 0, sizeof(e_host));
+       strcpy(cur_host.orig, "<anon clnt>");
+       memset(&ne_host, 0, sizeof(struct groupnode));
+       
+       cur_host.anonuid =
+       cur_host.anongid = ANON_NOTSPECIAL; 
+}
+
+/*
+ * clear current item
+ */
+static void clear_item(void)
+{
+       memset(&cur_item, 0, sizeof(e_item));
+       memset(&ne_item, 0, sizeof(struct exportnode));
+}
+
+/*
+ * add current host to current export item
+ */
+static void add_host(void)
+{
+       e_host *new;
+       e_host *iter;
+       
+       groups ne_new;
+       groups ne_iter;
+
+       new = malloc(sizeof(e_host));
+       ne_new = malloc(sizeof(struct groupnode));
+       if (!new || !ne_new) {
+               logmsg(LOG_EMERG, "out of memory, aborting");
+               daemon_exit(CRISIS);
+       }
+
+       *new = cur_host;
+       *ne_new = ne_host;
+       ne_new->gr_name = new->orig;
+
+       /* internal list */
+       if (cur_item.hosts) {
+               iter = cur_item.hosts;
+               while (iter->next)
+                       iter = (e_host *) iter->next;
+               iter->next = (struct e_host *) new;
+       } else
+               cur_item.hosts = new;
+
+       /* matching mount protocol list */
+       if (ne_item.ex_groups) {
+               ne_iter = ne_item.ex_groups;
+               while (ne_iter->gr_next)
+                       ne_iter = (groups) ne_iter->gr_next;
+               ne_iter->gr_next = ne_new;
+       } else
+               ne_item.ex_groups = ne_new;
+       
+       clear_host();
+}
+
+/* 
+   Normalize path, eliminating double slashes, etc. To be used instead
+   of realpath, when realpath is not possible. Normalizing export
+   points is important. Otherwise, mount requests might fail, since
+   /x/y is not a prefix of ///x///y/ etc.
+*/
+char *normpath(const char *path, char *normpath)
+{
+       char *n;
+       const char *p;
+
+       /* Copy path to normpath, and replace blocks of slashes with
+          single slash */
+       p = path;
+       n = normpath;
+       while (*p) {
+               /* Skip over multiple slashes */
+               if (*p == '/' && *(p+1) == '/') {
+                       p++;
+                       continue;
+               }
+               *n++ = *p++;
+       }
+       *n = '\0';
+
+       /* Remove trailing slash, if any. */
+       if ((n - normpath) > 1 && *(n-1) == '/')
+               *(n-1) = '\0';
+
+       return normpath;
+}
+
+/*
+ * add current item to current export list
+ */
+static void add_item(const char *path)
+{
+       char buf[PATH_MAX];
+       e_item *new;
+       e_item *iter;
+       e_host *host;
+       /* Is this item marked as removable for all hosts? */
+       int removable_for_all = 1;
+       
+       exports ne_new;
+       exports ne_iter;
+
+       new = malloc(sizeof(e_item));
+       ne_new = malloc(sizeof(struct exportnode));
+       if (!new || !ne_new) {
+               logmsg(LOG_EMERG, "out of memory, aborting");
+               daemon_exit(CRISIS);
+       }
+
+       /* Loop over all hosts and check if marked as removable. */
+       host = cur_item.hosts;
+       while (host) {
+               if (!(host->options & OPT_REMOVABLE))
+                       removable_for_all = 0;
+               host = (e_host *) host->next;
+       }
+
+       if (removable_for_all) {
+               /* If marked as removable for all hosts, don't try
+                  realpath. */
+               normpath(path, buf);
+       } else if (!backend_realpath(path, buf)) {
+               logmsg(LOG_CRIT, "realpath for %s failed", path);
+               e_error = TRUE;
+               free(new);
+               free(ne_new);
+               clear_item();
+               return;
+       }
+
+       if (strlen(buf) + 1 > NFS_MAXPATHLEN) {
+               logmsg(LOG_CRIT, "attempted to export too long path");
+               e_error = TRUE;
+               free(new);
+               free(ne_new);
+               clear_item();
+               return;
+       }
+
+       /* if no hosts listed, list default host */
+       if (!cur_item.hosts)
+               add_host();
+
+       *new = cur_item;
+       strcpy(new->path, buf);
+       strcpy(new->orig, path);
+       new->fsid = get_free_fsid(path);  
+       new->last_mtime = 0;
+       new->dir_hash = 0;
+
+       *ne_new = ne_item;
+       ne_new->ex_dir = new->orig;
+
+       /* internal list */
+       if (e_list) {
+               iter = e_list;
+               while (iter->next)
+                       iter = (e_item *) iter->next;
+               iter->next = (struct e_item *) new;
+       } else
+               e_list = new;
+
+       /* matching mount protocol list */
+       if (ne_list) {
+               ne_iter = ne_list;
+               while (ne_iter->ex_next)
+                       ne_iter = (exports) ne_iter->ex_next;
+               ne_iter->ex_next = ne_new;
+       } else
+               ne_list = ne_new;
+               
+       clear_item();
+}
+
+/*
+ * fill current host's address given a hostname
+ */
+static void set_hostname(const char *name)
+{
+       struct hostent *ent;
+
+       if (strlen(name) + 1 > NFS_MAXPATHLEN) {
+               e_error = TRUE;
+               return;
+       }
+       strcpy(cur_host.orig, name);
+
+       ent = gethostbyname(name);
+
+       if (ent) {
+               memcpy(&cur_host.addr, ent->h_addr_list[0],
+                      sizeof(struct in_addr));
+               cur_host.mask.s_addr = ~0UL;
+       } else {
+               logmsg(LOG_CRIT, "could not resolve hostname '%s'", name);
+               e_error = TRUE;
+       }
+}      
+
+/*
+ * fill current host's address given an IP address
+ */
+static void set_ipaddr(const char *addr)
+{
+       strcpy(cur_host.orig, addr);
+       
+       if (!inet_aton(addr, &cur_host.addr))
+               e_error = TRUE;
+       cur_host.mask.s_addr = ~0UL;
+}
+
+/*
+ * compute network bitmask
+ */
+static unsigned long make_netmask(int bits) {
+       unsigned long buf = 0;
+       int i;
+
+       for (i=0; i<bits; i++)
+               buf = (buf << 1) + 1;
+       for (; i < 32; i++)
+               buf = (buf << 1);
+       return htonl(buf);
+}
+
+/*
+ * fill current host's address given IP address and netmask
+ */
+static void set_ipnet(char *addr, int new)
+{
+       char *pos, *net;
+
+       pos = strchr(addr, '/');
+       net = pos + 1;
+       *pos = 0;
+       
+       set_ipaddr(addr);
+       
+       if (new)
+               cur_host.mask.s_addr = make_netmask(atoi(net));
+       else
+               if (!inet_aton(net, &cur_host.mask))
+                       e_error = TRUE;
+
+       *pos = '/';
+       strcpy(cur_host.orig, addr);
+}
+
+/*
+ * add an option bit to the current host
+ */
+static void add_option(const char *opt)
+{
+       if (strcmp(opt,"no_root_squash") == 0)
+               cur_host.options |= OPT_NO_ROOT_SQUASH;
+       else if (strcmp(opt,"root_squash") == 0)
+               cur_host.options &= ~OPT_NO_ROOT_SQUASH;
+       else if (strcmp(opt,"all_squash") == 0)
+               cur_host.options |= OPT_ALL_SQUASH;
+       else if (strcmp(opt,"no_all_squash") == 0)
+               cur_host.options &= ~OPT_ALL_SQUASH;
+       else if (strcmp(opt,"rw") == 0)
+               cur_host.options |= OPT_RW;
+       else if (strcmp(opt,"ro") == 0)
+               cur_host.options &= ~OPT_RW;
+       else if (strcmp(opt,"removable") == 0) {
+               cur_host.options |= OPT_REMOVABLE;
+       } else if (strcmp(opt,"fixed") == 0)
+               cur_host.options &= ~OPT_REMOVABLE;
+       else if (strcmp(opt,"insecure") == 0)
+               cur_host.options |= OPT_INSECURE;
+       else if (strcmp(opt,"secure") == 0)
+               cur_host.options &= ~OPT_INSECURE;
+       else
+               logmsg(LOG_WARNING, "Warning: unknown exports option `%s' ignored",
+                       opt);
+}
+
+static void add_option_with_value(const char *opt, const char *val)
+{
+    if (strcmp(opt,"password") == 0) {
+       if (strlen(val) > PASSWORD_MAXLEN) {
+           logmsg(LOG_WARNING, "Warning: password for export %s truncated to 64 chars",
+                  cur_item.orig);
+       }
+       strncpy(cur_host.password, val, sizeof(password));
+       cur_host.password[PASSWORD_MAXLEN] = '\0';
+       /* Calculate hash */
+       cur_host.password_hash = fnv1a_32(cur_host.password, 0);
+    } else if (strcmp(opt,"anonuid") == 0) {
+       cur_host.anonuid = atoi(val);
+    } else if (strcmp(opt,"anongid") == 0) {
+       cur_host.anongid = atoi(val);
+    } else {
+        logmsg(LOG_WARNING, "Warning: unknown exports option `%s' ignored",
+            opt);
+    }
+}
+
+/*
+ * dummy error function
+ */
+void yyerror(U(char *s))
+{
+       e_error = TRUE;
+       return;
+}
+
+%}
+
+%union {
+       char text[NFS_MAXPATHLEN];
+};
+
+%token <text> PATH
+%token <text> ID
+%token <text> OPTVALUE
+%token <text> WHITE
+%token <text> IP
+%token <text> NET
+%token <text> OLDNET
+
+%%
+
+exports:
+       export
+       | export '\n' exports
+       |
+       ;
+
+export:
+       PATH                    { add_item($1); }
+       | PATH WHITE hosts      { add_item($1); }
+       | PATH WHITE            { add_item($1); }
+       ;
+
+hosts:
+       host
+       | host WHITE hosts
+       | host WHITE
+       ;
+
+host:
+       name                    { add_host(); }
+       | name '(' opts ')'     { add_host(); }
+       | '(' opts ')'          { add_host(); }
+       ;
+
+name:
+       ID                      { set_hostname($1); }
+       | IP                    { set_ipaddr($1); }
+       | NET                   { set_ipnet($1, TRUE); }
+       | OLDNET                { set_ipnet($1, FALSE); }
+       ;
+       
+opts:
+       opt                     
+       | opt ',' opts          
+       |
+       ;
+opt: 
+       ID                      { add_option($1); }
+        | ID OPTVALUE           { add_option_with_value($1,$2); } 
+       ;
+%%
+
+/*
+ * C code using yacc parser + access code for exports list
+ */
+
+/* effective export list and access flag */
+static e_item *export_list = NULL;
+static volatile int exports_access = FALSE;
+
+/* mount protocol compatible exports list */
+exports exports_nfslist = NULL;
+
+/*
+ * free NFS groups list
+ */
+void free_nfsgroups(groups group)
+{
+       groups list, next;
+       
+       list = group;
+       while (list) {
+               next = (groups) list->gr_next;
+               free(list);
+               list = next;
+       }
+}
+
+/*
+ * free NFS exports list
+ */
+void free_nfslist(exports elist)
+{
+       exports list, next;
+
+       list = elist;
+       while(list) {
+               next = (exports) list->ex_next;
+               free_nfsgroups(list->ex_groups);
+               free(list);
+               list = next;
+       }
+}
+
+/*
+ * free list of host structures
+ */
+static void free_hosts(e_item *item)
+{
+       e_host *host, *cur;
+       
+       host = item->hosts;
+       while (host) {
+               cur = host;
+               host = (e_host *) host->next;
+               free(cur);
+       }
+}
+
+/*
+ * free list of export items
+ */
+static void free_list(e_item *item)
+{
+       e_item *cur;
+       
+       while (item) {
+               free_hosts(item);
+               cur = item;
+               item = (e_item *) item->next;
+               free(cur);
+       }
+}
+
+/*
+ * print out the current exports list (for debugging)
+ */
+void print_list(void)
+{
+       char addrbuf[16], maskbuf[16];
+
+       e_item *item;
+       e_host *host;
+       
+       item = e_list;
+               
+       while (item) {
+               host = item->hosts;
+               while (host) {
+                       /* inet_ntoa returns static buffer */
+                       strcpy(addrbuf, inet_ntoa(host->addr));
+                       strcpy(maskbuf, inet_ntoa(host->mask));
+                       printf("%s: ip %s mask %s options %i\n",
+                               item->path, addrbuf, maskbuf,
+                               host->options);
+                       host = (e_host *) host->next;
+               }
+               item = (e_item *) item->next;
+       }
+}
+
+/*
+ * clear current parse state
+ */
+static void clear_cur(void)
+{
+       e_list = NULL;
+       ne_list = NULL;
+       e_error = FALSE;
+       clear_host();
+       clear_item();
+}
+
+/*
+ * parse an exports file
+ */
+int exports_parse(void)
+{
+       FILE *efile;
+
+       /*
+        * if we are in the SIGHUP handler, a may_mount or get_options
+        * may currently be accessing the list
+        */
+       if (exports_access) {
+               logmsg(LOG_CRIT, "export list is being traversed, no reload\n");
+               return FALSE;
+       }
+
+       efile = fopen(opt_exports, "r");
+       if (!efile) {
+               logmsg(LOG_CRIT, "could not open '%s', exporting nothing",
+                      opt_exports);
+               free_list(export_list);
+               free_nfslist(exports_nfslist);
+               export_list = NULL;
+               exports_nfslist = NULL;
+               return FALSE;
+       }
+
+       yyin = efile;
+       clear_cur();
+       yyparse();
+       fclose(efile);
+       
+       if (e_error) {
+               logmsg(LOG_CRIT, "syntax error in '%s', exporting nothing",
+                      opt_exports);
+               free_list(export_list);
+               free_nfslist(exports_nfslist);
+               export_list = NULL;
+               exports_nfslist = NULL;
+               return FALSE;
+       }
+       
+       /* print out new list for debugging */
+       if (!opt_detach)
+               print_list();
+       
+       free_list(export_list);
+       free_nfslist(exports_nfslist);
+       export_list = e_list;
+       exports_nfslist = ne_list;
+       return TRUE;
+}
+
+/*
+ * find a given host inside a host list, return options
+ */
+static e_host* find_host(struct in_addr remote, e_item *item,
+                    char **password, uint32 *password_hash)
+{
+       e_host *host;
+
+       host = item->hosts;
+       while (host) {
+               if ((remote.s_addr & host->mask.s_addr) == host->addr.s_addr) {
+                       if (password != NULL) 
+                               *password = host->password;
+                       if (password_hash != NULL)
+                               *password_hash = host->password_hash;
+                       return host;
+               }
+               host = (e_host *) host->next;
+       }
+       return NULL;
+}
+
+/* options cache */
+int exports_opts = -1;
+const char *export_path = NULL; 
+uint32 export_fsid = 0;
+uint32 export_password_hash = 0;
+
+/*
+ * given a path, return client's effective options
+ */
+int exports_options(const char *path, struct svc_req *rqstp,
+                   char **password, uint32 *fsid)
+{
+       e_item *list;
+       struct in_addr remote;
+       unsigned int last_len = 0;
+       
+       exports_opts = -1;
+       export_path = NULL;
+       export_fsid = 0;
+       last_anonuid = ANON_NOTSPECIAL;
+       last_anongid = ANON_NOTSPECIAL;
+
+       /* check for client attempting to use invalid pathname */
+       if (!path || strstr(path, "/../"))
+               return exports_opts;
+       
+       remote = get_remote(rqstp);
+
+       /* protect against SIGHUP reloading the list */
+       exports_access = TRUE;
+       
+       list = export_list;
+       while (list) {
+               /* longest matching prefix wins */
+               if (strlen(list->path) > last_len    &&
+#ifndef WIN32
+                   strstr(path, list->path) == path) {
+#else
+                   !win_utf8ncasecmp(path, list->path, strlen(list->path))) {
+#endif
+                   e_host* cur_host = find_host(remote, list, password, &export_password_hash);
+
+                       if (fsid != NULL)
+                               *fsid = list->fsid;
+                       if (cur_host) {
+                               exports_opts = cur_host->options;
+                               export_path = list->path;
+                               export_fsid = list->fsid;
+                               last_len = strlen(list->path);
+                               last_anonuid = cur_host->anonuid;
+                               last_anongid = cur_host->anongid;
+                       }
+               }
+               list = (e_item *) list->next;
+       }
+       exports_access = FALSE;
+       return exports_opts;
+}
+
+/*
+ * check whether path is an export point
+ */
+int export_point(const char *path)
+{
+        e_item *list;
+
+       exports_access = TRUE;
+       list = export_list;
+
+       while (list) {
+           if (strcmp(path, list->path) == 0) {
+               exports_access = FALSE;
+               return TRUE;
+           }
+           list = (e_item *) list->next;
+       }
+       exports_access = FALSE;
+       return FALSE;
+}
+
+/*
+ * return exported path from static fsid
+ */
+char *export_point_from_fsid(uint32 fsid, time_t **last_mtime, uint32 **dir_hash)
+{
+    e_item *list;
+    
+    exports_access = TRUE;
+    list = export_list;
+    
+    while (list) {
+       if (list->fsid == fsid) {
+           if (last_mtime != NULL)
+               *last_mtime = &list->last_mtime;
+           if (dir_hash != NULL)
+               *dir_hash = &list->dir_hash;
+           exports_access = FALSE;
+           return list->path;
+       }
+       list = (e_item *) list->next;
+    }
+    exports_access = FALSE;
+    return NULL;
+}
+
+
+/*
+ * check whether export options of a path match with last set of options
+ */
+nfsstat3 exports_compat(const char *path, struct svc_req *rqstp)
+{
+       int prev;
+       uint32 prev_anonuid, prev_anongid;
+       
+       prev = exports_opts;
+       prev_anonuid = last_anonuid;
+       prev_anongid = last_anongid;
+       
+       if (exports_options(path, rqstp, NULL, NULL) == prev &&
+           last_anonuid == prev_anonuid &&
+           last_anongid == prev_anongid)
+               return NFS3_OK;
+       else if (exports_opts == -1)
+               return NFS3ERR_ACCES;
+       else
+               return NFS3ERR_XDEV;
+}
+
+/*
+ * check whether options indicate rw mount
+ */
+nfsstat3 exports_rw(void)
+{
+       if (exports_opts != -1 && (exports_opts & OPT_RW))
+               return NFS3_OK;
+       else
+               return NFS3ERR_ROFS;
+}
+
+/*
+ * returns the last looked-up anonuid for a mount (ANON_NOTSPECIAL means none in effect)
+ */
+uint32 exports_anonuid(void)
+{
+       return last_anonuid;
+}
+
+/*
+ * returns the last looked-up anongid for a mount (ANON_NOTSPECIAL means none in effect)
+ */
+uint32 exports_anongid(void)
+{
+       return last_anongid;
+}
diff --git a/unfs3/Extras/Makefile.in b/unfs3/Extras/Makefile.in
new file mode 100644 (file)
index 0000000..038f10c
--- /dev/null
@@ -0,0 +1,23 @@
+CC = @CC@
+CFLAGS = @CFLAGS@
+AR = ar
+RM = rm -f
+
+SOURCES = cluster.c
+OBJS = cluster.o
+
+all: lib.a
+
+lib.a: $(OBJS)
+       $(AR) crs lib.a $(OBJS)
+
+dep: $(SOURCES)
+       $(CC) $(CFLAGS) -MM $(SOURCES) >> Makefile
+
+clean:
+       $(RM) $(OBJS) lib.a
+
+distclean:
+       $(RM) Makefile
+
+# automatically generated dependencies follow
diff --git a/unfs3/Extras/cluster.c b/unfs3/Extras/cluster.c
new file mode 100644 (file)
index 0000000..3dd5628
--- /dev/null
@@ -0,0 +1,512 @@
+
+/*
+ * UNFS3 cluster support
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "../config.h"
+
+#ifdef WANT_CLUSTER
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "../nfs.h"
+#include "../daemon.h"
+#include "../backend.h"
+#include "cluster.h"
+
+/* array of dirents prefixed with master file name */
+static char **cluster_dirents = NULL;
+
+/* number of dirents in above array */
+static int cluster_count = -1;
+
+/*
+ * check whether given pathname is in clustering path
+ */
+int want_cluster(const char *path)
+{
+    char buf[NFS_MAXPATHLEN];
+    char *last, *next;
+
+    /* if path is too long, play it safe */
+    if (strlen(opt_cluster_path) + 1 > NFS_MAXPATHLEN)
+       return TRUE;
+
+    strcpy(buf, opt_cluster_path);
+    last = buf;
+
+    /* iterate over colon-seperated list */
+    do {
+       next = strchr(last, ':');
+       if (next)
+           *next = 0;
+
+       if (strstr(path, last) == path)
+           return TRUE;
+
+       if (next) {
+           last = next + 1;
+           if (strlen(last) == 0)
+               last = NULL;
+       } else {
+           last = NULL;
+       }
+    } while (last);
+
+    return FALSE;
+}
+
+/*
+ * get name of remote machine
+ */
+static char *get_host(struct in_addr remote)
+{
+    static char buf[NFS_MAXPATHLEN];
+    struct hostent *entry;
+    char *dot;
+
+    entry = gethostbyaddr((char *) &remote, sizeof(struct in_addr), AF_INET);
+
+    if (entry) {
+       strcpy(buf, entry->h_name);
+
+       /* have the name string end at the first dot */
+       dot = strchr(buf, '.');
+       if (dot)
+           *dot = 0;
+
+       return buf;
+    }
+
+    return NULL;
+}
+
+/*
+ * check whether name is already host tagged name
+ */
+int is_host(const char *name)
+{
+    return (int) (strstr(name, "$$HOST=") && name[strlen(name) - 1] == '$' &&
+                 name[strlen(name) - 2] == '$');
+}
+
+/*
+ * check whether a hostname matches a dirent
+ */
+char *match_host(const char *hname, const char *entry)
+{
+    char buf[NFS_MAXPATHLEN];
+    static char *part;
+
+    /* check for presence of hostname tag */
+    if (!is_host(entry))
+       return NULL;
+
+    part = strstr(entry, "$$HOST=");
+
+    /* copy hostname part of host tag */
+    memcpy(buf, part + 7, strlen(part) - 8);
+    buf[strlen(part) - 9] = 0;
+
+    /* exact match? */
+    if (strcmp(buf, hname) == 0)
+       return part;
+
+    /* wildcard pattern? */
+    if (buf[strlen(buf) - 1] != '*')
+       return NULL;
+
+    /* if wildcard, check for matching prefix */
+    buf[strlen(buf) - 1] = 0;
+    if (strstr(hname, buf) == hname)
+       return part;
+
+    return NULL;
+}
+
+/*
+ * better dirname providing internal buffer
+ */
+char *cluster_dirname(const char *path)
+{
+    static char buf[NFS_MAXPATHLEN];
+
+    strcpy(buf, path);
+    return dirname(buf);
+}
+
+/*
+ * better basename providing internal buffer
+ */
+char *cluster_basename(const char *path)
+{
+    static char buf[NFS_MAXPATHLEN];
+
+    strcpy(buf, path);
+    return basename(buf);
+}
+
+/*
+ * free dirent array
+ */
+void cluster_freedir(void)
+{
+    /* only if it was really allocated before */
+    if (cluster_dirents) {
+       while (cluster_count--)
+           free(cluster_dirents[cluster_count]);
+       free(cluster_dirents);
+       cluster_dirents = NULL;
+    }
+}
+
+/*
+ * compare function for qsort'ing the scandir list
+ */
+int compar(const void *x, const void *y)
+{
+    return strcmp(*(const char **) x, *(const char **) y);
+}
+
+/*
+ * reset euid/egid to specific values
+ */
+static void reset_ids(uid_t euid, gid_t egid)
+{
+    if (backend_setegid(egid) || backend_seteuid(euid)) {
+       logmsg(LOG_EMERG, "euid/egid switching failed, aborting");
+       daemon_exit(CRISIS);
+    }
+}
+
+/*
+ * scan directory for filenames beginning with master name as prefix
+ */
+void cluster_scandir(const char *path)
+{
+    char prefix[NFS_MAXPATHLEN];
+    DIR *scan;
+    struct dirent *entry;
+    char **new, *name;
+    uid_t euid;
+    gid_t egid;
+
+    strcpy(prefix, cluster_basename(path));
+
+    /* 
+     * need to read directory as root, temporarily switch back
+     */
+    euid = backend_geteuid();
+    egid = backend_getegid();
+    backend_setegid(0);
+    backend_seteuid(0);
+
+    scan = backend_opendir(cluster_dirname(path));
+    if (!scan) {
+       cluster_count = -1;
+       reset_ids(euid, egid);
+       return;
+    }
+
+    cluster_count = 0;
+    while ((entry = backend_readdir(scan))) {
+       if (strstr(entry->d_name, prefix) != entry->d_name &&
+           strcmp(entry->d_name, "$$CREATE=IP$$") != 0 &&
+           strcmp(entry->d_name, "$$CREATE=CLIENT$$") != 0 &&
+           strcmp(entry->d_name, "$$ALWAYS=IP$$") != 0 &&
+           strcmp(entry->d_name, "$$ALWAYS=CLIENT$$") != 0)
+           continue;
+
+       name = malloc(strlen(entry->d_name) + 1);
+       new = realloc(cluster_dirents, (cluster_count + 1) * sizeof(char *));
+       if (!new || !name) {
+           cluster_freedir();
+           cluster_count = -1;
+           free(new);
+           free(name);
+           backend_closedir(scan);
+           reset_ids(euid, egid);
+           return;
+       }
+
+       strcpy(name, entry->d_name);
+       cluster_dirents = new;
+       cluster_dirents[cluster_count] = name;
+       cluster_count++;
+    }
+
+    backend_closedir(scan);
+    reset_ids(euid, egid);
+
+    /* list needs to be sorted for cluster_lookup_lowlevel to work */
+    qsort(cluster_dirents, cluster_count, sizeof(char *), compar);
+}
+
+/*
+ * check whether master name + suffix matches with a string
+ */
+int match_suffix(const char *master, const char *suffix, const char *entry)
+{
+    char obj[NFS_MAXPATHLEN];
+
+    sprintf(obj, "%s%s", master, suffix);
+
+    if (strcmp(entry, obj) == 0)
+       return CLU_SLAVE;
+    else
+       return FALSE;
+}
+
+/*
+ * create string version of a netmask
+ * buf:    where to put string
+ * remote: full IP address of remote machine
+ * n:      number of dots to keep
+ */
+void cluster_netmask(char *buf, const char *remote, int n)
+{
+    int i;
+
+    sprintf(buf, "$$IP=%s", remote);
+
+    /* skip to desired dot position */
+    for (i = 0; i < n; i++)
+       buf = strchr(buf, '.') + 1;
+
+    *buf-- = 0;
+
+    /* append trailer of netmask string */
+    switch (n) {
+       case 3:
+           strcat(buf, "0_24$$");
+           break;
+       case 2:
+           strcat(buf, "0.0_16$$");
+           break;
+       case 1:
+           strcat(buf, "0.0.0_8$$");
+           break;
+    }
+}
+
+/*
+ * look up cluster name, defaulting to master name if no slave name found
+ */
+int cluster_lookup_lowlevel(char *path, struct svc_req *rqstp)
+{
+    struct in_addr raddr;
+    char *remote, *hname, *master, *entry, *match;
+    char buf[NFS_MAXPATHLEN];
+    int i, res = CLU_MASTER;
+
+    cluster_freedir();
+    cluster_scandir(path);
+
+    if (cluster_count == -1)
+       return CLU_IO;
+    else if (cluster_count == 0)
+       return CLU_MASTER;
+
+    raddr = get_remote(rqstp);        /* remote IP address */
+    master = cluster_basename(path);   /* master file name */
+    remote = inet_ntoa(raddr);        /* remote IP address string */
+    hname = get_host(raddr);          /* remote hostname */
+
+    /* 
+     * traversal in reverse alphanumerical order, so that 
+     *  IP is encountered before HOST, HOST before CLIENT,
+     *  CLIENT before ALWAYS, and also subnets are encountered
+     *  in the right order
+     */
+    i = cluster_count;
+    while (i--) {
+       entry = cluster_dirents[i];
+
+       /* match specific IP address */
+       sprintf(buf, "$$IP=%s$$", remote);
+       if ((res = match_suffix(master, buf, entry)))
+           break;
+
+       /* always match IP file */
+       if ((res = match_suffix(master, "$$ALWAYS=IP$$", entry)))
+           break;
+       if (strcmp("$$ALWAYS=IP$$", entry) == 0) {
+           res = CLU_SLAVE;
+           break;
+       }
+
+       /* match all clients */
+       strcpy(buf, "$$CLIENT$$");
+       if ((res = match_suffix(master, buf, entry)))
+           break;
+
+       /* always match CLIENT file */
+       if ((res = match_suffix(master, "$$ALWAYS=CLIENT$$", entry)))
+           break;
+       if (strcmp("$$ALWAYS=CLIENT$$", entry) == 0) {
+           res = CLU_SLAVE;
+           break;
+       }
+
+       /* match 24 bit network address */
+       cluster_netmask(buf, remote, 3);
+       if ((res = match_suffix(master, buf, entry)))
+           break;
+
+       /* match 16 bit network address */
+       cluster_netmask(buf, remote, 2);
+       if ((res = match_suffix(master, buf, entry)))
+           break;
+
+       /* match 8 bit network address */
+       cluster_netmask(buf, remote, 1);
+       if ((res = match_suffix(master, buf, entry)))
+           break;
+
+       /* match hostname pattern */
+       if (!is_host(master)) {
+           match = match_host(hname, entry);
+           if (match) {
+               res = CLU_SLAVE;
+               strcpy(buf, match);
+               break;
+           }
+       }
+    }
+
+    /* append suffix if possible */
+    if (res == CLU_SLAVE) {
+       if (strlen(path) + strlen(buf) + 1 < NFS_MAXPATHLEN)
+           strcat(path, buf);
+       else
+           res = CLU_TOOLONG;
+    } else {
+       /* res will be 0 after above loop */
+       res = CLU_MASTER;
+    }
+
+    /* 
+     * dirent array not freed here since cluster_create may need
+     * to look at it afterwards
+     */
+
+    return res;
+}
+
+/*
+ * substitute slave filename if possible
+ */
+void cluster_lookup(char *path, struct svc_req *rqstp, nfsstat3 * nstat)
+{
+    int res;
+
+    if (!opt_cluster)
+       return;
+
+    if (!path)
+       return;
+
+    if (*nstat != NFS3_OK)
+       return;
+
+    if (!want_cluster(path))
+       return;
+
+    res = strlen(path);
+    if (strstr(path, "$$$$") == path + res - 4) {
+       *(path + res - 4) = 0;
+       return;
+    }
+
+    res = cluster_lookup_lowlevel(path, rqstp);
+    if (res == CLU_TOOLONG)
+       *nstat = NFS3ERR_NAMETOOLONG;
+    else if (res == CLU_IO)
+       *nstat = NFS3ERR_IO;
+}
+
+/*
+ * substitute slave filename if possible, for create operations
+ */
+void cluster_create(char *path, struct svc_req *rqstp, nfsstat3 * nstat)
+{
+    int i, res;
+    char buf[NFS_MAXPATHLEN];
+    char *master, *entry;
+
+    if (!opt_cluster)
+       return;
+
+    if (*nstat != NFS3_OK)
+       return;
+
+    if (!want_cluster(path))
+       return;
+
+    res = cluster_lookup_lowlevel(path, rqstp);
+
+    if (res == CLU_TOOLONG) {
+       *nstat = NFS3ERR_NAMETOOLONG;
+       return;
+    } else if (res == CLU_IO) {
+       *nstat = NFS3ERR_IO;
+       return;
+    } else if (res == CLU_SLAVE)
+       return;
+
+    master = cluster_basename(path);
+
+    /* look for create tag */
+    i = cluster_count;
+    while (i--) {
+       entry = cluster_dirents[i];
+
+       /* always create IP file */
+       sprintf(buf, "$$IP=%s$$", inet_ntoa(get_remote(rqstp)));
+       if ((res = match_suffix(master, "$$CREATE=IP$$", entry)) ||
+           (res = match_suffix(master, "$$ALWAYS=IP$$", entry)))
+           break;
+       if ((strcmp("$$CREATE=IP$$", entry) == 0) ||
+           (strcmp("$$ALWAYS=IP$$", entry) == 0)) {
+           res = CLU_SLAVE;
+           break;
+       }
+
+       /* always create CLIENT file */
+       sprintf(buf, "$$CLIENT$$");
+       if ((res = match_suffix(master, "$$CREATE=CLIENT$$", entry)) ||
+           (res = match_suffix(master, "$$ALWAYS=CLIENT$$", entry)))
+           break;
+       if ((strcmp("$$CREATE=CLIENT$$", entry) == 0) ||
+           (strcmp("$$ALWAYS=CLIENT$$", entry) == 0)) {
+           res = CLU_SLAVE;
+           break;
+       }
+    }
+
+    if (res != CLU_SLAVE)
+       return;
+
+    /* append suffix if possible */
+    if (strlen(path) + strlen(buf) + 1 < NFS_MAXPATHLEN)
+       strcat(path, buf);
+    else
+       *nstat = NFS3ERR_NAMETOOLONG;
+}
+
+#endif
diff --git a/unfs3/Extras/cluster.h b/unfs3/Extras/cluster.h
new file mode 100644 (file)
index 0000000..d892e2e
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * UNFS3 cluster extensions
+ * (C) 2003, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_CLUSTER_H
+#define UNFS3_CLUSTER_H
+
+#ifdef WANT_CLUSTER
+
+#define CLU_TOOLONG            0       /* name got too long   */
+#define CLU_SLAVE              1       /* slave file matched  */
+#define CLU_MASTER             2       /* master file matched */
+#define CLU_IO                 3       /* I/O error */
+
+void cluster_lookup(char *path, struct svc_req *rqstp, nfsstat3 *nstat);
+void cluster_create(char *path, struct svc_req *rqstp, nfsstat3 *nstat);
+
+#else
+
+#define cluster_lookup(x,y,z)
+#define cluster_create(x,y,z)
+
+#endif
+
+#endif
diff --git a/unfs3/Extras/tags.7 b/unfs3/Extras/tags.7
new file mode 100644 (file)
index 0000000..b95c2f3
--- /dev/null
@@ -0,0 +1,123 @@
+.\"
+.\" (C) 2004, Pascal Schmidt
+.\"
+.TH tags 7 "04 Jan 2004"
+.SH NAME
+tags \- tagged files used for clustering extensions
+.SH DESCRIPTION
+Tagged files are used by
+.B ClusterNFS
+and
+.B unfs3
+to support NFS exporting directories like
+.I /etc
+and
+.I /var
+to a cluster of client machines. The problem at hand is that different
+files need to be served to different clients. Tagged files provide a
+way to specify which file should be served to which client(s).
+.P
+Tags are appended to the end of a filename and are seperated from the
+rest of the name by beginning and ending with $$. For each file, there
+can be multiple tagged variants. The normal file, without a tag, is
+only served to clients by default when no tagged file matching the client is
+found. The following tags exist:
+.TP
+.B file$$$$
+If a client attempts to access this file, it will be redirected to the
+normal file instead, no matter what other tagged files exist. This
+is mainly useful for use inside symlinks that need to point to a normal
+file although other access to the normal file should be redirected. In
+this special case, the tagged file
+.B file$$$$
+does not even need to exist (the symlink will then seem to be dangling
+on the server).
+.TP
+.B file$$IP=a.b.c.d$$
+If a client with an IP address of
+.I a.b.c.d
+attempts to access the normal file,
+it will be redirected to this file instead.
+.TP
+.B file$$IP=a.b.c.d_n$$
+If a client with an IP adress in the network
+.I a.b.c.d/n
+attempts to
+access the normal file, it will be redirected to this file instead. Note that
+.B unfs3
+only supports 8, 16, and 24 as values for
+.BR n .
+.TP
+.B file$$HOST=name$$
+If a client with the hostname
+.I name
+attempts to access the normal file, it will be redirected to this file instead.
+.TP
+.B file$$HOST=name*$$
+If a client whose hostname begins with the string
+.I name
+attempts to access the normal file, it will be redirected to this file instead.
+.TP
+.B file$$CLIENT$$
+If any client attempts to access the normal file, it will be redirected to this
+file instead.
+.TP
+.B file$$ALWAYS=IP$$
+If any client attempts to access or create the normal file, it will be redirected to
+.B file$$IP=a.b.c.d$$
+instead, where
+.I a.b.c.d
+is the IP address of the client. It does not matter whether the target
+tagged file exists or not.
+.TP
+.B file$$ALWAYS=CLIENT$$
+If any client attempts to access or create the normal file, it will be redirected to
+.B file$$CLIENT$$
+instead. It does not matter whether that file exists or not.
+.TP
+.B $$ALWAYS=CLIENT$$
+Like above, but effective for all files in the directory where it is found.
+.TP
+.B $$ALWAYS=IP$$
+Like above, but effective for all files in the directory where it is found.
+.PP
+Note that the ALWAYS tag can lead to file not found errors on clients
+if the tagged files it redirects to does not exist on the server. For
+example,
+.BR ls (1)
+can suffer from this problem. Furthermore, this tag is a
+.B unfs3
+extension and does not exist in
+.BR ClusterNFS .
+When this tags exists, it prevents all access to the normal file by any
+client.
+.TP
+.B file$$CREATE=IP$$
+If any client attempts to create the normal file,
+.B file$$IP=a.b.c.d$$
+will be created instead, where
+.I a.b.c.d
+is the IP address of the client.
+.TP
+.B file$$CREATE=CLIENT$$
+If any client attempts to create the normal file,
+.B file$$CLIENT$$
+will be created instead.
+.TP
+.B $$CREATE=IP$$
+Like above, but effective for all files in the directory where it is found.
+.TP
+.B $$CREATE=CLIENT$$
+Like above, but effective for all files in the directory where it is found.
+.PP
+Tags work for all kinds of named filesystem objects.
+If multiple tagged files
+are found for a normal file, they are considered in the order they are
+given above, starting at the top. Processing stops once a match is found.
+In
+.BR ClusterNFS ", but not in " unfs3 ,
+only files accessible and readable by a client are considered matches.
+.SH AUTHOR
+Pascal Schmidt
+.SH "SEE ALSO"
+.BR unfsd (8)
diff --git a/unfs3/LICENSE b/unfs3/LICENSE
new file mode 100644 (file)
index 0000000..7f2b53f
--- /dev/null
@@ -0,0 +1,24 @@
+UNFS3 user-space NFSv3 server
+(C) 2003, Pascal Schmidt <unfs3-server@ewetel.net>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/unfs3/Makefile.in b/unfs3/Makefile.in
new file mode 100644 (file)
index 0000000..551aa22
--- /dev/null
@@ -0,0 +1,158 @@
+CC = @CC@
+CFLAGS = @CFLAGS@ -D_GNU_SOURCE
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+VERSION = @PACKAGE_VERSION@
+RM = rm -f
+MAKE = make
+
+SOURCES = attr.c daemon.c error.c fd_cache.c fh.c fh_cache.c locate.c \
+          md5.c mount.c nfs.c password.c readdir.c user.c xdr.c winsupport.c
+OBJS = attr.o daemon.o error.o fd_cache.o fh.o fh_cache.o locate.o \
+       md5.o mount.o nfs.o password.o readdir.o user.o xdr.o winsupport.o
+CONFOBJ = Config/lib.a
+EXTRAOBJ = @EXTRAOBJ@
+LDFLAGS = @LDFLAGS@ @LIBS@ @LEXLIB@
+EXEEXT = @EXEEXT@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+sbindir = @sbindir@
+mandir = @mandir@
+datarootdir = @datarootdir@
+
+SUBDIRS = Config @SUBDIRS@
+
+DESTDIR =
+
+all: subdirs unfsd$(EXEEXT)
+
+unfsd$(EXEEXT): $(OBJS) $(CONFOBJ) $(EXTRAOBJ)
+       $(CC) -o $@ $(OBJS) $(CONFOBJ) $(EXTRAOBJ) $(LDFLAGS)
+
+subdirs:
+       for i in $(SUBDIRS); do cd $$i; $(MAKE) all; cd ..; done
+
+install: unfsd$(EXEEXT)
+       $(INSTALL) -d $(DESTDIR)$(sbindir)
+       $(INSTALL) -d $(DESTDIR)$(mandir)/man7
+       $(INSTALL) -d $(DESTDIR)$(mandir)/man8
+       $(INSTALL) unfsd$(EXEEXT) $(DESTDIR)$(sbindir)/unfsd$(EXEEXT)
+       $(INSTALL_DATA) Extras/tags.7 $(DESTDIR)$(mandir)/man7/tags.7
+       $(INSTALL_DATA) unfsd.8       $(DESTDIR)$(mandir)/man8/unfsd.8
+
+# Not installed by default, since it installs files outside prefix
+install-init: unfsd.init
+       $(INSTALL) -d $(DESTDIR)/etc/init.d
+       $(INSTALL) unfsd.init $(DESTDIR)/etc/init.d/unfsd
+
+install-strip: install
+       strip $(DESTDIR)$(sbindir)/unfsd$(EXEEXT)
+
+uninstall:
+       $(RM) $(DESTDIR)$(sbindir)/unfsd$(EXEEXT)
+       $(RM) $(DESTDIR)$(mandir)/man7/tags.7
+       $(RM) $(DESTDIR)$(mandir)/man8/unfsd.8
+
+clean:
+       for i in $(SUBDIRS); do cd $$i; $(MAKE) clean; cd ..; done
+       $(RM) $(OBJS)
+       $(RM) unfsd$(EXEEXT)
+       $(RM) unfs3-$(VERSION).tar.gz
+
+distclean: clean
+       for i in $(SUBDIRS); do cd $$i; $(MAKE) distclean; cd ..; done
+       $(RM) Makefile config.h
+       $(RM) config.log config.status
+
+maintainer-clean: distclean
+       $(RM) config.h.in configure
+       $(RM) -rf autom4te.cache
+
+dep: $(SOURCES)
+       for i in $(SUBDIRS); do cd $$i; $(MAKE) dep; cd ..; done
+       $(CC) $(CFLAGS) -MM $(SOURCES) >> Makefile
+
+.PHONY: dist unfs3-$(VERSION).tar.gz
+
+dist: unfs3-$(VERSION).tar.gz
+
+unfs3-$(VERSION).tar.gz:
+       rm -rf /tmp/unfs3-make-dist-dir
+       mkdir /tmp/unfs3-make-dist-dir
+       ln -sf `pwd` /tmp/unfs3-make-dist-dir/unfs3-$(VERSION)
+       (cd /tmp/unfs3-make-dist-dir; \
+        tar zcf unfs3-$(VERSION)/unfs3-$(VERSION).tar.gz \
+        unfs3-$(VERSION)/fd_cache.c \
+        unfs3-$(VERSION)/md5.h \
+        unfs3-$(VERSION)/xdr.h \
+        unfs3-$(VERSION)/attr.c \
+        unfs3-$(VERSION)/README \
+        unfs3-$(VERSION)/backend.h \
+        unfs3-$(VERSION)/password.c \
+        unfs3-$(VERSION)/README.nfsroot \
+        unfs3-$(VERSION)/error.c \
+        unfs3-$(VERSION)/winsupport.c \
+        unfs3-$(VERSION)/fh_cache.h \
+        unfs3-$(VERSION)/user.c \
+        unfs3-$(VERSION)/unfs3.spec \
+        unfs3-$(VERSION)/winsupport.h \
+        unfs3-$(VERSION)/readdir.h \
+        unfs3-$(VERSION)/nfs.c \
+        unfs3-$(VERSION)/configure \
+        unfs3-$(VERSION)/xdr.c \
+        unfs3-$(VERSION)/install-sh \
+        unfs3-$(VERSION)/mount.c \
+        unfs3-$(VERSION)/password.h \
+        unfs3-$(VERSION)/Extras/Makefile.in \
+        unfs3-$(VERSION)/Extras/cluster.c \
+        unfs3-$(VERSION)/Extras/tags.7 \
+        unfs3-$(VERSION)/Extras/cluster.h \
+        unfs3-$(VERSION)/NEWS \
+        unfs3-$(VERSION)/CREDITS \
+        unfs3-$(VERSION)/aclocal.m4 \
+        unfs3-$(VERSION)/daemon.h \
+        unfs3-$(VERSION)/doc/TODO \
+        unfs3-$(VERSION)/doc/README.win \
+        unfs3-$(VERSION)/doc/kirch1.txt \
+        unfs3-$(VERSION)/doc/passwords.txt \
+        unfs3-$(VERSION)/unfsd.8 \
+        unfs3-$(VERSION)/Makefile.in \
+        unfs3-$(VERSION)/unfsd.init \
+        unfs3-$(VERSION)/backend_win32.h \
+        unfs3-$(VERSION)/indent-all \
+        unfs3-$(VERSION)/backend_unix.h \
+        unfs3-$(VERSION)/nfs.h \
+        unfs3-$(VERSION)/locate.c \
+        unfs3-$(VERSION)/BRANCHES \
+        unfs3-$(VERSION)/fd_cache.h \
+        unfs3-$(VERSION)/daemon.c \
+        unfs3-$(VERSION)/error.h \
+        unfs3-$(VERSION)/contrib/nfsotpclient/README \
+        unfs3-$(VERSION)/contrib/nfsotpclient/mountclient \
+        unfs3-$(VERSION)/contrib/nfsotpclient/mountclient/__init__.py \
+        unfs3-$(VERSION)/contrib/nfsotpclient/mountclient/mountpacker.py \
+        unfs3-$(VERSION)/contrib/nfsotpclient/mountclient/mountconstants.py \
+        unfs3-$(VERSION)/contrib/nfsotpclient/mountclient/mounttypes.py \
+        unfs3-$(VERSION)/contrib/nfsotpclient/nfsotpclient.py \
+        unfs3-$(VERSION)/contrib/nfsotpclient/rpc.py \
+        unfs3-$(VERSION)/contrib/rpcproxy/rpcproxy \
+        unfs3-$(VERSION)/LICENSE \
+        unfs3-$(VERSION)/fh.h \
+        unfs3-$(VERSION)/fh.c \
+        unfs3-$(VERSION)/Config/exports.y \
+        unfs3-$(VERSION)/Config/exports.l \
+        unfs3-$(VERSION)/Config/exports.h \
+        unfs3-$(VERSION)/Config/Makefile.in \
+        unfs3-$(VERSION)/locate.h \
+        unfs3-$(VERSION)/md5.c \
+        unfs3-$(VERSION)/fh_cache.c \
+        unfs3-$(VERSION)/config.h.in \
+        unfs3-$(VERSION)/attr.h \
+        unfs3-$(VERSION)/configure.ac \
+        unfs3-$(VERSION)/mount.h \
+        unfs3-$(VERSION)/readdir.c \
+        unfs3-$(VERSION)/user.h)
+       rm -rf /tmp/unfs3-make-dist-dir
+
+# automatically generated dependencies follow
diff --git a/unfs3/NEWS b/unfs3/NEWS
new file mode 100644 (file)
index 0000000..231eaad
--- /dev/null
@@ -0,0 +1,242 @@
+What's new or changed in 0.9.22
+===============================
+
+Garrett Cooper reported that Andy Levine found a bug
+where the "no_all_squash" option is unusable due to a
+spelling mistake in the options parser. This is fixed now.
+
+Wesley Shields reported build problems on more recent
+FreeBSD systems which are fixed now.
+
+Bernhard Duebi reported problems with filesystem size
+reporting when using a Solaris Zone. This is fixed now.
+
+The BUGS section of the manpage has been updated to
+explicitly list the permissions that UNFS3 will need
+for exported directories and the parents of exported
+directories.
+
+
+ChangeLog
+=========
+
+Version 0.9.22
+
+  - fix spelling mistake in "no_all_squash" export option parsing
+  - fix build on more recent FreeBSD systems
+  - fix filesystem size reporting for Solaris Zones and others
+  - document permission needs for exported directories
+
+Version 0.9.21
+
+  - avoid returning large fileids on Windows, for better compatibility
+    with clients such as Linux 2.6.24
+  - try to prevent races between READDIR and removal of files
+
+Version 0.9.20
+
+  - fix LOOKUP permission reporting on execute-bit-only directories
+  - fix writing of files opened for writing with intervening chmod -w
+  - fix reading of files opened for reading with intervening chmod -r
+
+Version 0.9.19
+
+  - fix chown operation on symbolic links
+  - fix auxiliary group support
+
+Version 0.9.18
+
+  - add support for writing a pid file with the -i option
+  - fix atomic_attr() to respect uid/gid squashing
+  - add -T option for doing test parse of the exports file
+
+Version 0.9.17
+
+  - add support for 64 bit inode numbers
+  - the returned "fileid" is now equal to the file's real inode number
+  - fix ACCESS and read_executable to stop adding permissions when user or 
+    group matches
+  - report unreadable executables as unreadable unless -r is used
+  - support for using larger read and write sizes: up to 32K for UDP and 
+    512K for TCP
+  - add support for "anonuid" and "anongid" export options
+  - add Windows support
+  - improve error reporting for WRITE and COMMIT requests
+  - improve file descriptor cache to not keep files open forever
+
+Version 0.9.16
+
+  - add support for "insecure" and "secure" export options
+  - do not abort daemon on RPC or XDR failures
+
+Version 0.9.15
+
+  - only allow mount requests from source ports below 1024
+
+Version 0.9.14
+
+  - accept SET_TO_SERVER_TIME in object creation calls
+  - update maintainer email address
+
+Version 0.9.13
+
+  - add support for CREATE EXCLUSIVE procedure call
+  - fix some minor compiler warnings
+
+Version 0.9.12
+
+  - add option for binding to a specified interface
+  - lots of compile fixes for Solaris/AIX/Irix/Mac OS X
+  - make redirection of operating system calls possible
+  - fix READDIR error reporting
+  - fix handling of . and .. lookups for Irix clients
+
+Version 0.9.11
+
+  - handle case where a tagged file exists but the master file doesn't
+  - make clustering work in non-readable directories
+  - make filehandle resolution work on non-readable directories
+  - fix file creation by Solaris NFS clients
+
+Version 0.9.10
+
+  - fix building with gcc 2.95.x or 2.96
+  - make ALWAYS cluster tag take effect for file creations
+  - fix possible use-after-free bug in fh.c:fh_rec
+
+Version 0.9.9
+
+  - add support for mount passwords, for use with secure links
+  - make exports parser accept dashes in hostnames and leading whitespace
+  - fix debug mode to use line buffering, useful for redirections
+  - make exporting of (automounted) removable media possible
+
+Version 0.9.8
+
+  - fix cache LRU algorithm, could lead to unreliable operations once
+    the filehandle cache filled up
+
+Version 0.9.7
+
+  - make renames across directories not cause stale filehandles
+  - allow clients to read files they only have execute permission for
+  - lift restrictions on attribute setting
+  - fix bug in special-casing of root directory filehandles
+  - add file$$$$ tag to clustering extensions
+
+Version 0.9.6
+
+  - run the source through indent to improve style consistency
+  - fix bug in READDIR that resulted in truncated replies
+  - add options to specify which ports and protocols to use
+
+Version 0.9.5
+
+  - code cleanups, remove now impossible cases (stale filehandles)
+  - fix bug in READDIR that could return EOF one entry too early
+
+Version 0.9.4
+
+  - support the DUMP and EXPORT mount protocol procedures
+  - use a simple counter instead of the system time for cache LRU
+  - document tagged files in a seperate manpage, tags(7)
+  - introduce a new ALWAYS tag for forced redirection
+
+Version 0.9.3
+
+  - fix mount handler not properly initializing return status 
+  - send messages to syslog for some mount problems
+  - make it possible to export and mount symlinks to directories
+  - fix cluster extensions breakage from 0.9.1 code merge
+  - prevent filehandle cache from storing redundant information
+
+Version 0.9.2
+
+  - clean up lots of duplicate code in nfs.c
+  - move configuration parser and cluster extensions into subdirectories
+  - use real write verifier in WRITE and COMMIT
+
+Version 0.9.1
+
+  - add -d option to prevent forking into background (for debugging)
+  - merge in lots of cleaner code from the experimental branch
+  - merge nfsd and mountd into one program
+  - better /etc/exports parser using lex and yacc
+
+Version 0.9.0
+
+  - check for clients attempting to use invalid pathnames
+  - include cluster extensions (optional at compile and run time)
+
+Version 0.8.8
+
+  - fix ACCESS compatibility problem affecting HP-UX clients
+
+Version 0.8.7
+
+  - fixed chmod handling to make us pass the Connectathon 2003 NFS
+    testsuite's "basic" and "general" tests
+  - improve filehandle integrity checking
+
+Version 0.8.6
+
+  - use inode generation numbers only in SETATTR and fd cache because
+    it is a huge performance hit everywhere else
+  - use 32 bit device and inode numbers to save space in filehandles
+  - have nfsd bind to port number 2049 (nfs) by default
+
+Version 0.8.5
+
+  - undo refusal to use "." and ".." since it confuses NetBSD clients
+  - include inode generation number in filehandles whenever possible
+
+Version 0.8.4
+
+  - handle export of the root directory correctly
+  - return XDEV error in RENAME and LINK if export options for given
+    objects do not match (needed for squash options differences)
+  - prevent the use of "." and ".." as names
+  - use largefile API to enable access to files greater than 2 GB
+  - check filehandles for integrity before using them
+  - we no longer need GNU make
+
+Version 0.8.3
+
+  - use shorter filehandles whenever possible
+  - use stat cache in unfs3_readdir()
+  - switch filehandle cache to use static allocation of entries
+  - make unfs3_readdir() account for XDR overhead correctly
+
+Version 0.8.2
+
+  - simplify and speed up low-level filehandle routines
+  - fix a serious bug in READDIR, cookie == 0 was handled incorrectly
+  - make it build and run on NetBSD 1.6.1/i386
+
+Version 0.8.1
+
+  - use svc_getcaller instead of our own hack (well, duh)
+  - don't attempt to service v1 mount calls, only v1 umount
+  - check incoming object names for validity
+
+Version 0.8
+
+  - support for export controls via /etc/exports
+  - introduce caching of stat() results to improve performance
+  - explicitly check for gcc in the configure script
+  - make user/group id squashing be based on export options
+  - remove the now obsolete -r and -a nfsd options
+
+Version 0.7
+
+  - attempt at race protection/detection code for many NFS procedures
+  - add comments to all error code paths
+
+Version 0.6.1
+
+  - document possible races with local fs activity on server
+  - minor fix in LOOKUP error handling
+
+Version 0.6
+
+  - initial public release
diff --git a/unfs3/README b/unfs3/README
new file mode 100644 (file)
index 0000000..d8a7059
--- /dev/null
@@ -0,0 +1,89 @@
+UNFS3 (User-Space NFSv3 Server)
+(C) 2003-2006, Pascal Schmidt <unfs3-server@ewetel.net>
+
+
+INTRODUCTION
+============
+
+UNFS3 is a user-space implementation of the NFSv3 server
+specification.
+
+UNFS3 supports all NFSv3 procedures with the exception of the
+READDIRPLUS procedure. It tries to provide as much information
+to NFS clients as possible, within the limits possible from
+user-space.
+
+See the unfsd(8) manpage for restrictions imposed on NFS
+operations (section RESTRICTIONS) and for possible races
+with local file system activity (section BUGS).
+
+It is not possible to export to netgroups or wildcard hostnames
+via /etc/exports, all other addressing methods should work. The
+following options are recognized in the exports file: ro, rw,
+root_squash, all_squash, no_root_squash, no_all_squash. If
+other options are present, they are ignored.
+
+Cluster extensions compatible to the older ClusterNFS project
+are supported when the source is configured with --enable-cluster.
+
+
+SUPPORTED SYSTEMS
+=================
+
+Linux:         compiles and runs, passes Connectathon 2003 NFS
+               testsuite's "basic" and "general" tests, survives
+               fsx stress testing
+
+Solaris:       compiles and runs, survives light testing
+
+NetBSD:        compiles and runs, but there are compatibility
+               problems with the in-kernel NFS client
+
+AIX:           compiles
+
+Irix:          compiles (with gcc and native compiler)
+
+OS X:          compiles
+
+Note that only Linux support is checked for all releases. If unfs3
+breaks on other platforms, a bug report is appreciated.
+
+
+INSTALLATION
+============
+
+You will need gcc, lex (flex), and yacc (bison) to compile UNFS3.
+
+       ./configure
+       make
+       make install
+
+Please read the manpage for information about command-line
+options.
+
+       man 8 unfsd
+
+If you decide to modify the code yourself, you can run
+
+       make dep
+       
+to append dependency information to the Makefile, so that make
+knows which files depend on each other and recompiles all the
+necessary files on changes.
+
+
+CVS REPOSITORY
+==============
+
+The unfs3 CVS repository is accessible via anonymous CVS. The source
+can be checked out with:
+
+  cvs -d:pserver:anonymous@cvs.lysator.liu.se:/cvsroot/unfs3 login
+  cvs -d:pserver:anonymous@cvs.lysator.liu.se:/cvsroot/unfs3 co unfs3
+
+You can also use the web-based CVS repository viewer at: 
+
+  http://cvs.lysator.liu.se/viewcvs/viewcvs.cgi/?cvsroot=unfs3
+
+In the HEAD branch, you will find a file called "BRANCHES", which
+describes the different branches in this repository. 
diff --git a/unfs3/README.nfsroot b/unfs3/README.nfsroot
new file mode 100644 (file)
index 0000000..6b2fb1b
--- /dev/null
@@ -0,0 +1,44 @@
+Using UNFS3 for Linux nfsroot
+=============================
+
+General information about Linux nfsroot can be found in the
+Linux kernel source, in the file Documentation/nfsroot.txt.
+
+If you want the use the Linux kernel's "nfsroot=" boot option
+to use a root directory on an NFS server and use UNFS3 in the
+role of the NFS server for that, you need to remember that UNFS3
+only supports NFSv3, not NFSv2. The kernel, on the other hand,
+always defaults to using NFSv2. Thus, you need to modify the
+nfsroot boot option to force the kernel to use NFSv3. If you do
+not do this, an error message like this will appear on the
+client machine:
+
+  Looking up port of RPC 1000003/2 on 172.16.100.100
+  Root-NFS: Portmapper on server returned 2049 as nfsd port
+  Looking up port of RPC 1000005/1 on 172.16.100.100
+  Root-NFS: mounted port is 2049
+  NFS:      nfs_mount (ac106464:/nfsroot)
+  RPC: call_verify: programm 100003 version 2 unsupported by server
+  nfs_get_root: getattr error = 5
+  nfs_read_super: get root inode failed
+  VFS: Unable to mount root fs via NFS trying floppy
+
+To fix this problem, append the "v3" NFS option to the nfsroot
+boot option. Assuming your NFS server's IP address is 192.168.2.72
+and the path you need to mount is /tftpboot/nfsroot, the boot
+option should look like this:
+
+  nfsroot=192.168.2.72:/tftpboot/nfsroot,v3
+
+You can add more options to the end, seperated by commas. If you
+use DHCP to pass the NFS server configuration to the client, you
+need to use a line like this in the /etc/dhcpd.conf settings
+for the client machine:
+
+  option root-path "/tftpboot/nfsroot,v3";
+
+As above, more options can be added to the end, sepereated by
+commas.
+
+Thanks go to Jean Aumont <JeanAumont@videotron.ca> for suggesting
+this bit of information to be documented.
diff --git a/unfs3/aclocal.m4 b/unfs3/aclocal.m4
new file mode 100644 (file)
index 0000000..1a2845e
--- /dev/null
@@ -0,0 +1,25 @@
+dnl Special rpc library for Solaris
+dnl
+AC_DEFUN([UNFS3_SOLARIS_RPC],[
+  AC_CHECK_FUNC(svc_tli_create, [
+    # On Solaris, you must link with librpcsoc, or the binaries won't work. 
+    LDFLAGS="-L/usr/ucblib -R/usr/ucblib $LDFLAGS"
+    AC_CHECK_LIB(rpcsoc, svctcp_create, 
+        [ LIBS="-lrpcsoc $LIBS" ],
+        [ AC_MSG_WARN([*** Cannot find librpcsoc. On Solaris, install package SUNWscpu. ***]) ]
+    )
+  ])
+])
+dnl PORTMAP define needed for Solaris
+dnl
+AC_DEFUN([UNFS3_PORTMAP_DEFINE],[
+  AC_DEFINE([PORTMAP], [], [Define to an empty value if you use Solaris.])
+])
+dnl Set compiler warnings for gcc
+dnl
+AC_DEFUN([UNFS3_COMPILE_WARNINGS],[
+  if test "$GCC" = "yes"
+  then
+    CFLAGS="$CFLAGS -Wall -W"
+  fi
+])
diff --git a/unfs3/attr.c b/unfs3/attr.c
new file mode 100644 (file)
index 0000000..2653ed0
--- /dev/null
@@ -0,0 +1,496 @@
+
+/*
+ * UNFS3 attribute handling
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <fcntl.h>
+#include <time.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <utime.h>
+#include <errno.h>
+
+#include "backend.h"
+#include "nfs.h"
+#include "attr.h"
+#include "error.h"
+#include "fh.h"
+#include "fh_cache.h"
+#include "daemon.h"
+#include "user.h"
+#include "Config/exports.h"
+
+/*
+ * check whether stat_cache is for a regular file
+ *
+ * fh_decomp must be called before to fill the stat cache
+ */
+nfsstat3 is_reg(void)
+{
+    if (!st_cache_valid)
+       return NFS3ERR_STALE;
+    else if (S_ISREG(st_cache.st_mode))
+       return NFS3_OK;
+    else
+       return NFS3ERR_INVAL;
+}
+
+/*
+ * find stat bit corresponding to given NFS file type
+ */
+mode_t type_to_mode(ftype3 ftype)
+{
+    switch (ftype) {
+       case NF3REG:
+           return S_IFREG;
+       case NF3DIR:
+           return S_IFDIR;
+       case NF3LNK:
+           return S_IFLNK;
+       case NF3CHR:
+           return S_IFCHR;
+       case NF3BLK:
+           return S_IFBLK;
+       case NF3FIFO:
+           return S_IFIFO;
+       case NF3SOCK:
+           return S_IFSOCK;
+    }
+
+    /* fix gcc warning */
+    return 0;
+}
+
+/*
+ * post_op_attr for error returns
+ */
+#ifdef __GNUC__
+static post_op_attr error_attr = {.attributes_follow = FALSE };
+#else
+static post_op_attr error_attr = { FALSE };
+#endif
+
+/*
+ * return pre-operation attributes
+ *
+ * fh_decomp must be called before to fill the stat cache
+ */
+pre_op_attr get_pre_cached(void)
+{
+    pre_op_attr result;
+
+    if (!st_cache_valid) {
+       result.attributes_follow = FALSE;
+       return result;
+    }
+
+    result.attributes_follow = TRUE;
+
+    result.pre_op_attr_u.attributes.size = st_cache.st_size;
+    result.pre_op_attr_u.attributes.mtime.seconds = st_cache.st_mtime;
+    result.pre_op_attr_u.attributes.mtime.nseconds = 0;
+    result.pre_op_attr_u.attributes.ctime.seconds = st_cache.st_ctime;
+    result.pre_op_attr_u.attributes.ctime.nseconds = 0;
+
+    return result;
+}
+
+/*
+ * compute post-operation attributes given a stat buffer
+ */
+post_op_attr get_post_buf(backend_statstruct buf, struct svc_req * req)
+{
+    post_op_attr result;
+
+    result.attributes_follow = TRUE;
+
+    if (S_ISDIR(buf.st_mode))
+       result.post_op_attr_u.attributes.type = NF3DIR;
+    else if (S_ISBLK(buf.st_mode))
+       result.post_op_attr_u.attributes.type = NF3BLK;
+    else if (S_ISCHR(buf.st_mode))
+       result.post_op_attr_u.attributes.type = NF3CHR;
+#ifdef S_ISLNK
+    else if (S_ISLNK(buf.st_mode))
+       result.post_op_attr_u.attributes.type = NF3LNK;
+#endif                                /* S_ISLNK */
+#ifdef S_ISSOCK
+    else if (S_ISSOCK(buf.st_mode))
+       result.post_op_attr_u.attributes.type = NF3SOCK;
+#endif                                /* S_ISSOCK */
+    else if (S_ISFIFO(buf.st_mode))
+       result.post_op_attr_u.attributes.type = NF3FIFO;
+    else
+       result.post_op_attr_u.attributes.type = NF3REG;
+
+    /* adapt permissions for executable files */
+    if (opt_readable_executables && S_ISREG(buf.st_mode)) {
+       if (buf.st_mode & S_IXUSR)
+           buf.st_mode |= S_IRUSR;
+       if (buf.st_mode & S_IXGRP)
+           buf.st_mode |= S_IRGRP;
+       if (buf.st_mode & S_IXOTH)
+           buf.st_mode |= S_IROTH;
+    }
+
+    result.post_op_attr_u.attributes.mode = buf.st_mode & 0xFFFF;
+    result.post_op_attr_u.attributes.nlink = buf.st_nlink;
+
+    /* If -s, translate uids */
+    if (opt_singleuser) {
+       unsigned int req_uid = 0;
+       unsigned int req_gid = 0;
+       struct authunix_parms *auth = (void *) req->rq_clntcred;
+       uid_t ruid = backend_getuid();
+
+       if (req->rq_cred.oa_flavor == AUTH_UNIX) {
+           req_uid = auth->aup_uid;
+           req_gid = auth->aup_gid;
+       }
+
+       if ((buf.st_uid == ruid) || (ruid == 0))
+           result.post_op_attr_u.attributes.uid = req_uid;
+       else
+           result.post_op_attr_u.attributes.uid = 0;
+
+       if ((buf.st_gid == backend_getgid()) || (ruid == 0))
+           result.post_op_attr_u.attributes.gid = req_gid;
+       else
+           result.post_op_attr_u.attributes.gid = 0;
+    } else {
+       /* Normal case */
+       result.post_op_attr_u.attributes.uid = buf.st_uid;
+       result.post_op_attr_u.attributes.gid = buf.st_gid;
+    }
+
+    result.post_op_attr_u.attributes.size = buf.st_size;
+    result.post_op_attr_u.attributes.used = buf.st_blocks * 512;
+    result.post_op_attr_u.attributes.rdev.specdata1 =
+       (buf.st_rdev >> 8) & 0xFF;
+    result.post_op_attr_u.attributes.rdev.specdata2 = buf.st_rdev & 0xFF;
+    result.post_op_attr_u.attributes.fsid = buf.st_dev;
+
+    /* If this is a removable export point, we should return the preset fsid
+       for all objects which resides in the same file system as the exported
+       directory */
+    if (exports_opts & OPT_REMOVABLE) {
+       backend_statstruct epbuf;
+
+       if (backend_lstat(export_path, &epbuf) != -1 &&
+           buf.st_dev == epbuf.st_dev) {
+           result.post_op_attr_u.attributes.fsid = export_fsid;
+       }
+    }
+#ifdef WIN32
+    /* Recent Linux kernels (2.6.24 and newer) exposes large fileids even to
+       non-LFS 32-bit applications, unless kernel parameter
+       nfs.enable_ino64=0. This means that applications will fail with
+       EOVERFLOW. On Windows, we always have large st_ino:s. To avoid
+       trouble, we truncate to 32 bits */
+    result.post_op_attr_u.attributes.fileid =
+       (buf.st_ino >> 32) ^ (buf.st_ino & 0xffffffff);
+#else
+    result.post_op_attr_u.attributes.fileid = buf.st_ino;
+#endif
+    result.post_op_attr_u.attributes.atime.seconds = buf.st_atime;
+    result.post_op_attr_u.attributes.atime.nseconds = 0;
+    result.post_op_attr_u.attributes.mtime.seconds = buf.st_mtime;
+    result.post_op_attr_u.attributes.mtime.nseconds = 0;
+    result.post_op_attr_u.attributes.ctime.seconds = buf.st_ctime;
+    result.post_op_attr_u.attributes.ctime.nseconds = 0;
+
+    return result;
+}
+
+/*
+ * lowlevel routine for getting post-operation attributes
+ */
+static post_op_attr get_post_ll(const char *path, uint32 dev, uint64 ino,
+                               struct svc_req *req)
+{
+    backend_statstruct buf;
+    int res;
+
+    if (!path)
+       return error_attr;
+
+    res = backend_lstat(path, &buf);
+    if (res == -1)
+       return error_attr;
+
+    /* protect against local fs race */
+    if (dev != buf.st_dev || ino != buf.st_ino)
+       return error_attr;
+
+    return get_post_buf(buf, req);
+}
+
+/*
+ * return post-operation attributes, using fh for old dev/ino
+ */
+post_op_attr get_post_attr(const char *path, nfs_fh3 nfh,
+                          struct svc_req * req)
+{
+    unfs3_fh_t *fh = (void *) nfh.data.data_val;
+
+    return get_post_ll(path, fh->dev, fh->ino, req);
+}
+
+/*
+ * return post-operation attributes, using stat cache for old dev/ino
+ */
+post_op_attr get_post_stat(const char *path, struct svc_req * req)
+{
+    return get_post_ll(path, st_cache.st_dev, st_cache.st_ino, req);
+}
+
+/*
+ * return post-operation attributes using stat cache
+ *
+ * fd_decomp must be called before to fill the stat cache
+ */
+post_op_attr get_post_cached(struct svc_req * req)
+{
+    if (!st_cache_valid)
+       return error_attr;
+
+    return get_post_buf(st_cache, req);
+}
+
+/*
+ * setting of time, races with local filesystem
+ *
+ * there is no futimes() function in POSIX or Linux
+ */
+static nfsstat3 set_time(const char *path, backend_statstruct buf, sattr3 new)
+{
+    time_t new_atime, new_mtime;
+    struct utimbuf utim;
+    int res;
+
+    /* set atime and mtime */
+    if (new.atime.set_it != DONT_CHANGE || new.mtime.set_it != DONT_CHANGE) {
+
+       /* compute atime to set */
+       if (new.atime.set_it == SET_TO_SERVER_TIME)
+           new_atime = time(NULL);
+       else if (new.atime.set_it == SET_TO_CLIENT_TIME)
+           new_atime = new.atime.set_atime_u.atime.seconds;
+       else                           /* DONT_CHANGE */
+           new_atime = buf.st_atime;
+
+       /* compute mtime to set */
+       if (new.mtime.set_it == SET_TO_SERVER_TIME)
+           new_mtime = time(NULL);
+       else if (new.mtime.set_it == SET_TO_CLIENT_TIME)
+           new_mtime = new.mtime.set_mtime_u.mtime.seconds;
+       else                           /* DONT_CHANGE */
+           new_mtime = buf.st_mtime;
+
+       utim.actime = new_atime;
+       utim.modtime = new_mtime;
+
+       res = backend_utime(path, &utim);
+       if (res == -1)
+           return setattr_err();
+    }
+
+    return NFS3_OK;
+}
+
+/*
+ * race unsafe setting of attributes
+ */
+static nfsstat3 set_attr_unsafe(const char *path, nfs_fh3 nfh, sattr3 new)
+{
+    unfs3_fh_t *fh = (void *) nfh.data.data_val;
+    uid_t new_uid;
+    gid_t new_gid;
+    backend_statstruct buf;
+    int res;
+
+    res = backend_lstat(path, &buf);
+    if (res != 0)
+       return NFS3ERR_STALE;
+
+    /* check local fs race */
+    if (buf.st_dev != fh->dev || buf.st_ino != fh->ino)
+       return NFS3ERR_STALE;
+
+    /* set file size */
+    if (new.size.set_it == TRUE) {
+       res = backend_truncate(path, new.size.set_size3_u.size);
+       if (res == -1)
+           return setattr_err();
+    }
+
+    /* set uid and gid */
+    if (new.uid.set_it == TRUE || new.gid.set_it == TRUE) {
+       if (new.uid.set_it == TRUE)
+           new_uid = new.uid.set_uid3_u.uid;
+       else
+           new_uid = -1;
+       if (new_uid == buf.st_uid)
+           new_uid = -1;
+
+       if (new.gid.set_it == TRUE)
+           new_gid = new.gid.set_gid3_u.gid;
+       else
+           new_gid = -1;
+
+       res = backend_lchown(path, new_uid, new_gid);
+       if (res == -1)
+           return setattr_err();
+    }
+
+    /* set mode */
+    if (new.mode.set_it == TRUE) {
+       res = backend_chmod(path, new.mode.set_mode3_u.mode);
+       if (res == -1)
+           return setattr_err();
+    }
+
+    return set_time(path, buf, new);
+}
+
+/*
+ * set attributes of an object
+ */
+nfsstat3 set_attr(const char *path, nfs_fh3 nfh, sattr3 new)
+{
+    unfs3_fh_t *fh = (void *) nfh.data.data_val;
+    int res, fd;
+    uid_t new_uid;
+    gid_t new_gid;
+    backend_statstruct buf;
+
+    res = backend_lstat(path, &buf);
+    if (res != 0)
+       return NFS3ERR_STALE;
+
+    /* 
+     * don't open(2) device nodes, it could trigger
+     * module loading on the server
+     */
+    if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
+       return set_attr_unsafe(path, nfh, new);
+
+#ifdef S_ISLNK
+    /* 
+     * opening a symlink would open the underlying file,
+     * don't try to do that
+     */
+    if (S_ISLNK(buf.st_mode))
+       return set_attr_unsafe(path, nfh, new);
+#endif
+
+    /* 
+     * open object for atomic setting of attributes
+     */
+    fd = backend_open(path, O_WRONLY | O_NONBLOCK);
+    if (fd == -1)
+       fd = backend_open(path, O_RDONLY | O_NONBLOCK);
+
+    if (fd == -1)
+       return set_attr_unsafe(path, nfh, new);
+
+    res = backend_fstat(fd, &buf);
+    if (res == -1) {
+       backend_close(fd);
+       return NFS3ERR_STALE;
+    }
+
+    /* check local fs race */
+    if (fh->dev != buf.st_dev || fh->ino != buf.st_ino ||
+       fh->gen != backend_get_gen(buf, fd, path)) {
+       backend_close(fd);
+       return NFS3ERR_STALE;
+    }
+
+    /* set file size */
+    if (new.size.set_it == TRUE) {
+       res = backend_ftruncate(fd, new.size.set_size3_u.size);
+       if (res == -1) {
+           backend_close(fd);
+           return setattr_err();
+       }
+    }
+
+    /* set uid and gid */
+    if (new.uid.set_it == TRUE || new.gid.set_it == TRUE) {
+       if (new.uid.set_it == TRUE)
+           new_uid = new.uid.set_uid3_u.uid;
+       else
+           new_uid = -1;
+       if (new_uid == buf.st_uid)
+           new_uid = -1;
+
+       if (new.gid.set_it == TRUE)
+           new_gid = new.gid.set_gid3_u.gid;
+       else
+           new_gid = -1;
+
+       res = backend_fchown(fd, new_uid, new_gid);
+       if (res == -1) {
+           backend_close(fd);
+           return setattr_err();
+       }
+    }
+
+    /* set mode */
+    if (new.mode.set_it == TRUE) {
+       res = backend_fchmod(fd, new.mode.set_mode3_u.mode);
+       if (res == -1) {
+           backend_close(fd);
+           return setattr_err();
+       }
+    }
+
+    res = backend_close(fd);
+    if (res == -1) {
+       /* error on close probably means attributes didn't make it */
+       return NFS3ERR_IO;
+    }
+
+    /* finally, set times */
+    return set_time(path, buf, new);
+}
+
+/*
+ * deduce mode from given settable attributes
+ * default to rwxrwxr-x if no mode given
+ */
+mode_t create_mode(sattr3 new)
+{
+    if (new.mode.set_it == TRUE)
+       return new.mode.set_mode3_u.mode;
+    else
+       return S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
+           S_IROTH | S_IXOTH;
+}
+
+/*
+ * check whether an sattr3 is settable atomically on a create op
+ */
+nfsstat3 atomic_attr(sattr3 attr)
+{
+    uid_t used_uid = mangle_uid(attr.uid.set_uid3_u.uid);
+    gid_t used_gid = mangle_gid(attr.gid.set_gid3_u.gid);
+
+    if ((attr.uid.set_it == TRUE && used_uid != backend_geteuid()) ||
+       (attr.gid.set_it == TRUE && used_gid != backend_getegid()) ||
+       (attr.size.set_it == TRUE && attr.size.set_size3_u.size != 0) ||
+       attr.atime.set_it == SET_TO_CLIENT_TIME ||
+       attr.mtime.set_it == SET_TO_CLIENT_TIME)
+       return NFS3ERR_INVAL;
+    else
+       return NFS3_OK;
+}
diff --git a/unfs3/attr.h b/unfs3/attr.h
new file mode 100644 (file)
index 0000000..4b2b0bc
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * UNFS3 attribute handling
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef NFS_ATTR_H
+#define NFS_ATTR_H
+
+nfsstat3 is_reg(void);
+
+mode_t type_to_mode(ftype3 ftype);
+
+post_op_attr get_post_attr(const char *path, nfs_fh3 fh, struct svc_req *req);
+post_op_attr get_post_stat(const char *path, struct svc_req *req);
+post_op_attr get_post_cached(struct svc_req *req);
+post_op_attr get_post_buf(backend_statstruct buf, struct svc_req *req);
+pre_op_attr  get_pre_cached(void);
+
+nfsstat3 set_attr(const char *path, nfs_fh3 fh, sattr3 sattr);
+
+mode_t create_mode(sattr3 sattr);
+
+nfsstat3 atomic_attr(sattr3 sattr);
+
+#endif
diff --git a/unfs3/backend.h b/unfs3/backend.h
new file mode 100644 (file)
index 0000000..d3c595d
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * UNFS3 low-level filesystem calls
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_BACKEND_H
+#define UNFS3_BACKEND_H
+
+#ifdef WIN32
+#include "backend_win32.h"
+#else
+#include "backend_unix.h"
+#endif /* WIN32 */
+
+#endif
diff --git a/unfs3/backend_unix.h b/unfs3/backend_unix.h
new file mode 100644 (file)
index 0000000..713288d
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * UNFS3 low-level filesystem calls for Unix
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_BACKEND_UNIX_H
+#define UNFS3_BACKEND_UNIX_H
+
+/*
+ * backend init and shutdown
+ */
+#define backend_init() 1
+#define backend_shutdown() do { } while (0)
+
+/*
+ * unfs3 functions
+ */
+#define backend_get_gen get_gen
+#define backend_mksocket mksocket
+#define backend_locate_file locate_file
+
+/*
+ * system calls
+ */
+#define backend_chmod chmod
+#define backend_chown chown
+#define backend_close close
+#define backend_closedir closedir
+#define backend_fchmod fchmod
+#define backend_fchown fchown
+#define backend_fstat fstat
+#define backend_fsync fsync
+#define backend_ftruncate ftruncate
+#define backend_getegid getegid
+#define backend_geteuid geteuid
+#define backend_getgid getgid
+#define backend_getuid getuid
+#define backend_link link
+#define backend_lseek lseek
+#define backend_lstat lstat
+#define backend_mkdir mkdir
+#define backend_mkfifo mkfifo
+#define backend_mknod mknod
+#define backend_open open
+#define backend_open_create open
+#define backend_opendir opendir
+#define backend_pread pread
+#define backend_pwrite pwrite
+#define backend_readdir readdir
+#define backend_readlink readlink
+#define backend_realpath realpath
+#define backend_remove remove
+#define backend_rename rename
+#define backend_rmdir rmdir
+#define backend_setegid setegid
+#define backend_seteuid seteuid
+#define backend_setgroups setgroups
+#define backend_stat stat
+#define backend_statvfs statvfs
+#define backend_symlink symlink
+#define backend_truncate truncate
+#define backend_utime utime
+#define backend_statstruct struct stat
+#define backend_dirstream DIR
+#define backend_statvfsstruct struct statvfs
+#define backend_fsinfo_properties FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME;
+#define backend_time_delta_seconds 1
+#define backend_pathconf_case_insensitive FALSE
+#define backend_passwdstruct struct passwd
+#define backend_getpwnam getpwnam
+#define backend_gen_nonce gen_nonce
+#define backend_flock flock
+#define backend_getpid getpid
+#define backend_store_create_verifier store_create_verifier
+#define backend_check_create_verifier check_create_verifier
+
+#if HAVE_LCHOWN == 1
+#define backend_lchown lchown
+#else
+#define backend_lchown chown
+#endif
+
+#endif
diff --git a/unfs3/backend_win32.h b/unfs3/backend_win32.h
new file mode 100644 (file)
index 0000000..03878a3
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * UNFS3 low-level filesystem calls for Win32
+ * (C) 2006, Peter Ã…strand
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_BACKEND_WIN32_H
+#define UNFS3_BACKEND_WIN32_H
+
+#include "winsupport.h"
+
+/*
+ * backend init and shutdown
+ */
+#define backend_shutdown() do { } while (0)
+
+/*
+ * unfs3 functions
+ */
+#define backend_get_gen get_gen
+#define backend_mksocket win_mkfifo
+#define backend_locate_file locate_file
+
+/*
+ * system calls
+ */
+#define backend_chmod win_chmod
+#define backend_chown win_chown
+#define backend_close win_close
+#define backend_closedir win_closedir
+#define backend_fchmod win_fchmod
+#define backend_fchown win_fchown
+#define backend_fstat win_fstat
+#define backend_fsync _commit
+#define backend_ftruncate chsize
+#define backend_getegid() 0
+#define backend_geteuid() 0
+#define backend_getgid() 0
+#define backend_getuid() 0
+#define backend_lchown win_chown
+#define backend_link win_link
+#define backend_lseek lseek
+#define backend_lstat win_stat
+#define backend_mkdir win_mkdir
+#define backend_mkfifo win_mkfifo
+#define backend_mknod win_mknod
+#define backend_open win_open
+#define backend_open_create win_open
+#define backend_opendir win_opendir
+#define backend_pread pread
+#define backend_pwrite pwrite
+#define backend_readdir win_readdir
+#define backend_readlink win_readlink
+#define backend_realpath win_realpath
+#define backend_remove win_remove
+#define backend_rename win_rename 
+#define backend_rmdir win_rmdir
+#define backend_setegid win_setegid
+#define backend_seteuid win_seteuid
+#define backend_setgroups(size, groups) 0
+#define backend_stat win_stat
+#define backend_statvfs win_statvfs
+#define backend_symlink win_symlink
+#define backend_truncate win_truncate
+#define backend_utime win_utime
+#define backend_init win_init
+#define backend_dirstream UNFS3_WIN_DIR
+#define backend_fsinfo_properties FSF3_HOMOGENEOUS | FSF3_CANSETTIME;
+/*
+  Note: FAT has different granularities for different times: 1 day for
+  atime, 2 seconds for mtime and 10ms för CreationTime. time_delta
+  only applies to atime/mtime. We are choosing 2 seconds.
+*/
+#define backend_time_delta_seconds 2
+#define backend_pathconf_case_insensitive TRUE
+#define backend_getpwnam(name) NULL
+#define backend_gen_nonce win_gen_nonce
+#define backend_flock flock(fd, op) (-1)
+#define backend_getpid getpid
+#define backend_store_create_verifier win_store_create_verifier
+#define backend_check_create_verifier win_check_create_verifier
+
+#endif
diff --git a/unfs3/bootstrap b/unfs3/bootstrap
new file mode 100755 (executable)
index 0000000..68f4a17
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+autoreconf -i
diff --git a/unfs3/config.h.in b/unfs3/config.h.in
new file mode 100644 (file)
index 0000000..f50dddd
--- /dev/null
@@ -0,0 +1,119 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if the system has the type `int32'. */
+#undef HAVE_INT32
+
+/* Define to 1 if the system has the type `int64'. */
+#undef HAVE_INT64
+
+/* Define to 1 if you have the `lchown' function. */
+#undef HAVE_LCHOWN
+
+/* Define to 1 if you have the <linux/ext2_fs.h> header file. */
+#undef HAVE_LINUX_EXT2_FS_H
+
+/* Define to 1 if you have the <mntent.h> header file. */
+#undef HAVE_MNTENT_H
+
+/* Define to 1 if you have the <rpc/svc_soc.h> header file. */
+#undef HAVE_RPC_SVC_SOC_H
+
+/* Define to 1 if you have the `setegid' function. */
+#undef HAVE_SETEGID
+
+/* Define to 1 if you have the `seteuid' function. */
+#undef HAVE_SETEUID
+
+/* Define to 1 if you have the `setgroups' function. */
+#undef HAVE_SETGROUPS
+
+/* Define to 1 if you have the `setresgid' function. */
+#undef HAVE_SETRESGID
+
+/* Define to 1 if you have the `setresuid' function. */
+#undef HAVE_SETRESUID
+
+/* Define to 1 if you have the `statvfs' function. */
+#undef HAVE_STATVFS
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if `st_gen' is member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_GEN
+
+/* Define to 1 if `xp_fd' is member of `struct __rpc_svcxprt'. */
+#undef HAVE_STRUCT___RPC_SVCXPRT_XP_FD
+
+/* Define to 1 if you have the <sys/mnttab.h> header file. */
+#undef HAVE_SYS_MNTTAB_H
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#undef HAVE_SYS_MOUNT_H
+
+/* Define to 1 if you have the <sys/vmount.h> header file. */
+#undef HAVE_SYS_VMOUNT_H
+
+/* Define to 1 if the system has the type `uint32'. */
+#undef HAVE_UINT32
+
+/* Define to 1 if the system has the type `uint64'. */
+#undef HAVE_UINT64
+
+/* Define to 1 if you have the `vsyslog' function. */
+#undef HAVE_VSYSLOG
+
+/* Define to 1 if you have the `xdr_int32' function. */
+#undef HAVE_XDR_INT32
+
+/* Define to 1 if you have the `xdr_long' function. */
+#undef HAVE_XDR_LONG
+
+/* Define to 1 if you have the `xdr_uint32' function. */
+#undef HAVE_XDR_UINT32
+
+/* Define to 1 if you have the `xdr_uint64' function. */
+#undef HAVE_XDR_UINT64
+
+/* Define to 1 if you have the `xdr_uint64_t' function. */
+#undef HAVE_XDR_UINT64_T
+
+/* Define to 1 if you have the `xdr_u_int64_t' function. */
+#undef HAVE_XDR_U_INT64_T
+
+/* Define to 1 if you have the `xdr_u_long' function. */
+#undef HAVE_XDR_U_LONG
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to an empty value if you use Solaris. */
+#undef PORTMAP
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Cluster extensions */
+#undef WANT_CLUSTER
+
+/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
+   `char[]'. */
+#undef YYTEXT_POINTER
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
diff --git a/unfs3/configure b/unfs3/configure
new file mode 100755 (executable)
index 0000000..07ec5cd
--- /dev/null
@@ -0,0 +1,6835 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.61 for unfs3 0.9.22.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+  if (eval ":") 2>/dev/null; then
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+
+  if test $as_have_required = yes &&    (eval ":
+(as_func_return () {
+  (exit \$1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+  as_lineno_1=\$LINENO
+  as_lineno_2=\$LINENO
+  test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+  test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+  :
+else
+  as_candidate_shells=
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  case $as_dir in
+        /*)
+          for as_base in sh bash ksh sh5; do
+            as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+          done;;
+       esac
+done
+IFS=$as_save_IFS
+
+
+      for as_shell in $as_candidate_shells $SHELL; do
+        # Try only shells that exist, to save several forks.
+        if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+               { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+  CONFIG_SHELL=$as_shell
+              as_have_required=yes
+              if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+  (exit $1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+  break
+fi
+
+fi
+
+      done
+
+      if test "x$CONFIG_SHELL" != x; then
+  for as_var in BASH_ENV ENV
+        do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+        done
+        export CONFIG_SHELL
+        exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+    if test $as_have_required = no; then
+  echo This script requires a shell more modern than all the
+      echo shells that I found on your system.  Please install a
+      echo modern shell, or manually run the script under such a
+      echo shell if you do have one.
+      { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+  (exit \$1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+  echo No shell found that supports shell functions.
+  echo Please tell autoconf@gnu.org about your system,
+  echo including any error possibly output before this
+  echo message
+}
+
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line after each line using $LINENO; the second 'sed'
+  # does the real work.  The second script uses 'N' to pair each
+  # line-number line with the line containing $LINENO, and appends
+  # trailing '-' during substitution so that $LINENO is not a special
+  # case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # scripts with optimization help from Paolo Bonzini.  Blame Lee
+  # E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+  case `echo 'x\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  *)   ECHO_C='\c';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s='ln -s'
+  # ... but there are two gotchas:
+  # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+  # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+  # In both cases, we have to default to `cp -p'.
+  ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+    as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+        test -d "$1/.";
+      else
+       case $1 in
+        -*)set "./$1";;
+       esac;
+       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+       ???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME='unfs3'
+PACKAGE_TARNAME='unfs3'
+PACKAGE_VERSION='0.9.22'
+PACKAGE_STRING='unfs3 0.9.22'
+PACKAGE_BUGREPORT=''
+
+ac_subst_vars='SHELL
+PATH_SEPARATOR
+PACKAGE_NAME
+PACKAGE_TARNAME
+PACKAGE_VERSION
+PACKAGE_STRING
+PACKAGE_BUGREPORT
+exec_prefix
+prefix
+program_transform_name
+bindir
+sbindir
+libexecdir
+datarootdir
+datadir
+sysconfdir
+sharedstatedir
+localstatedir
+includedir
+oldincludedir
+docdir
+infodir
+htmldir
+dvidir
+pdfdir
+psdir
+libdir
+localedir
+mandir
+DEFS
+ECHO_C
+ECHO_N
+ECHO_T
+LIBS
+build_alias
+host_alias
+target_alias
+INSTALL_PROGRAM
+INSTALL_SCRIPT
+INSTALL_DATA
+CC
+CFLAGS
+LDFLAGS
+CPPFLAGS
+ac_ct_CC
+EXEEXT
+OBJEXT
+LEX
+LEX_OUTPUT_ROOT
+LEXLIB
+YACC
+YFLAGS
+CPP
+GREP
+EGREP
+SUBDIRS
+EXTRAOBJ
+LIBOBJS
+LTLIBOBJS'
+ac_subst_files=''
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+YACC
+YFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *)   ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+    eval enable_$ac_feature=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+    eval enable_$ac_feature=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+    eval with_$ac_package=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+    eval with_$ac_package=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute directory names.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  { echo "$as_me: error: Working directory cannot be determined" >&2
+   { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  { echo "$as_me: error: pwd does not report name of working directory" >&2
+   { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$0" ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$0" : 'X\(//\)[^/]' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2
+   { (exit 1); exit 1; }; }
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures unfs3 0.9.22 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                         [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                         [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --datarootdir=DIR      read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR          read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR          info documentation [DATAROOTDIR/info]
+  --localedir=DIR        locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR           man documentation [DATAROOTDIR/man]
+  --docdir=DIR           documentation root [DATAROOTDIR/doc/unfs3]
+  --htmldir=DIR          html documentation [DOCDIR]
+  --dvidir=DIR           dvi documentation [DOCDIR]
+  --pdfdir=DIR           pdf documentation [DOCDIR]
+  --psdir=DIR            ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of unfs3 0.9.22:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --disable-largefile     omit support for large files
+  --enable-cluster        include clustering extensions
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  YACC        The `Yet Another C Compiler' implementation to use. Defaults to
+              the first program found out of: `bison -y', `byacc', `yacc'.
+  YFLAGS      The list of arguments that will be passed by default to $YACC.
+              This script will default YFLAGS to the empty string to avoid a
+              default value of `-d' given by some make applications.
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" || continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+unfs3 configure 0.9.22
+generated by GNU Autoconf 2.61
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by unfs3 $as_me 0.9.22, which was
+generated by GNU Autoconf 2.61.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      ac_configure_args="$ac_configure_args '$ac_arg'"
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      *) $as_unset $ac_var ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -n "$CONFIG_SITE"; then
+  set x "$CONFIG_SITE"
+elif test "x$prefix" != xNONE; then
+  set x "$prefix/share/config.site" "$prefix/etc/config.site"
+else
+  set x "$ac_default_prefix/share/config.site" \
+       "$ac_default_prefix/etc/config.site"
+fi
+shift
+for ac_site_file
+do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+       { echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+       { echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+       ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5
+echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+       if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+         if test $ac_prog = install &&
+           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         elif test $ac_prog = install &&
+           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # program-specific install script used by HP pwplus--don't use.
+           :
+         else
+           ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+           break 3
+         fi
+       fi
+      done
+    done
+    ;;
+esac
+done
+IFS=$as_save_IFS
+
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in gcc egcs kgcc cc
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in gcc egcs kgcc cc
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet.  If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet.  If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO: checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compiler --version >&5") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compiler -v >&5") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compiler -V >&5") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; }
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+#
+# List of possible output files, starting from the most likely.
+# The algorithm is not robust to junk in `.', hence go to wildcards (a.*)
+# only as a last resort.  b.out is created by i960 compilers.
+ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out'
+#
+# The IRIX 6 linker writes into existing files which may not be
+# executable, retaining their permissions.  Remove them first so a
+# subsequent execution test works.
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+        if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+
+{ echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6; }
+if test -z "$ac_file"; then
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+{ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; }
+{ echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6; }
+
+{ echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; }
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; }
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       CFLAGS=""
+      cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_c89=$ac_arg
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6; } ;;
+  xno)
+    { echo "$as_me:$LINENO: result: unsupported" >&5
+echo "${ECHO_T}unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+for ac_prog in flex lex
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_LEX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$LEX"; then
+  ac_cv_prog_LEX="$LEX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_LEX="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+LEX=$ac_cv_prog_LEX
+if test -n "$LEX"; then
+  { echo "$as_me:$LINENO: result: $LEX" >&5
+echo "${ECHO_T}$LEX" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  test -n "$LEX" && break
+done
+test -n "$LEX" || LEX=":"
+
+if test "x$LEX" != "x:"; then
+  cat >conftest.l <<_ACEOF
+%%
+a { ECHO; }
+b { REJECT; }
+c { yymore (); }
+d { yyless (1); }
+e { yyless (input () != 0); }
+f { unput (yytext[0]); }
+. { BEGIN INITIAL; }
+%%
+#ifdef YYTEXT_POINTER
+extern char *yytext;
+#endif
+int
+main (void)
+{
+  return ! yylex () + ! yywrap ();
+}
+_ACEOF
+{ (ac_try="$LEX conftest.l"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$LEX conftest.l") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ echo "$as_me:$LINENO: checking lex output file root" >&5
+echo $ECHO_N "checking lex output file root... $ECHO_C" >&6; }
+if test "${ac_cv_prog_lex_root+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+if test -f lex.yy.c; then
+  ac_cv_prog_lex_root=lex.yy
+elif test -f lexyy.c; then
+  ac_cv_prog_lex_root=lexyy
+else
+  { { echo "$as_me:$LINENO: error: cannot find output from $LEX; giving up" >&5
+echo "$as_me: error: cannot find output from $LEX; giving up" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_lex_root" >&5
+echo "${ECHO_T}$ac_cv_prog_lex_root" >&6; }
+LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root
+
+if test -z "${LEXLIB+set}"; then
+  { echo "$as_me:$LINENO: checking lex library" >&5
+echo $ECHO_N "checking lex library... $ECHO_C" >&6; }
+if test "${ac_cv_lib_lex+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+    ac_save_LIBS=$LIBS
+    ac_cv_lib_lex='none needed'
+    for ac_lib in '' -lfl -ll; do
+      LIBS="$ac_lib $ac_save_LIBS"
+      cat >conftest.$ac_ext <<_ACEOF
+`cat $LEX_OUTPUT_ROOT.c`
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_lex=$ac_lib
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+      test "$ac_cv_lib_lex" != 'none needed' && break
+    done
+    LIBS=$ac_save_LIBS
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_lex" >&5
+echo "${ECHO_T}$ac_cv_lib_lex" >&6; }
+  test "$ac_cv_lib_lex" != 'none needed' && LEXLIB=$ac_cv_lib_lex
+fi
+
+
+{ echo "$as_me:$LINENO: checking whether yytext is a pointer" >&5
+echo $ECHO_N "checking whether yytext is a pointer... $ECHO_C" >&6; }
+if test "${ac_cv_prog_lex_yytext_pointer+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # POSIX says lex can declare yytext either as a pointer or an array; the
+# default is implementation-dependent.  Figure out which it is, since
+# not all implementations provide the %pointer and %array declarations.
+ac_cv_prog_lex_yytext_pointer=no
+ac_save_LIBS=$LIBS
+LIBS="$LEXLIB $ac_save_LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+#define YYTEXT_POINTER 1
+`cat $LEX_OUTPUT_ROOT.c`
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_prog_lex_yytext_pointer=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_save_LIBS
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_lex_yytext_pointer" >&5
+echo "${ECHO_T}$ac_cv_prog_lex_yytext_pointer" >&6; }
+if test $ac_cv_prog_lex_yytext_pointer = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define YYTEXT_POINTER 1
+_ACEOF
+
+fi
+rm -f conftest.l $LEX_OUTPUT_ROOT.c
+
+fi
+for ac_prog in 'bison -y' byacc
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_YACC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$YACC"; then
+  ac_cv_prog_YACC="$YACC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_YACC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+YACC=$ac_cv_prog_YACC
+if test -n "$YACC"; then
+  { echo "$as_me:$LINENO: result: $YACC" >&5
+echo "${ECHO_T}$YACC" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  test -n "$YACC" && break
+done
+test -n "$YACC" || YACC="yacc"
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
+echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # Extract the first word of "grep ggrep" to use in msg output
+if test -z "$GREP"; then
+set dummy grep ggrep; ac_prog_name=$2
+if test "${ac_cv_path_GREP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_path_GREP_found=false
+# Loop through the user's path and test for each of PROGNAME-LIST
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in grep ggrep; do
+  for ac_exec_ext in '' $ac_executable_extensions; do
+    ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+    { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+    # Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+
+    $ac_path_GREP_found && break 3
+  done
+done
+
+done
+IFS=$as_save_IFS
+
+
+fi
+
+GREP="$ac_cv_path_GREP"
+if test -z "$GREP"; then
+  { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
+echo "${ECHO_T}$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     # Extract the first word of "egrep" to use in msg output
+if test -z "$EGREP"; then
+set dummy egrep; ac_prog_name=$2
+if test "${ac_cv_path_EGREP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_path_EGREP_found=false
+# Loop through the user's path and test for each of PROGNAME-LIST
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in egrep; do
+  for ac_exec_ext in '' $ac_executable_extensions; do
+    ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+    { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+    # Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+
+    $ac_path_EGREP_found && break 3
+  done
+done
+
+done
+IFS=$as_save_IFS
+
+
+fi
+
+EGREP="$ac_cv_path_EGREP"
+if test -z "$EGREP"; then
+  { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+
+   fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
+echo "${ECHO_T}$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# Check whether --enable-largefile was given.
+if test "${enable_largefile+set}" = set; then
+  enableval=$enable_largefile;
+fi
+
+if test "$enable_largefile" != no; then
+
+  { echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5
+echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6; }
+if test "${ac_cv_sys_largefile_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_sys_largefile_CC=no
+     if test "$GCC" != yes; then
+       ac_save_CC=$CC
+       while :; do
+        # IRIX 6.2 and later do not support large files by default,
+        # so use the C compiler's -n32 option if that helps.
+        cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+                      && LARGE_OFF_T % 2147483647 == 1)
+                     ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+        rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+        CC="$CC -n32"
+        rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_sys_largefile_CC=' -n32'; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+        break
+       done
+       CC=$ac_save_CC
+       rm -f conftest.$ac_ext
+    fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5
+echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6; }
+  if test "$ac_cv_sys_largefile_CC" != no; then
+    CC=$CC$ac_cv_sys_largefile_CC
+  fi
+
+  { echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5
+echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6; }
+if test "${ac_cv_sys_file_offset_bits+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  while :; do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+                      && LARGE_OFF_T % 2147483647 == 1)
+                     ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_sys_file_offset_bits=no; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#define _FILE_OFFSET_BITS 64
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+                      && LARGE_OFF_T % 2147483647 == 1)
+                     ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_sys_file_offset_bits=64; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  ac_cv_sys_file_offset_bits=unknown
+  break
+done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5
+echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6; }
+case $ac_cv_sys_file_offset_bits in #(
+  no | unknown) ;;
+  *)
+cat >>confdefs.h <<_ACEOF
+#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits
+_ACEOF
+;;
+esac
+rm -f conftest*
+  if test $ac_cv_sys_file_offset_bits = unknown; then
+    { echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5
+echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6; }
+if test "${ac_cv_sys_large_files+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  while :; do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+                      && LARGE_OFF_T % 2147483647 == 1)
+                     ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_sys_large_files=no; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#define _LARGE_FILES 1
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+                      && LARGE_OFF_T % 2147483647 == 1)
+                     ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_sys_large_files=1; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  ac_cv_sys_large_files=unknown
+  break
+done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5
+echo "${ECHO_T}$ac_cv_sys_large_files" >&6; }
+case $ac_cv_sys_large_files in #(
+  no | unknown) ;;
+  *)
+cat >>confdefs.h <<_ACEOF
+#define _LARGE_FILES $ac_cv_sys_large_files
+_ACEOF
+;;
+esac
+rm -f conftest*
+  fi
+fi
+
+{ echo "$as_me:$LINENO: checking for library containing xdr_int" >&5
+echo $ECHO_N "checking for library containing xdr_int... $ECHO_C" >&6; }
+if test "${ac_cv_search_xdr_int+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char xdr_int ();
+int
+main ()
+{
+return xdr_int ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' nsl; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_search_xdr_int=$ac_res
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext
+  if test "${ac_cv_search_xdr_int+set}" = set; then
+  break
+fi
+done
+if test "${ac_cv_search_xdr_int+set}" = set; then
+  :
+else
+  ac_cv_search_xdr_int=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_xdr_int" >&5
+echo "${ECHO_T}$ac_cv_search_xdr_int" >&6; }
+ac_res=$ac_cv_search_xdr_int
+if test "$ac_res" != no; then
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+{ echo "$as_me:$LINENO: checking for library containing socket" >&5
+echo $ECHO_N "checking for library containing socket... $ECHO_C" >&6; }
+if test "${ac_cv_search_socket+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' socket; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_search_socket=$ac_res
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext
+  if test "${ac_cv_search_socket+set}" = set; then
+  break
+fi
+done
+if test "${ac_cv_search_socket+set}" = set; then
+  :
+else
+  ac_cv_search_socket=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_socket" >&5
+echo "${ECHO_T}$ac_cv_search_socket" >&6; }
+ac_res=$ac_cv_search_socket
+if test "$ac_res" != no; then
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+{ echo "$as_me:$LINENO: checking for library containing inet_aton" >&5
+echo $ECHO_N "checking for library containing inet_aton... $ECHO_C" >&6; }
+if test "${ac_cv_search_inet_aton+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char inet_aton ();
+int
+main ()
+{
+return inet_aton ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' resolv; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_search_inet_aton=$ac_res
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext
+  if test "${ac_cv_search_inet_aton+set}" = set; then
+  break
+fi
+done
+if test "${ac_cv_search_inet_aton+set}" = set; then
+  :
+else
+  ac_cv_search_inet_aton=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_inet_aton" >&5
+echo "${ECHO_T}$ac_cv_search_inet_aton" >&6; }
+ac_res=$ac_cv_search_inet_aton
+if test "$ac_res" != no; then
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+for ac_header in mntent.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in stdint.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/mnttab.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/mount.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/vmount.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in rpc/svc_soc.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <rpc/rpc.h>
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in linux/ext2_fs.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <unistd.h>
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ echo "$as_me:$LINENO: checking for int32" >&5
+echo $ECHO_N "checking for int32... $ECHO_C" >&6; }
+if test "${ac_cv_type_int32+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/inttypes.h>
+
+typedef int32 ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+  return 0;
+if (sizeof (ac__type_new_))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_type_int32=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_int32=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_int32" >&5
+echo "${ECHO_T}$ac_cv_type_int32" >&6; }
+if test $ac_cv_type_int32 = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_INT32 1
+_ACEOF
+
+
+fi
+
+{ echo "$as_me:$LINENO: checking for uint32" >&5
+echo $ECHO_N "checking for uint32... $ECHO_C" >&6; }
+if test "${ac_cv_type_uint32+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/inttypes.h>
+
+typedef uint32 ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+  return 0;
+if (sizeof (ac__type_new_))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_type_uint32=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_uint32=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_uint32" >&5
+echo "${ECHO_T}$ac_cv_type_uint32" >&6; }
+if test $ac_cv_type_uint32 = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UINT32 1
+_ACEOF
+
+
+fi
+
+{ echo "$as_me:$LINENO: checking for int64" >&5
+echo $ECHO_N "checking for int64... $ECHO_C" >&6; }
+if test "${ac_cv_type_int64+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/inttypes.h>
+
+typedef int64 ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+  return 0;
+if (sizeof (ac__type_new_))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_type_int64=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_int64=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_int64" >&5
+echo "${ECHO_T}$ac_cv_type_int64" >&6; }
+if test $ac_cv_type_int64 = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_INT64 1
+_ACEOF
+
+
+fi
+
+{ echo "$as_me:$LINENO: checking for uint64" >&5
+echo $ECHO_N "checking for uint64... $ECHO_C" >&6; }
+if test "${ac_cv_type_uint64+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/inttypes.h>
+
+typedef uint64 ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+  return 0;
+if (sizeof (ac__type_new_))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_type_uint64=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_uint64=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_uint64" >&5
+echo "${ECHO_T}$ac_cv_type_uint64" >&6; }
+if test $ac_cv_type_uint64 = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UINT64 1
+_ACEOF
+
+
+fi
+
+{ echo "$as_me:$LINENO: checking for struct stat.st_gen" >&5
+echo $ECHO_N "checking for struct stat.st_gen... $ECHO_C" >&6; }
+if test "${ac_cv_member_struct_stat_st_gen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/stat.h>
+
+int
+main ()
+{
+static struct stat ac_aggr;
+if (ac_aggr.st_gen)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_member_struct_stat_st_gen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/stat.h>
+
+int
+main ()
+{
+static struct stat ac_aggr;
+if (sizeof ac_aggr.st_gen)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_member_struct_stat_st_gen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_member_struct_stat_st_gen=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_gen" >&5
+echo "${ECHO_T}$ac_cv_member_struct_stat_st_gen" >&6; }
+if test $ac_cv_member_struct_stat_st_gen = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_GEN 1
+_ACEOF
+
+
+fi
+
+{ echo "$as_me:$LINENO: checking for struct __rpc_svcxprt.xp_fd" >&5
+echo $ECHO_N "checking for struct __rpc_svcxprt.xp_fd... $ECHO_C" >&6; }
+if test "${ac_cv_member_struct___rpc_svcxprt_xp_fd+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <rpc/rpc.h>
+
+int
+main ()
+{
+static struct __rpc_svcxprt ac_aggr;
+if (ac_aggr.xp_fd)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_member_struct___rpc_svcxprt_xp_fd=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <rpc/rpc.h>
+
+int
+main ()
+{
+static struct __rpc_svcxprt ac_aggr;
+if (sizeof ac_aggr.xp_fd)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_member_struct___rpc_svcxprt_xp_fd=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_member_struct___rpc_svcxprt_xp_fd=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_member_struct___rpc_svcxprt_xp_fd" >&5
+echo "${ECHO_T}$ac_cv_member_struct___rpc_svcxprt_xp_fd" >&6; }
+if test $ac_cv_member_struct___rpc_svcxprt_xp_fd = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT___RPC_SVCXPRT_XP_FD 1
+_ACEOF
+
+
+fi
+
+
+
+
+
+for ac_func in xdr_long xdr_int32 xdr_u_long xdr_uint32
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+
+for ac_func in xdr_uint64 xdr_uint64_t xdr_u_int64_t
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in statvfs
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+for ac_func in seteuid setegid
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+for ac_func in setresuid setresgid
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in vsyslog
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in lchown
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in setgroups
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+  { echo "$as_me:$LINENO: checking for svc_tli_create" >&5
+echo $ECHO_N "checking for svc_tli_create... $ECHO_C" >&6; }
+if test "${ac_cv_func_svc_tli_create+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define svc_tli_create to an innocuous variant, in case <limits.h> declares svc_tli_create.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define svc_tli_create innocuous_svc_tli_create
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char svc_tli_create (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef svc_tli_create
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char svc_tli_create ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_svc_tli_create || defined __stub___svc_tli_create
+choke me
+#endif
+
+int
+main ()
+{
+return svc_tli_create ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_func_svc_tli_create=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_svc_tli_create=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_svc_tli_create" >&5
+echo "${ECHO_T}$ac_cv_func_svc_tli_create" >&6; }
+if test $ac_cv_func_svc_tli_create = yes; then
+
+    # On Solaris, you must link with librpcsoc, or the binaries won't work.
+    LDFLAGS="-L/usr/ucblib -R/usr/ucblib $LDFLAGS"
+    { echo "$as_me:$LINENO: checking for svctcp_create in -lrpcsoc" >&5
+echo $ECHO_N "checking for svctcp_create in -lrpcsoc... $ECHO_C" >&6; }
+if test "${ac_cv_lib_rpcsoc_svctcp_create+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrpcsoc  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char svctcp_create ();
+int
+main ()
+{
+return svctcp_create ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_rpcsoc_svctcp_create=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_rpcsoc_svctcp_create=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_rpcsoc_svctcp_create" >&5
+echo "${ECHO_T}$ac_cv_lib_rpcsoc_svctcp_create" >&6; }
+if test $ac_cv_lib_rpcsoc_svctcp_create = yes; then
+   LIBS="-lrpcsoc $LIBS"
+else
+   { echo "$as_me:$LINENO: WARNING: *** Cannot find librpcsoc. On Solaris, install package SUNWscpu. ***" >&5
+echo "$as_me: WARNING: *** Cannot find librpcsoc. On Solaris, install package SUNWscpu. ***" >&2;}
+
+fi
+
+
+fi
+
+
+
+
+cat >>confdefs.h <<\_ACEOF
+#define PORTMAP
+_ACEOF
+
+
+
+  if test "$GCC" = "yes"
+  then
+    CFLAGS="$CFLAGS -Wall -W"
+  fi
+
+# Check whether --enable-cluster was given.
+if test "${enable_cluster+set}" = set; then
+  enableval=$enable_cluster;
+cat >>confdefs.h <<\_ACEOF
+#define WANT_CLUSTER
+_ACEOF
+ SUBDIRS=Extras
+ EXTRAOBJ=Extras/lib.a
+
+fi
+
+ac_config_files="$ac_config_files Makefile Config/Makefile Extras/Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      *) $as_unset $ac_var ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    test "x$cache_file" != "x/dev/null" &&
+      { echo "$as_me:$LINENO: updating cache $cache_file" >&5
+echo "$as_me: updating cache $cache_file" >&6;}
+    cat confcache >$cache_file
+  else
+    { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line after each line using $LINENO; the second 'sed'
+  # does the real work.  The second script uses 'N' to pair each
+  # line-number line with the line containing $LINENO, and appends
+  # trailing '-' during substitution so that $LINENO is not a special
+  # case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # scripts with optimization help from Paolo Bonzini.  Blame Lee
+  # E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+  case `echo 'x\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  *)   ECHO_C='\c';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s='ln -s'
+  # ... but there are two gotchas:
+  # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+  # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+  # In both cases, we have to default to `cp -p'.
+  ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+    as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+        test -d "$1/.";
+      else
+       case $1 in
+        -*)set "./$1";;
+       esac;
+       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+       ???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by unfs3 $as_me 0.9.22, which was
+generated by GNU Autoconf 2.61.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+                  instantiate the configuration file FILE
+  --header=FILE[:TEMPLATE]
+                  instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+unfs3 config.status 0.9.22
+configured by $0, generated by GNU Autoconf 2.61,
+  with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2006 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    echo "$ac_cs_version"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    { echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  CONFIG_SHELL=$SHELL
+  export CONFIG_SHELL
+  exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "Config/Makefile") CONFIG_FILES="$CONFIG_FILES Config/Makefile" ;;
+    "Extras/Makefile") CONFIG_FILES="$CONFIG_FILES Extras/Makefile" ;;
+
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp=
+  trap 'exit_status=$?
+  { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+#
+# Set up the sed scripts for CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "$CONFIG_FILES"; then
+
+_ACEOF
+
+
+
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  cat >conf$$subs.sed <<_ACEOF
+SHELL!$SHELL$ac_delim
+PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim
+PACKAGE_NAME!$PACKAGE_NAME$ac_delim
+PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim
+PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim
+PACKAGE_STRING!$PACKAGE_STRING$ac_delim
+PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim
+exec_prefix!$exec_prefix$ac_delim
+prefix!$prefix$ac_delim
+program_transform_name!$program_transform_name$ac_delim
+bindir!$bindir$ac_delim
+sbindir!$sbindir$ac_delim
+libexecdir!$libexecdir$ac_delim
+datarootdir!$datarootdir$ac_delim
+datadir!$datadir$ac_delim
+sysconfdir!$sysconfdir$ac_delim
+sharedstatedir!$sharedstatedir$ac_delim
+localstatedir!$localstatedir$ac_delim
+includedir!$includedir$ac_delim
+oldincludedir!$oldincludedir$ac_delim
+docdir!$docdir$ac_delim
+infodir!$infodir$ac_delim
+htmldir!$htmldir$ac_delim
+dvidir!$dvidir$ac_delim
+pdfdir!$pdfdir$ac_delim
+psdir!$psdir$ac_delim
+libdir!$libdir$ac_delim
+localedir!$localedir$ac_delim
+mandir!$mandir$ac_delim
+DEFS!$DEFS$ac_delim
+ECHO_C!$ECHO_C$ac_delim
+ECHO_N!$ECHO_N$ac_delim
+ECHO_T!$ECHO_T$ac_delim
+LIBS!$LIBS$ac_delim
+build_alias!$build_alias$ac_delim
+host_alias!$host_alias$ac_delim
+target_alias!$target_alias$ac_delim
+INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim
+INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim
+INSTALL_DATA!$INSTALL_DATA$ac_delim
+CC!$CC$ac_delim
+CFLAGS!$CFLAGS$ac_delim
+LDFLAGS!$LDFLAGS$ac_delim
+CPPFLAGS!$CPPFLAGS$ac_delim
+ac_ct_CC!$ac_ct_CC$ac_delim
+EXEEXT!$EXEEXT$ac_delim
+OBJEXT!$OBJEXT$ac_delim
+LEX!$LEX$ac_delim
+LEX_OUTPUT_ROOT!$LEX_OUTPUT_ROOT$ac_delim
+LEXLIB!$LEXLIB$ac_delim
+YACC!$YACC$ac_delim
+YFLAGS!$YFLAGS$ac_delim
+CPP!$CPP$ac_delim
+GREP!$GREP$ac_delim
+EGREP!$EGREP$ac_delim
+SUBDIRS!$SUBDIRS$ac_delim
+EXTRAOBJ!$EXTRAOBJ$ac_delim
+LIBOBJS!$LIBOBJS$ac_delim
+LTLIBOBJS!$LTLIBOBJS$ac_delim
+_ACEOF
+
+  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 59; then
+    break
+  elif $ac_last_try; then
+    { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
+if test -n "$ac_eof"; then
+  ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
+  ac_eof=`expr $ac_eof + 1`
+fi
+
+cat >>$CONFIG_STATUS <<_ACEOF
+cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end
+_ACEOF
+sed '
+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
+s/^/s,@/; s/!/@,|#_!!_#|/
+:n
+t n
+s/'"$ac_delim"'$/,g/; t
+s/$/\\/; p
+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
+' >>$CONFIG_STATUS <conf$$subs.sed
+rm -f conf$$subs.sed
+cat >>$CONFIG_STATUS <<_ACEOF
+:end
+s/|#_!!_#|//g
+CEOF$ac_eof
+_ACEOF
+
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[    ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+fi # test -n "$CONFIG_FILES"
+
+
+for ac_tag in  :F $CONFIG_FILES  :H $CONFIG_HEADERS
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5
+echo "$as_me: error: Invalid tag $ac_tag." >&2;}
+   { (exit 1); exit 1; }; };;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+   { (exit 1); exit 1; }; };;
+      esac
+      ac_file_inputs="$ac_file_inputs $ac_f"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input="Generated from "`IFS=:
+         echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure."
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    fi
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$tmp/stdin";;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  { as_dir="$ac_dir"
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+echo "$as_me: error: cannot create directory $as_dir" >&2;}
+   { (exit 1); exit 1; }; }; }
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+
+case `sed -n '/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p
+' $ac_file_inputs` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+    s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s&@configure_input@&$configure_input&;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+$ac_datarootdir_hack
+" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+  { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&5
+echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&2;}
+
+  rm -f "$tmp/stdin"
+  case $ac_file in
+  -) cat "$tmp/out"; rm -f "$tmp/out";;
+  *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;;
+  esac
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+_ACEOF
+
+# Transform confdefs.h into a sed script `conftest.defines', that
+# substitutes the proper values into config.h.in to produce config.h.
+rm -f conftest.defines conftest.tail
+# First, append a space to every undef/define line, to ease matching.
+echo 's/$/ /' >conftest.defines
+# Then, protect against being on the right side of a sed subst, or in
+# an unquoted here document, in config.status.  If some macros were
+# called several times there might be several #defines for the same
+# symbol, which is useless.  But do not sort them, since the last
+# AC_DEFINE must be honored.
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where
+# NAME is the cpp macro being defined, VALUE is the value it is being given.
+# PARAMS is the parameter list in the macro definition--in most cases, it's
+# just an empty string.
+ac_dA='s,^\\([  #]*\\)[^        ]*\\([  ]*'
+ac_dB='\\)[     (].*,\\1define\\2'
+ac_dC=' '
+ac_dD=' ,'
+
+uniq confdefs.h |
+  sed -n '
+       t rset
+       :rset
+       s/^[     ]*#[    ]*define[       ][      ]*//
+       t ok
+       d
+       :ok
+       s/[\\&,]/\\&/g
+       s/^\('"$ac_word_re"'\)\(([^()]*)\)[      ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p
+       s/^\('"$ac_word_re"'\)[  ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p
+  ' >>conftest.defines
+
+# Remove the space that was appended to ease matching.
+# Then replace #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+# (The regexp can be short, since the line contains either #define or #undef.)
+echo 's/ $//
+s,^[    #]*u.*,/* & */,' >>conftest.defines
+
+# Break up conftest.defines:
+ac_max_sed_lines=50
+
+# First sed command is:         sed -f defines.sed $ac_file_inputs >"$tmp/out1"
+# Second one is:        sed -f defines.sed "$tmp/out1" >"$tmp/out2"
+# Third one will be:    sed -f defines.sed "$tmp/out2" >"$tmp/out1"
+# et cetera.
+ac_in='$ac_file_inputs'
+ac_out='"$tmp/out1"'
+ac_nxt='"$tmp/out2"'
+
+while :
+do
+  # Write a here document:
+    cat >>$CONFIG_STATUS <<_ACEOF
+    # First, check the format of the line:
+    cat >"\$tmp/defines.sed" <<\\CEOF
+/^[     ]*#[    ]*undef[        ][      ]*$ac_word_re[  ]*\$/b def
+/^[     ]*#[    ]*define[       ][      ]*$ac_word_re[(         ]/b def
+b
+:def
+_ACEOF
+  sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS
+  echo 'CEOF
+    sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS
+  ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in
+  sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail
+  grep . conftest.tail >/dev/null || break
+  rm -f conftest.defines
+  mv conftest.tail conftest.defines
+done
+rm -f conftest.defines conftest.tail
+
+echo "ac_result=$ac_in" >>$CONFIG_STATUS
+cat >>$CONFIG_STATUS <<\_ACEOF
+  if test x"$ac_file" != x-; then
+    echo "/* $configure_input  */" >"$tmp/config.h"
+    cat "$ac_result" >>"$tmp/config.h"
+    if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then
+      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f $ac_file
+      mv "$tmp/config.h" $ac_file
+    fi
+  else
+    echo "/* $configure_input  */"
+    cat "$ac_result"
+  fi
+  rm -f "$tmp/out12"
+ ;;
+
+
+  esac
+
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
diff --git a/unfs3/configure.ac b/unfs3/configure.ac
new file mode 100644 (file)
index 0000000..023b296
--- /dev/null
@@ -0,0 +1,37 @@
+AC_INIT(unfs3, 0.9.22) 
+AC_CONFIG_HEADER(config.h)
+AC_PROG_INSTALL
+AC_PROG_CC([gcc egcs kgcc cc])
+AC_PROG_LEX
+AC_PROG_YACC
+AC_HEADER_STDC
+AC_SYS_LARGEFILE
+AC_SEARCH_LIBS(xdr_int, nsl)
+AC_SEARCH_LIBS(socket, socket)
+AC_SEARCH_LIBS(inet_aton, resolv)
+AC_CHECK_HEADERS(mntent.h,,,[#include <stdio.h>])
+AC_CHECK_HEADERS(stdint.h,,,[#include <stdio.h>])
+AC_CHECK_HEADERS(sys/mnttab.h,,,[#include <stdio.h>])
+AC_CHECK_HEADERS(sys/mount.h,,,[#include <stdio.h>])
+AC_CHECK_HEADERS(sys/vmount.h,,,[#include <stdio.h>])
+AC_CHECK_HEADERS(rpc/svc_soc.h,,,[#include <rpc/rpc.h>])
+AC_CHECK_HEADERS(linux/ext2_fs.h,,,[#include <unistd.h>])
+AC_CHECK_TYPES(int32,,,[#include <sys/inttypes.h>])
+AC_CHECK_TYPES(uint32,,,[#include <sys/inttypes.h>])
+AC_CHECK_TYPES(int64,,,[#include <sys/inttypes.h>])
+AC_CHECK_TYPES(uint64,,,[#include <sys/inttypes.h>])
+AC_CHECK_MEMBERS([struct stat.st_gen],,,[#include <sys/stat.h>])
+AC_CHECK_MEMBERS([struct __rpc_svcxprt.xp_fd],,,[#include <rpc/rpc.h>])
+AC_CHECK_FUNCS(xdr_long xdr_int32 xdr_u_long xdr_uint32)
+AC_CHECK_FUNCS(xdr_uint64 xdr_uint64_t xdr_u_int64_t)
+AC_CHECK_FUNCS(statvfs)
+AC_CHECK_FUNCS(seteuid setegid)
+AC_CHECK_FUNCS(setresuid setresgid)
+AC_CHECK_FUNCS(vsyslog)
+AC_CHECK_FUNCS(lchown)
+AC_CHECK_FUNCS(setgroups)
+UNFS3_SOLARIS_RPC
+UNFS3_PORTMAP_DEFINE
+UNFS3_COMPILE_WARNINGS
+AC_ARG_ENABLE(cluster,[  --enable-cluster        include clustering extensions],[AC_DEFINE([WANT_CLUSTER], [], [Cluster extensions]) AC_SUBST([SUBDIRS],[Extras]) AC_SUBST([EXTRAOBJ],[Extras/lib.a])])
+AC_OUTPUT(Makefile Config/Makefile Extras/Makefile)
diff --git a/unfs3/contrib/nfsotpclient/README b/unfs3/contrib/nfsotpclient/README
new file mode 100644 (file)
index 0000000..5ad1f0e
--- /dev/null
@@ -0,0 +1,3 @@
+
+This is a NFS one time password client. Read doc/passwords.txt for
+more information.
diff --git a/unfs3/contrib/nfsotpclient/mountclient/__init__.py b/unfs3/contrib/nfsotpclient/mountclient/__init__.py
new file mode 100644 (file)
index 0000000..88c0f92
--- /dev/null
@@ -0,0 +1,6 @@
+
+import mountconstants
+import mounttypes
+import mountpacker
+
+__all__ = ['mountconstants', 'mounttypes', 'mountpacker']
diff --git a/unfs3/contrib/nfsotpclient/mountclient/mountconstants.py b/unfs3/contrib/nfsotpclient/mountclient/mountconstants.py
new file mode 100644 (file)
index 0000000..e5b0628
--- /dev/null
@@ -0,0 +1,48 @@
+# Generated by rpcgen.py at Mon Mar  8 11:09:57 2004
+
+__all__ = ['MNTPATHLEN', 'MNTNAMLEN', 'FHSIZE2', 'FHSIZE3', 'MNT3_OK', 'MNT3ERR_PERM', 'MNT3ERR_NOENT', 'MNT3ERR_IO', 'MNT3ERR_ACCES', 'MNT3ERR_NOTDIR', 'MNT3ERR_INVAL', 'MNT3ERR_NAMETOOLONG', 'MNT3ERR_NOTSUPP', 'MNT3ERR_SERVERFAULT', 'mountstat3_id', 'MOUNTPROC_NULL', 'MOUNTPROC_MNT', 'MOUNTPROC_DUMP', 'MOUNTPROC_UMNT', 'MOUNTPROC_UMNTALL', 'MOUNTPROC_EXPORT', 'MOUNTPROC_EXPORTALL', 'MOUNT_V1', 'MOUNTPROC3_NULL', 'MOUNTPROC3_MNT', 'MOUNTPROC3_DUMP', 'MOUNTPROC3_UMNT', 'MOUNTPROC3_UMNTALL', 'MOUNTPROC3_EXPORT', 'MOUNT_V3', 'MOUNT_PROGRAM']
+
+FALSE = 0
+TRUE = 1
+MNTPATHLEN = 1024
+MNTNAMLEN = 255
+FHSIZE2 = 32
+FHSIZE3 = 64
+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
+mountstat3_id = {
+    MNT3_OK: "MNT3_OK",
+    MNT3ERR_PERM: "MNT3ERR_PERM",
+    MNT3ERR_NOENT: "MNT3ERR_NOENT",
+    MNT3ERR_IO: "MNT3ERR_IO",
+    MNT3ERR_ACCES: "MNT3ERR_ACCES",
+    MNT3ERR_NOTDIR: "MNT3ERR_NOTDIR",
+    MNT3ERR_INVAL: "MNT3ERR_INVAL",
+    MNT3ERR_NAMETOOLONG: "MNT3ERR_NAMETOOLONG",
+    MNT3ERR_NOTSUPP: "MNT3ERR_NOTSUPP",
+    MNT3ERR_SERVERFAULT: "MNT3ERR_SERVERFAULT"
+    }
+MOUNTPROC_NULL = 0
+MOUNTPROC_MNT = 1
+MOUNTPROC_DUMP = 2
+MOUNTPROC_UMNT = 3
+MOUNTPROC_UMNTALL = 4
+MOUNTPROC_EXPORT = 5
+MOUNTPROC_EXPORTALL = 6
+MOUNT_V1 = 1
+MOUNTPROC3_NULL = 0
+MOUNTPROC3_MNT = 1
+MOUNTPROC3_DUMP = 2
+MOUNTPROC3_UMNT = 3
+MOUNTPROC3_UMNTALL = 4
+MOUNTPROC3_EXPORT = 5
+MOUNT_V3 = 3
+MOUNT_PROGRAM = 100005
diff --git a/unfs3/contrib/nfsotpclient/mountclient/mountpacker.py b/unfs3/contrib/nfsotpclient/mountclient/mountpacker.py
new file mode 100644 (file)
index 0000000..b6e1e1f
--- /dev/null
@@ -0,0 +1,113 @@
+# Generated by rpcgen.py at Mon Mar  8 11:09:57 2004
+
+import rpc
+import mounttypes
+import mountconstants
+import xdrlib
+
+__all__ = ['MOUNTPacker', 'MOUNTUnpacker']
+
+class MOUNTPacker(rpc.Packer):
+    def __init__(self, ncl):
+        xdrlib.Packer.__init__(self)
+        self.ncl = ncl
+
+    pack_hyper = rpc.Packer.pack_hyper
+
+    pack_string = rpc.Packer.pack_string
+
+    pack_enum = rpc.Packer.pack_enum
+
+    pack_opaque = rpc.Packer.pack_opaque
+
+    pack_int = rpc.Packer.pack_int
+
+    pack_double = rpc.Packer.pack_double
+
+    pack_float = rpc.Packer.pack_float
+
+    pack_unsigned = rpc.Packer.pack_uint
+
+    pack_quadruple = rpc.Packer.pack_double
+
+    pack_bool = rpc.Packer.pack_bool
+
+    pack_unsigned_int = rpc.Packer.pack_uint
+
+    pack_unsigned_hyper = rpc.Packer.pack_uhyper
+
+    def pack_fhandle2(self, data):
+        self.pack_fopaque(mountconstants.FHSIZE2, data)
+        
+    def pack_fhandle3(self, data):
+        self.pack_opaque(data)
+        
+    def pack_dirpath(self, data):
+        self.pack_string(data)
+        
+    def pack_name(self, data):
+        self.pack_string(data)
+        
+    pack_mountstat3 = pack_enum
+
+    def pack_mountlist(self, data):
+        mounttypes.pack_objarray(self, data)
+        
+    def pack_groups(self, data):
+        mounttypes.pack_objarray(self, data)
+        
+    def pack_exports(self, data):
+        mounttypes.pack_objarray(self, data)
+        
+class MOUNTUnpacker(rpc.Unpacker):
+    def __init__(self, ncl, data=''):
+        xdrlib.Unpacker.__init__(self, data)
+        self.ncl = ncl
+
+    unpack_hyper = rpc.Unpacker.unpack_hyper
+
+    unpack_string = rpc.Unpacker.unpack_string
+
+    unpack_enum = rpc.Unpacker.unpack_enum
+
+    unpack_opaque = rpc.Unpacker.unpack_opaque
+
+    unpack_int = rpc.Unpacker.unpack_int
+
+    unpack_double = rpc.Unpacker.unpack_double
+
+    unpack_float = rpc.Unpacker.unpack_float
+
+    unpack_unsigned = rpc.Unpacker.unpack_uint
+
+    unpack_quadruple = rpc.Unpacker.unpack_double
+
+    unpack_bool = rpc.Unpacker.unpack_bool
+
+    unpack_unsigned_int = rpc.Unpacker.unpack_uint
+
+    unpack_unsigned_hyper = rpc.Unpacker.unpack_uhyper
+
+    def unpack_fhandle2(self):
+        return self.unpack_fopaque(mountconstants.FHSIZE2)
+        
+    def unpack_fhandle3(self):
+        return self.unpack_opaque()
+        
+    def unpack_dirpath(self):
+        return self.unpack_string()
+        
+    def unpack_name(self):
+        return self.unpack_string()
+        
+    unpack_mountstat3 = unpack_enum
+
+    def unpack_mountlist(self):
+        return mounttypes.unpack_objarray(self.ncl, mounttypes.mountbody)
+        
+    def unpack_groups(self):
+        return mounttypes.unpack_objarray(self.ncl, mounttypes.groupnode)
+        
+    def unpack_exports(self):
+        return mounttypes.unpack_objarray(self.ncl, mounttypes.exportnode)
+        
diff --git a/unfs3/contrib/nfsotpclient/mountclient/mounttypes.py b/unfs3/contrib/nfsotpclient/mountclient/mounttypes.py
new file mode 100644 (file)
index 0000000..303462c
--- /dev/null
@@ -0,0 +1,232 @@
+# Generated by rpcgen.py at Mon Mar  8 11:09:57 2004
+
+from mountconstants import *
+from mountpacker import *
+import rpc
+
+__all__ = ['BadDiscriminant', 'fhstatus', 'mountres3_ok', 'mountres3', 'mountbody', 'groupnode', 'exportnode']
+
+def init_type_class(klass, ncl):
+    # Initilize type class
+    klass.ncl = ncl
+    klass.packer = ncl.packer
+    klass.unpacker = ncl.unpacker
+
+def assert_not_none(klass, *args):
+    for arg in args:
+       if arg == None:
+           raise TypeError(repr(klass) + " has uninitialized data")
+
+def pack_objarray(ncl, list):
+    # FIXME: Support for length assertion. 
+    ncl.packer.pack_uint(len(list))
+    for item in list:
+       item.pack()
+
+def unpack_objarray(ncl, klass):
+    n = ncl.unpacker.unpack_uint()
+    list = []
+    for i in range(n):
+       obj = klass(ncl)
+       obj.unpack()
+       list.append(obj)
+    return list
+
+
+class BadDiscriminant(rpc.RPCException):
+    def __init__(self, value, klass):
+        self.value = value
+        self.klass = klass
+
+    def __str__(self):
+        return "Bad Discriminant %s in %s" % (self.value, self.klass)
+
+class fhstatus:
+    # XDR definition:
+    # union fhstatus switch (unsigned fhs_status) {
+    #     case 0:
+    #         fhandle2    fhs_fhandle;
+    #     default:
+    #         void;
+    # };
+    def __init__(self, ncl, fhs_status=None, fhs_fhandle=None):
+        init_type_class(self, ncl)
+        self.fhs_status = fhs_status
+        self.fhs_fhandle = fhs_fhandle
+        # Shortcut to current arm
+        self.arm = None
+
+    def __repr__(self):
+        s = " fhs_status=%s fhs_fhandle=%s" % (str(self.fhs_status), str(self.fhs_fhandle))
+        if len(s) > 70: s = s[:70] + "..."
+        return "<fhstatus:%s>" % s
+
+    def pack(self, dummy=None):
+        assert_not_none(self, self.fhs_status)
+        self.packer.pack_unsigned(self.fhs_status)
+        if self.fhs_status == 0:
+            assert_not_none(self, self.fhs_fhandle)
+            self.packer.pack_fhandle2(self.fhs_fhandle)
+            self.arm = self.fhs_fhandle
+        else:
+            pass
+            
+
+    def unpack(self):
+        self.fhs_status = self.unpacker.unpack_unsigned()
+        if self.fhs_status == 0:
+            self.fhs_fhandle = self.unpacker.unpack_fhandle2()
+            self.arm = self.fhs_fhandle
+        else:
+            pass
+            
+
+class mountres3_ok:
+    # XDR definition:
+    # struct mountres3_ok {
+    #     fhandle3 fhandle;
+    #     int auth_flavors<>;
+    # };
+    def __init__(self, ncl, fhandle=None, auth_flavors=None):
+        init_type_class(self, ncl)
+        self.fhandle = fhandle
+        self.auth_flavors = auth_flavors
+
+    def __repr__(self):
+        s = " fhandle=%s auth_flavors=%s" % (str(self.fhandle), str(self.auth_flavors))
+        if len(s) > 70: s = s[:70] + "..."
+        return "<mountres3_ok:%s>" % s
+
+    def pack(self, dummy=None):
+        assert_not_none(self, self.fhandle, self.auth_flavors)
+        self.packer.pack_fhandle3(self.fhandle)
+        self.packer.pack_int(self.auth_flavors)
+
+    def unpack(self):
+        self.fhandle = self.unpacker.unpack_fhandle3()
+        self.auth_flavors = self.unpacker.unpack_array(self.unpacker.unpack_int)
+
+class mountres3:
+    # XDR definition:
+    # union mountres3 switch (mountstat3 fhs_status) {
+    #     case MNT3_OK:
+    #         mountres3_ok    mountinfo;
+    #     default:
+    #         void;
+    # };
+    def __init__(self, ncl, fhs_status=None, mountinfo=None):
+        init_type_class(self, ncl)
+        self.fhs_status = fhs_status
+        self.mountinfo = mountinfo
+        # Shortcut to current arm
+        self.arm = None
+
+    def __repr__(self):
+        s = " fhs_status=%s mountinfo=%s" % (str(self.fhs_status), str(self.mountinfo))
+        if len(s) > 70: s = s[:70] + "..."
+        return "<mountres3:%s>" % s
+
+    def pack(self, dummy=None):
+        assert_not_none(self, self.fhs_status)
+        self.packer.pack_mountstat3(self.fhs_status)
+        if self.fhs_status == MNT3_OK:
+            assert_not_none(self, self.mountinfo)
+            self.mountinfo.pack()
+            self.arm = self.mountinfo
+        else:
+            pass
+            
+
+    def unpack(self):
+        self.fhs_status = self.unpacker.unpack_mountstat3()
+        if self.fhs_status == MNT3_OK:
+            self.mountinfo = mountres3_ok(self)
+            self.mountinfo.unpack()
+            self.arm = self.mountinfo
+        else:
+            pass
+            
+
+class mountbody:
+    # XDR definition:
+    # struct mountbody {
+    #     name ml_hostname;
+    #     dirpath ml_directory;
+    #     mountlist ml_next;
+    # };
+    def __init__(self, ncl, ml_hostname=None, ml_directory=None, ml_next=None):
+        init_type_class(self, ncl)
+        self.ml_hostname = ml_hostname
+        self.ml_directory = ml_directory
+        self.ml_next = ml_next
+
+    def __repr__(self):
+        s = " ml_hostname=%s ml_directory=%s ml_next=%s" % (str(self.ml_hostname), str(self.ml_directory), str(self.ml_next))
+        if len(s) > 70: s = s[:70] + "..."
+        return "<mountbody:%s>" % s
+
+    def pack(self, dummy=None):
+        assert_not_none(self, self.ml_hostname, self.ml_directory, self.ml_next)
+        self.packer.pack_name(self.ml_hostname)
+        self.packer.pack_dirpath(self.ml_directory)
+        self.packer.pack_mountlist(self.ml_next)
+
+    def unpack(self):
+        self.ml_hostname = self.unpacker.unpack_name()
+        self.ml_directory = self.unpacker.unpack_dirpath()
+        self.ml_next = self.unpacker.unpack_mountlist()
+
+class groupnode:
+    # XDR definition:
+    # struct groupnode {
+    #     name gr_name;
+    #     groups gr_next;
+    # };
+    def __init__(self, ncl, gr_name=None, gr_next=None):
+        init_type_class(self, ncl)
+        self.gr_name = gr_name
+        self.gr_next = gr_next
+
+    def __repr__(self):
+        s = " gr_name=%s gr_next=%s" % (str(self.gr_name), str(self.gr_next))
+        if len(s) > 70: s = s[:70] + "..."
+        return "<groupnode:%s>" % s
+
+    def pack(self, dummy=None):
+        assert_not_none(self, self.gr_name, self.gr_next)
+        self.packer.pack_name(self.gr_name)
+        self.packer.pack_groups(self.gr_next)
+
+    def unpack(self):
+        self.gr_name = self.unpacker.unpack_name()
+        self.gr_next = self.unpacker.unpack_groups()
+
+class exportnode:
+    # XDR definition:
+    # struct exportnode {
+    #     dirpath ex_dir;
+    #     groups ex_groups;
+    #     exports ex_next;
+    # };
+    def __init__(self, ncl, ex_dir=None, ex_groups=None, ex_next=None):
+        init_type_class(self, ncl)
+        self.ex_dir = ex_dir
+        self.ex_groups = ex_groups
+        self.ex_next = ex_next
+
+    def __repr__(self):
+        s = " ex_dir=%s ex_groups=%s ex_next=%s" % (str(self.ex_dir), str(self.ex_groups), str(self.ex_next))
+        if len(s) > 70: s = s[:70] + "..."
+        return "<exportnode:%s>" % s
+
+    def pack(self, dummy=None):
+        assert_not_none(self, self.ex_dir, self.ex_groups, self.ex_next)
+        self.packer.pack_dirpath(self.ex_dir)
+        self.packer.pack_groups(self.ex_groups)
+        self.packer.pack_exports(self.ex_next)
+
+    def unpack(self):
+        self.ex_dir = self.unpacker.unpack_dirpath()
+        self.ex_groups = self.unpacker.unpack_groups()
+        self.ex_next = self.unpacker.unpack_exports()
+
diff --git a/unfs3/contrib/nfsotpclient/nfsotpclient.py b/unfs3/contrib/nfsotpclient/nfsotpclient.py
new file mode 100755 (executable)
index 0000000..2470906
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# -*-mode: python; coding: iso-8859-1 -*-
+#
+# Copyright (c) 2002-2005 Cendio AB. All rights reserved.
+
+import sys
+import rpc
+import mountclient
+import socket
+import os
+import md5
+
+
+class PartialMOUNTClient:
+    def __init__(self):
+        pass
+
+    def addpackers(self):
+        self.packer = mountclient.mountpacker.MOUNTPacker(self)
+        self.unpacker = mountclient.mountpacker.MOUNTUnpacker(self, '')
+
+    def mnt(self, dirpath):
+        res = mountclient.mounttypes.mountres3(self)
+        self.make_call(mountclient.mountconstants.MOUNTPROC_MNT,
+                       dirpath, self.packer.pack_string, res.unpack)
+        return res
+    
+
+class TCPMOUNTClient(PartialMOUNTClient, rpc.RawTCPClient):
+    def __init__(self, host, port):
+        rpc.RawTCPClient.__init__(self, host,
+                                  mountclient.mountconstants.MOUNT_PROGRAM,
+                                  mountclient.mountconstants.MOUNT_V3,
+                                  port)
+        PartialMOUNTClient.__init__(self)
+
+
+class NFSOTPClient:
+    def __init__(self, host, port):
+        self.mountcl = TCPMOUNTClient(host, port)
+
+    def getotp(self, password):
+        res = self.mountcl.mnt("@getnonce")
+
+        if res.fhs_status != mountclient.mountconstants.MNT3_OK:
+            print >>sys.stderr, "Failed to get nonce:", mountclient.mountconstants.mountstat3_id[res.fhs_status]
+            sys.exit(1)
+        
+        fhandle = res.mountinfo.fhandle
+        digest = md5.new(fhandle + password).hexdigest()
+        return digest
+
+
+def usage():
+    print >>sys.stderr, "Usage: nfsotpclient.py host[:port]"
+    sys.exit(1)
+
+
+if __name__ == "__main__":
+    if len(sys.argv) != 2:
+        usage()
+
+    fields = sys.argv[1].split(":")
+    host = fields[0]
+    del fields[0]
+    if fields:
+        port = int(fields[0])
+    else:
+        # No port specified, fetch from portmapper
+        # FIXME
+        print >>sys.stderr, "Portmapper support not yet implemented"
+        sys.exit(1)
+
+    cl = NFSOTPClient(host, port)
+    import getpass
+    password = getpass.getpass()
+    
+    print cl.getotp(password)
diff --git a/unfs3/contrib/nfsotpclient/rpc.py b/unfs3/contrib/nfsotpclient/rpc.py
new file mode 100644 (file)
index 0000000..8be1926
--- /dev/null
@@ -0,0 +1,1050 @@
+
+# rpc.py - RFC1057/RFC1831
+#
+# Copyright (C) 2001 Cendio Systems AB (http://www.cendio.se)
+# All rights reserved.
+# 
+# Copyright (c) 2001 Python Software Foundation.
+# All rights reserved.
+# 
+# Copyright (c) 2000 BeOpen.com.
+# All rights reserved.
+# 
+# Copyright (c) 1995-2001 Corporation for National Research Initiatives.
+# All rights reserved.
+# 
+# Copyright (c) 1991-1995 Stichting Mathematisch Centrum.
+# All rights reserved.
+# 
+#
+# 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; version 2 of the License. 
+# 
+# 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, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# XXX The UDP version of the protocol resends requests when it does
+# XXX not receive a timely reply -- use only for idempotent calls!
+
+# XXX There is no provision for call timeout on TCP connections
+
+__pychecker__ = 'no-callinit'
+
+import xdrlib
+import socket
+import os
+import time
+
+RPCVERSION = 2
+
+CALL = 0
+REPLY = 1
+
+AUTH_NULL = 0
+AUTH_UNIX = 1
+AUTH_SHORT = 2
+AUTH_DES = 3
+
+MSG_ACCEPTED = 0
+MSG_DENIED = 1
+
+SUCCESS = 0                            # RPC executed successfully
+PROG_UNAVAIL  = 1                      # remote hasn't exported program
+PROG_MISMATCH = 2                      # remote can't support version #
+PROC_UNAVAIL  = 3                      # program can't support procedure
+GARBAGE_ARGS  = 4                      # procedure can't decode params
+
+RPC_MISMATCH = 0                       # RPC version number != 2
+AUTH_ERROR = 1                         # remote can't authenticate caller
+
+AUTH_BADCRED      = 1                  # bad credentials (seal broken)
+AUTH_REJECTEDCRED = 2                  # client must begin new session
+AUTH_BADVERF      = 3                  # bad verifier (seal broken)
+AUTH_REJECTEDVERF = 4                  # verifier expired or replayed
+AUTH_TOOWEAK      = 5                  # rejected for security reasons
+
+# All RPC errors are subclasses of RPCException
+class RPCException(Exception):
+    def __str__(self):
+       return "RPCException"
+
+class BadRPCMsgType(RPCException):
+    def __init__(self, msg_type):
+       self.msg_type = msg_type
+
+    def __str__(self):
+       return "BadRPCMsgType %d" % self.msg_type
+
+class BadRPCVersion(RPCException):
+    def __init__(self, version):
+       self.version = version
+
+    def __str__(self):
+       return "BadRPCVersion %d" % self.version
+
+class RPCMsgDenied(RPCException):
+    # MSG_DENIED
+    def __init__(self, stat):
+       self.stat = stat
+
+    def __str__(self):
+       return "RPCMsgDenied %d" % self.stat
+
+class RPCMisMatch(RPCException):
+    # MSG_DENIED: RPC_MISMATCH
+    def __init__(self, low, high):
+       self.low = low
+       self.high = high
+
+    def __str__(self):
+       return "RPCMisMatch %d,%d" % (self.low, self.high)
+
+class RPCAuthError(RPCException):
+    # MSG_DENIED: AUTH_ERROR
+    def __init__(self, stat):
+       self.stat = stat
+
+    def __str__(self):
+       return "RPCAuthError %d" % self.stat
+
+class BadRPCReplyType(RPCException):
+    # Neither MSG_DENIED nor MSG_ACCEPTED
+    def __init__(self, msg_type):
+       self.msg_type = msg_type
+
+    def __str__(self):
+       return "BadRPCReplyType %d" % self.msg_type
+
+class RPCProgUnavail(RPCException):
+    # PROG_UNAVAIL
+    def __str__(self):
+       return "RPCProgUnavail"
+
+class RPCProgMismatch(RPCException):
+    # PROG_MISMATCH
+    def __init__(self, low, high):
+       self.low = low
+       self.high = high
+
+    def __str__(self):
+       return "RPCProgMismatch %d,%d" % (self.low, self.high)
+
+class RPCProcUnavail(RPCException):
+    # PROC_UNAVAIL
+    def __str__(self):
+       return "RPCProcUnavail"
+
+class RPCGarbageArgs(RPCException):
+    # GARBAGE_ARGS
+    def __str__(self):
+       return "RPCGarbageArgs"
+
+class RPCUnextractedData(RPCException):
+    # xdrlib raised exception because unextracted data remained
+    def __str__(self):
+        return "RPCUnextractedData"
+
+class RPCBadAcceptStats(RPCException):
+    # Unknown accept_stat
+    def __init__(self, stat):
+       self.stat = stat
+
+    def __str__(self):
+       return "RPCBadAcceptStats %d" % self.stat
+
+class XidMismatch(RPCException):
+    # Got wrong XID in reply, got "xid" instead of "expected"
+    def __init__(self, xid, expected):
+       self.xid = xid
+       self.expected = expected
+
+    def __str__(self):
+       return "XidMismatch %d,%d" % (self.xid, self.expected)
+
+class TimeoutError(RPCException):
+    pass
+
+class PortMapError(RPCException):
+    pass
+
+
+class Packer(xdrlib.Packer):
+
+    def pack_auth(self, auth):
+       flavor, stuff = auth
+       self.pack_enum(flavor)
+       self.pack_opaque(stuff)
+
+    def pack_auth_unix(self, stamp, machinename, uid, gid, gids):
+       self.pack_uint(stamp)
+       self.pack_string(machinename)
+       self.pack_uint(uid)
+       self.pack_uint(gid)
+       self.pack_uint(len(gids))
+       for i in gids:
+           self.pack_uint(i)
+
+    def pack_callheader(self, xid, prog, vers, proc, cred, verf):
+       self.pack_uint(xid)
+       self.pack_enum(CALL)
+       self.pack_uint(RPCVERSION)
+       self.pack_uint(prog)
+       self.pack_uint(vers)
+       self.pack_uint(proc)
+       self.pack_auth(cred)
+       self.pack_auth(verf)
+       # Caller must add procedure-specific part of call
+
+    def pack_replyheader(self, xid, verf):
+       self.pack_uint(xid)
+       self.pack_enum(REPLY)
+       self.pack_uint(MSG_ACCEPTED)
+       self.pack_auth(verf)
+       self.pack_enum(SUCCESS)
+       # Caller must add procedure-specific part of reply
+
+
+class Unpacker(xdrlib.Unpacker):
+
+    def unpack_auth(self):
+       flavor = self.unpack_enum()
+       stuff = self.unpack_opaque()
+       if flavor == AUTH_UNIX:
+               p = Unpacker(stuff)
+               stuff = p.unpack_auth_unix()
+       return (flavor, stuff)
+
+    def unpack_auth_unix(self):
+        stamp=self.unpack_uint()
+        machinename=self.unpack_string()
+        print "machinename: %s" % machinename
+        uid=self.unpack_uint()
+        gid=self.unpack_uint()
+        n_gids=self.unpack_uint()
+        gids = []
+        print "n_gids: %d" % n_gids
+        for i in range(n_gids):
+            gids.append(self.unpack_uint())
+        return stamp, machinename, uid, gid, gids
+
+
+    def unpack_callheader(self):
+       xid = self.unpack_uint()
+       msg_type = self.unpack_enum()
+       if msg_type <> CALL:
+           raise BadRPCMsgType(msg_type)
+       rpcvers = self.unpack_uint()
+       if rpcvers <> RPCVERSION:
+           raise BadRPCVersion(rpcvers)
+       prog = self.unpack_uint()
+       vers = self.unpack_uint()
+       proc = self.unpack_uint()
+       cred = self.unpack_auth()
+       verf = self.unpack_auth()
+       return xid, prog, vers, proc, cred, verf
+       # Caller must add procedure-specific part of call
+
+    def unpack_replyheader(self):
+       xid = self.unpack_uint()
+       msg_type = self.unpack_enum()
+       if msg_type <> REPLY:
+           raise BadRPCMsgType(msg_type)
+       stat = self.unpack_enum()
+       if stat == MSG_DENIED:
+           stat = self.unpack_enum()
+           if stat == RPC_MISMATCH:
+               low = self.unpack_uint()
+               high = self.unpack_uint()
+               raise RPCMisMatch(low, high)
+           if stat == AUTH_ERROR:
+               stat = self.unpack_uint()
+               raise RPCAuthError(stat)
+           raise RPCMsgDenied(stat)
+       if stat <> MSG_ACCEPTED:
+           raise BadRPCReplyType(stat)
+       verf = self.unpack_auth()
+       stat = self.unpack_enum()
+       if stat == PROG_UNAVAIL:
+           raise RPCProgUnavail()
+       if stat == PROG_MISMATCH:
+           low = self.unpack_uint()
+           high = self.unpack_uint()
+           raise RPCProgMismatch(low, high)
+       if stat == PROC_UNAVAIL:
+           raise RPCProcUnavail()
+       if stat == GARBAGE_ARGS:
+           raise RPCGarbageArgs()
+       if stat <> SUCCESS:
+           raise RPCBadAcceptStats(stat)
+       return xid, verf
+       # Caller must get procedure-specific part of reply
+
+
+# Subroutines to create opaque authentication objects
+
+def make_auth_null():
+    return ''
+
+def make_auth_unix(seed, host, uid, gid, groups):
+    p = Packer()
+    p.pack_auth_unix(seed, host, uid, gid, groups)
+    return p.get_buffer()
+
+def make_auth_unix_default():
+    try:
+       uid = os.getuid()
+       gid = os.getgid()
+    except AttributeError:
+       uid = gid = 0
+    return make_auth_unix(int(time.time()-unix_epoch()), \
+             socket.gethostname(), uid, gid, [])
+
+_unix_epoch = -1
+def unix_epoch():
+    """Very painful calculation of when the Unix Epoch is.
+
+    This is defined as the return value of time.time() on Jan 1st,
+    1970, 00:00:00 GMT.
+
+    On a Unix system, this should always return 0.0.  On a Mac, the
+    calculations are needed -- and hard because of integer overflow
+    and other limitations.
+
+    """
+    global _unix_epoch
+    if _unix_epoch >= 0: return _unix_epoch
+    now = time.time()
+    localt = time.localtime(now)       # (y, m, d, hh, mm, ss, ..., ..., ...)
+    gmt = time.gmtime(now)
+    offset = time.mktime(localt) - time.mktime(gmt)
+    y, m, d, hh, mm, ss = 1970, 1, 1, 0, 0, 0
+    offset, ss = divmod(ss + offset, 60)
+    offset, mm = divmod(mm + offset, 60)
+    offset, hh = divmod(hh + offset, 24)
+    d = d + offset
+    _unix_epoch = time.mktime((y, m, d, hh, mm, ss, 0, 0, 0))
+    print "Unix epoch:", time.ctime(_unix_epoch)
+    return _unix_epoch
+
+
+# Common base class for clients
+
+class Client:
+
+    def __init__(self, host, prog, vers, port):
+       self.host = host
+       self.prog = prog
+       self.vers = vers
+       self.port = port
+        self.sock = None
+       self.makesocket() # Assigns to self.sock
+       self.bindsocket()
+       self.connsocket()
+        # Servers may do XID caching, so try to come up with something
+        # unique to start with. XIDs are 32 bits. Python integers are always
+        # at least 32 bits. 
+       self.lastxid = int(long(time.time() * 1E6) & 0xfffffff)
+       self.addpackers()
+       self.cred = None
+       self.verf = None
+
+    def close(self):
+       self.sock.close()
+
+    def makesocket(self):
+       # This MUST be overridden
+       raise RuntimeError("makesocket not defined")
+
+    def connsocket(self):
+       # Override this if you don't want/need a connection
+       self.sock.connect((self.host, self.port))
+
+    def bindsocket(self):
+       # Override this to bind to a different port (e.g. reserved)
+       self.sock.bind(('', 0))
+
+    def addpackers(self):
+       # Override this to use derived classes from Packer/Unpacker
+       self.packer = Packer()
+       self.unpacker = Unpacker('')
+
+    def make_call(self, proc, args, pack_func, unpack_func):
+       # Don't normally override this (but see Broadcast)
+       if pack_func is None and args is not None:
+           raise TypeError("non-null args with null pack_func")
+       self.start_call(proc)
+       if pack_func:
+           pack_func(args)
+       self.do_call()
+       if unpack_func:
+           result = unpack_func()
+       else:
+           result = None
+        try:
+            self.unpacker.done()
+        except xdrlib.Error:
+            raise RPCUnextractedData()
+            
+       return result
+
+    def start_call(self, proc):
+       # Don't override this
+       self.lastxid = xid = self.lastxid + 1
+       cred = self.mkcred()
+       verf = self.mkverf()
+       p = self.packer
+       p.reset()
+       p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
+
+    def do_call(self):
+       # This MUST be overridden
+       raise RuntimeError("do_call not defined")
+
+    def mkcred(self):
+       # Override this to use more powerful credentials
+       if self.cred == None:
+           self.cred = (AUTH_NULL, make_auth_null())
+       return self.cred
+
+    def mkverf(self):
+       # Override this to use a more powerful verifier
+       if self.verf == None:
+           self.verf = (AUTH_NULL, make_auth_null())
+       return self.verf
+
+    def call_0(self):          # Procedure 0 is always like this
+       return self.make_call(0, None, None, None)
+
+
+# Record-Marking standard support
+
+def sendfrag(sock, last, frag):
+    x = len(frag)
+    if last: x = x | 0x80000000L
+    header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
+             chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
+    sock.send(header + frag)
+
+def sendrecord(sock, record):
+    sendfrag(sock, 1, record)
+
+def recvfrag(sock):
+    header = sock.recv(4)
+    if len(header) < 4:
+       raise EOFError
+    x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \
+       ord(header[2])<<8 | ord(header[3])
+    last = ((x & 0x80000000) != 0)
+    n = int(x & 0x7fffffff)
+    frag = ''
+    while n > 0:
+       buf = sock.recv(n)
+       if not buf: raise EOFError
+       n = n - len(buf)
+       frag = frag + buf
+    return last, frag
+
+def recvrecord(sock):
+    record = ''
+    last = 0
+    while not last:
+       last, frag = recvfrag(sock)
+       record = record + frag
+    return record
+
+
+# Try to bind to a reserved port (must be root)
+
+last_resv_port_tried = None
+def bindresvport(sock, host):
+    global last_resv_port_tried
+    FIRST, LAST = 600, 1024 # Range of ports to try
+    if last_resv_port_tried == None:
+       last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST)
+    for i in range(last_resv_port_tried, LAST) + \
+             range(FIRST, last_resv_port_tried):
+       last_resv_port_tried = i
+       try:
+           sock.bind((host, i))
+           return last_resv_port_tried
+       except socket.error, (errno, msg):
+           if errno <> 114:
+               raise socket.error(errno, msg)
+    raise RuntimeError("can't assign reserved port")
+
+
+# Client using TCP to a specific port
+
+class RawTCPClient(Client):
+
+    def makesocket(self):
+       self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+    def do_call(self):
+       call = self.packer.get_buffer()
+       sendrecord(self.sock, call)
+       reply = recvrecord(self.sock)
+       u = self.unpacker
+       u.reset(reply)
+       xid, verf = u.unpack_replyheader()
+       if xid <> self.lastxid:
+           # Can't really happen since this is TCP...
+           raise XidMismatch(xid, self.lastxid)
+
+# Client using UDP to a specific port
+
+class RawUDPClient(Client):
+
+    def makesocket(self):
+       self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+    def do_call(self):
+       call = self.packer.get_buffer()
+       self.sock.send(call)
+       try:
+           from select import select
+       except ImportError:
+           print 'WARNING: select not found, RPC may hang'
+           select = None
+       BUFSIZE = 8192 # Max UDP buffer size
+       timeout = 1
+       count = 5
+       while 1:
+           r, w, x = [self.sock], [], []
+           if select:
+               r, w, x = select(r, w, x, timeout)
+           if self.sock not in r:
+               count = count - 1
+               if count < 0: raise TimeoutError() 
+               if timeout < 25: timeout = timeout *2
+##                             print 'RESEND', timeout, count
+               self.sock.send(call)
+               continue
+           reply = self.sock.recv(BUFSIZE)
+           u = self.unpacker
+           u.reset(reply)
+           xid, verf = u.unpack_replyheader()
+           if xid <> self.lastxid:
+##                             print 'BAD xid'
+               continue
+           break
+
+
+# Client using UDP broadcast to a specific port
+
+class RawBroadcastUDPClient(RawUDPClient):
+
+    def __init__(self, bcastaddr, prog, vers, port):
+       RawUDPClient.__init__(self, bcastaddr, prog, vers, port)
+       self.reply_handler = None
+       self.timeout = 30
+
+    def connsocket(self):
+       # Don't connect -- use sendto
+       self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+
+    def set_reply_handler(self, reply_handler):
+       self.reply_handler = reply_handler
+
+    def set_timeout(self, timeout):
+       self.timeout = timeout # Use None for infinite timeout
+
+    def make_call(self, proc, args, pack_func, unpack_func):
+       if pack_func is None and args is not None:
+           raise TypeError("non-null args with null pack_func")
+       self.start_call(proc)
+       if pack_func:
+           pack_func(args)
+       call = self.packer.get_buffer()
+       self.sock.sendto(call, (self.host, self.port))
+       try:
+           from select import select
+       except ImportError:
+           print 'WARNING: select not found, broadcast will hang'
+           select = None
+       BUFSIZE = 8192 # Max UDP buffer size (for reply)
+       replies = []
+       if unpack_func is None:
+           def dummy(): pass
+           unpack_func = dummy
+       while 1:
+           r, w, x = [self.sock], [], []
+           if select:
+               if self.timeout is None:
+                   r, w, x = select(r, w, x)
+               else:
+                   r, w, x = select(r, w, x, self.timeout)
+           if self.sock not in r:
+               break
+           reply, fromaddr = self.sock.recvfrom(BUFSIZE)
+           u = self.unpacker
+           u.reset(reply)
+           xid, verf = u.unpack_replyheader()
+           if xid <> self.lastxid:
+##                             print 'BAD xid'
+               continue
+           reply = unpack_func()
+            try:
+                self.unpacker.done()
+            except xdrlib.Error:
+                raise RPCUnextractedData()
+           replies.append((reply, fromaddr))
+           if self.reply_handler:
+               self.reply_handler(reply, fromaddr)
+       return replies
+
+
+# Port mapper interface
+
+# Program number, version and (fixed!) port number
+PMAP_PROG = 100000
+PMAP_VERS = 2
+PMAP_PORT = 111
+
+# Procedure numbers
+PMAPPROC_NULL = 0                      # (void) -> void
+PMAPPROC_SET = 1                       # (mapping) -> bool
+PMAPPROC_UNSET = 2                     # (mapping) -> bool
+PMAPPROC_GETPORT = 3                   # (mapping) -> unsigned int
+PMAPPROC_DUMP = 4                      # (void) -> pmaplist
+PMAPPROC_CALLIT = 5                    # (call_args) -> call_result
+
+# A mapping is (prog, vers, prot, port) and prot is one of:
+
+IPPROTO_TCP = 6
+IPPROTO_UDP = 17
+
+# A pmaplist is a variable-length list of mappings, as follows:
+# either (1, mapping, pmaplist) or (0).
+
+# A call_args is (prog, vers, proc, args) where args is opaque;
+# a call_result is (port, res) where res is opaque.
+
+
+class PortMapperPacker(Packer):
+
+    def pack_mapping(self, mapping):
+       prog, vers, prot, port = mapping
+       self.pack_uint(prog)
+       self.pack_uint(vers)
+       self.pack_uint(prot)
+       self.pack_uint(port)
+
+    def pack_pmaplist(self, list):
+       self.pack_list(list, self.pack_mapping)
+
+    def pack_call_args(self, ca):
+       prog, vers, proc, args = ca
+       self.pack_uint(prog)
+       self.pack_uint(vers)
+       self.pack_uint(proc)
+       self.pack_opaque(args)
+
+
+class PortMapperUnpacker(Unpacker):
+
+    def unpack_mapping(self):
+       prog = self.unpack_uint()
+       vers = self.unpack_uint()
+       prot = self.unpack_uint()
+       port = self.unpack_uint()
+       return prog, vers, prot, port
+
+    def unpack_pmaplist(self):
+       return self.unpack_list(self.unpack_mapping)
+
+    def unpack_call_result(self):
+       port = self.unpack_uint()
+       res = self.unpack_opaque()
+       return port, res
+
+
+class PartialPortMapperClient:
+    __pychecker__ = 'no-classattr'
+    def addpackers(self):
+       self.packer = PortMapperPacker()
+       self.unpacker = PortMapperUnpacker('')
+
+    def Set(self, mapping):
+       return self.make_call(PMAPPROC_SET, mapping, \
+               self.packer.pack_mapping, \
+               self.unpacker.unpack_uint)
+
+    def Unset(self, mapping):
+       return self.make_call(PMAPPROC_UNSET, mapping, \
+               self.packer.pack_mapping, \
+               self.unpacker.unpack_uint)
+
+    def Getport(self, mapping):
+       return self.make_call(PMAPPROC_GETPORT, mapping, \
+               self.packer.pack_mapping, \
+               self.unpacker.unpack_uint)
+
+    def Dump(self):
+       return self.make_call(PMAPPROC_DUMP, None, \
+               None, \
+               self.unpacker.unpack_pmaplist)
+
+    def Callit(self, ca):
+       return self.make_call(PMAPPROC_CALLIT, ca, \
+               self.packer.pack_call_args, \
+               self.unpacker.unpack_call_result)
+
+
+class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
+
+    def __init__(self, host):
+       RawTCPClient.__init__(self, \
+               host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+
+
+class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
+
+    def __init__(self, host):
+       RawUDPClient.__init__(self, \
+               host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+
+
+class BroadcastUDPPortMapperClient(PartialPortMapperClient, \
+                                  RawBroadcastUDPClient):
+
+    def __init__(self, bcastaddr):
+       RawBroadcastUDPClient.__init__(self, \
+               bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+
+
+# Generic clients that find their server through the Port mapper
+
+class TCPClient(RawTCPClient):
+
+    def __init__(self, host, prog, vers):
+       pmap = TCPPortMapperClient(host)
+       port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
+       pmap.close()
+       if port == 0:
+           raise PortMapError("program not registered")
+       RawTCPClient.__init__(self, host, prog, vers, port)
+
+
+class UDPClient(RawUDPClient):
+
+    def __init__(self, host, prog, vers):
+       pmap = UDPPortMapperClient(host)
+       port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
+       pmap.close()
+       if port == 0:
+           raise PortMapError("program not registered")
+       RawUDPClient.__init__(self, host, prog, vers, port)
+
+
+class BroadcastUDPClient(Client):
+
+    def __init__(self, bcastaddr, prog, vers):
+       self.pmap = BroadcastUDPPortMapperClient(bcastaddr)
+       self.pmap.set_reply_handler(self.my_reply_handler)
+       self.prog = prog
+       self.vers = vers
+       self.user_reply_handler = None
+       self.addpackers()
+
+    def close(self):
+       self.pmap.close()
+
+    def set_reply_handler(self, reply_handler):
+       self.user_reply_handler = reply_handler
+
+    def set_timeout(self, timeout):
+       self.pmap.set_timeout(timeout)
+
+    def my_reply_handler(self, reply, fromaddr):
+       port, res = reply
+       self.unpacker.reset(res)
+       result = self.unpack_func()
+        try:
+            self.unpacker.done()
+        except xdrlib.Error:
+            raise RPCUnextractedData()
+       self.replies.append((result, fromaddr))
+       if self.user_reply_handler is not None:
+           self.user_reply_handler(result, fromaddr)
+
+    def make_call(self, proc, args, pack_func, unpack_func):
+       self.packer.reset()
+       if pack_func:
+           pack_func(args)
+       if unpack_func is None:
+           def dummy(): pass
+           self.unpack_func = dummy
+       else:
+           self.unpack_func = unpack_func
+       self.replies = []
+       packed_args = self.packer.get_buffer()
+       dummy_replies = self.pmap.Callit( \
+               (self.prog, self.vers, proc, packed_args))
+       return self.replies
+
+
+# Server classes
+
+# These are not symmetric to the Client classes
+# XXX No attempt is made to provide authorization hooks yet
+
+class Server:
+
+    def __init__(self, host, prog, vers, port):
+       self.host = host # Should normally be '' for default interface
+       self.prog = prog
+       self.vers = vers
+       self.port = port # Should normally be 0 for random port
+        self.sock = None
+        self.prot = None
+       self.makesocket() # Assigns to self.sock and self.prot
+       self.bindsocket()
+       self.host, self.port = self.sock.getsockname()
+       self.addpackers()
+
+    def register(self):
+       mapping = self.prog, self.vers, self.prot, self.port
+       p = TCPPortMapperClient(self.host)
+       if not p.Set(mapping):
+           raise PortMapError("register failed")
+
+    def unregister(self):
+       mapping = self.prog, self.vers, self.prot, self.port
+       p = TCPPortMapperClient(self.host)
+       if not p.Unset(mapping):
+           raise PortMapError("unregister failed")
+
+    def handle(self, call):
+       # Don't use unpack_header but parse the header piecewise
+       # XXX I have no idea if I am using the right error responses!
+       self.unpacker.reset(call)
+       self.packer.reset()
+       xid = self.unpacker.unpack_uint()
+       self.packer.pack_uint(xid)
+       temp = self.unpacker.unpack_enum()
+       if temp <> CALL:
+           return None # Not worthy of a reply
+       self.packer.pack_uint(REPLY)
+       temp = self.unpacker.unpack_uint()
+       if temp <> RPCVERSION:
+           self.packer.pack_uint(MSG_DENIED)
+           self.packer.pack_uint(RPC_MISMATCH)
+           self.packer.pack_uint(RPCVERSION)
+           self.packer.pack_uint(RPCVERSION)
+           return self.packer.get_buffer()
+       self.packer.pack_uint(MSG_ACCEPTED)
+       self.packer.pack_auth((AUTH_NULL, make_auth_null()))
+       prog = self.unpacker.unpack_uint()
+       if prog <> self.prog:
+           self.packer.pack_uint(PROG_UNAVAIL)
+           return self.packer.get_buffer()
+       vers = self.unpacker.unpack_uint()
+       if vers <> self.vers:
+           self.packer.pack_uint(PROG_MISMATCH)
+           self.packer.pack_uint(self.vers)
+           self.packer.pack_uint(self.vers)
+           return self.packer.get_buffer()
+       proc = self.unpacker.unpack_uint()
+       methname = 'handle_' + `proc`
+       try:
+           meth = getattr(self, methname)
+       except AttributeError:
+           self.packer.pack_uint(PROC_UNAVAIL)
+           return self.packer.get_buffer()
+       self.recv_cred = self.unpacker.unpack_auth()
+       self.recv_verf = self.unpacker.unpack_auth()
+       try:
+           meth() # Unpack args, call turn_around(), pack reply
+       except (EOFError, RPCGarbageArgs):
+           # Too few or too many arguments
+           self.packer.reset()
+           self.packer.pack_uint(xid)
+           self.packer.pack_uint(REPLY)
+           self.packer.pack_uint(MSG_ACCEPTED)
+           self.packer.pack_auth((AUTH_NULL, make_auth_null()))
+           self.packer.pack_uint(GARBAGE_ARGS)
+       return self.packer.get_buffer()
+
+    def turn_around(self):
+        try:
+            self.unpacker.done()
+        except xdrlib.Error:
+            raise RPCUnextractedData()
+        
+       self.packer.pack_uint(SUCCESS)
+
+    def handle_0(self): # Handle NULL message
+       self.turn_around()
+
+    def makesocket(self):
+       # This MUST be overridden
+       raise RuntimeError("makesocket not defined")
+
+    def bindsocket(self):
+       # Override this to bind to a different port (e.g. reserved)
+       self.sock.bind((self.host, self.port))
+
+    def addpackers(self):
+       # Override this to use derived classes from Packer/Unpacker
+       self.packer = Packer()
+       self.unpacker = Unpacker('')
+
+
+class TCPServer(Server):
+
+    def makesocket(self):
+       self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+       self.prot = IPPROTO_TCP
+
+    def loop(self):
+       self.sock.listen(0)
+       while 1:
+           self.session(self.sock.accept())
+
+    def session(self, connection):
+       sock, (host, port) = connection
+       while 1:
+           try:
+               call = recvrecord(sock)
+           except EOFError:
+               break
+           except socket.error, msg:
+               print 'socket error:', msg
+               break
+           reply = self.handle(call)
+           if reply is not None:
+               sendrecord(sock, reply)
+
+    def forkingloop(self):
+       # Like loop but uses forksession()
+       self.sock.listen(0)
+       while 1:
+           self.forksession(self.sock.accept())
+
+    def forksession(self, connection):
+       # Like session but forks off a subprocess
+       # Wait for deceased children
+       try:
+           while 1:
+               pid, sts = os.waitpid(0, 1)
+       except os.error:
+           pass
+       pid = None
+       try:
+           pid = os.fork()
+           if pid: # Parent
+               connection[0].close()
+               return
+           # Child
+           self.session(connection)
+       finally:
+           # Make sure we don't fall through in the parent
+           if pid == 0:
+               os._exit(0)
+
+
+class UDPServer(Server):
+
+    def makesocket(self):
+       self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+       self.prot = IPPROTO_UDP
+
+    def loop(self):
+       while 1:
+           self.session()
+
+    def session(self):
+       call, host_port = self.sock.recvfrom(8192)
+       self.sender_port = host_port
+       reply = self.handle(call)
+       if reply <> None:
+           self.sock.sendto(reply, host_port)
+
+
+# Simple test program -- dump local portmapper status
+
+def test():
+    pmap = UDPPortMapperClient('')
+    list = pmap.Dump()
+    list.sort()
+    for prog, vers, prot, port in list:
+       print prog, vers,
+       if prot == IPPROTO_TCP: print 'tcp',
+       elif prot == IPPROTO_UDP: print 'udp',
+       else: print prot,
+       print port
+
+
+# Test program for broadcast operation -- dump everybody's portmapper status
+
+def testbcast():
+    import sys
+    if sys.argv[1:]:
+       bcastaddr = sys.argv[1]
+    else:
+       bcastaddr = '<broadcast>'
+    def rh(reply, fromaddr):
+       host, port = fromaddr
+       print host + '\t' + `reply`
+    pmap = BroadcastUDPPortMapperClient(bcastaddr)
+    pmap.set_reply_handler(rh)
+    pmap.set_timeout(5)
+    unused_replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0))
+
+
+# Test program for server, with corresponding client
+# On machine A: python -c 'import rpc; rpc.testsvr()'
+# On machine B: python -c 'import rpc; rpc.testclt()' A
+# (A may be == B)
+
+def testsvr():
+    # Simple test class -- proc 1 doubles its string argument as reply
+    class S(UDPServer):
+       def handle_1(self):
+           arg = self.unpacker.unpack_string()
+           self.turn_around()
+           print 'RPC function 1 called, arg', `arg`
+           self.packer.pack_string(arg + arg)
+    #
+    s = S('', 0x20000000, 1, 0)
+    try:
+       s.unregister()
+    except PortMapError, e:
+       print 'RuntimeError:', e.args, '(ignored)'
+    s.register()
+    print 'Service started...'
+    try:
+       s.loop()
+    finally:
+       s.unregister()
+       print 'Service interrupted.'
+
+
+def testclt():
+    import sys
+    if sys.argv[1:]: host = sys.argv[1]
+    else: host = ''
+    # Client for above server
+    class C(UDPClient):
+       def call_1(self, arg):
+           return self.make_call(1, arg, \
+                   self.packer.pack_string, \
+                   self.unpacker.unpack_string)
+    c = C(host, 0x20000000, 1)
+    print 'making call...'
+    reply = c.call_1('hello, world, ')
+    print 'call returned', `reply`
+
+
+# Local variables:
+# py-indent-offset: 4
+# tab-width: 8
+# End:
diff --git a/unfs3/contrib/rpcproxy/rpcproxy b/unfs3/contrib/rpcproxy/rpcproxy
new file mode 100755 (executable)
index 0000000..e800b1e
--- /dev/null
@@ -0,0 +1,378 @@
+#!/usr/bin/env python
+# -*-mode: python; coding: utf-8 -*-
+
+# TODO:
+# Support for limiting data sizes, max number of connections from the same IP etc
+
+import sys
+import time
+import socket
+import select
+import struct
+
+# Connection states, both for client and server connection.
+# Client cycle: STATE_READING, WAITING, WRITING
+# Server cycle: WAITING, WRITING, STATE_READING
+STATE_READING = 0 # Reading record
+STATE_WAITING = 2 # Waiting for server response callback, or client mission. 
+STATE_WRITING = 3 # Writing response to client or request to server
+STATE_EOF = 4 # EOF while reading
+
+# Constants
+FRAG_HEADER_LEN = 4
+FRAG_MAX_SIZE = 2**31 - 1
+FRAG_SIZE = FRAG_MAX_SIZE # Size of newly created fragments
+
+
+class ProxyEngine:
+    def __init__(self):
+        self.connections = [] # Client or server connections
+        self.proxies = [] # Proxy objects
+
+
+    def add_proxy(self, bind_address, port, host, hostport):
+        """Add a new proxy"""
+        proxy = Proxy(self, bind_address, port, host, hostport)
+        self.proxies.append(proxy)
+        self.connections.append(proxy.srv)
+
+    def add_connection(self, conn):
+        """Add a new connection"""
+        self.connections.append(conn)
+
+
+class Proxy:
+    def __init__(self, pe, bind_address, port, host, hostport):
+        self.pe = pe
+        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.sock.bind((bind_address, port)) 
+        self.sock.listen(1)
+        self.srv = ServerConnection(host, hostport)
+
+
+    def fileno(self):
+        """Return the sockets fileno"""
+        return self.sock.fileno()
+
+
+    def handle_read(self):
+        """Accept a new connection. Return a new ClientConnection"""
+        sock, addr = self.sock.accept()
+        self.pe.add_connection(ClientConnection(sock, addr, self.srv))
+
+
+class ServerCall:
+    def __init__(self, data, callback):
+        self.data = data
+        self.callback = callback
+
+
+class RPCConnection:
+    def __init__(self):
+        self.record = "" # Current record, as stream with RMs
+        self.sndbuf = None
+        self.sock = None
+
+
+    def set_sock(self, sock):
+        """Set socket to use"""
+        self.sock = sock
+        self.sndbuf = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
+
+
+    def eof_event(self):
+        """recv/send discovered that the connection was closed"""
+        self.state = STATE_EOF 
+        self.sock.close()
+
+
+    def assert_sock(self):
+        """Make sure a socket is available. May be overridden."""
+        assert self.sock is not None
+
+
+    def readable(self):
+        """Returns true if connection wants to read"""
+        return self.state is STATE_READING
+
+
+    def writable(self):
+        """Returns true if connection wants to write"""
+        return self.state is STATE_WRITING
+
+
+    def eof(self):
+        """Returns true if EOF has been detected"""
+        return self.state is STATE_EOF
+
+
+    def fileno(self):
+        """Return the sockets fileno"""
+        self.assert_sock()
+        return self.sock.fileno()
+
+
+    def write_record(self):
+        """Write RPC record. Returns true when everything is written"""
+        self.assert_sock()
+        # We can write up to SO_SNDBUF without risk blocking
+        wrote = self.sock.send(buffer(self.record, 0, self.sndbuf))
+        self.record = self.record[wrote:]
+        return len(self.record) == 0
+
+
+    def frag_length(self, head):
+        """Return the length of a fragment, including header"""
+        assert len(head) == FRAG_HEADER_LEN
+        x = struct.unpack('>L', head)[0]
+        return int(x & 0x7fffffff) + FRAG_HEADER_LEN
+
+
+    def frag_last(self, head):
+        """Return true if last flag is set"""
+        assert len(head) == FRAG_HEADER_LEN
+        x = struct.unpack('>L', head)[0]
+        return ((x & 0x80000000L) != 0)
+
+
+    def rm_stream(self, stream):
+        """Record-mark a data stream"""
+        fragpos = 0
+        data = []
+
+        while 1:
+            last = (fragpos+FRAG_SIZE >= len(stream))
+            frag_data = buffer(stream, fragpos, FRAG_SIZE)
+            x = len(frag_data)
+            if last:
+                x = x | 0x80000000L
+            frag_head = struct.pack('>L', x)
+            data.append(frag_head + str(frag_data))
+            if last:
+                break
+            fragpos += len(frag_data)
+
+        return "".join(data)
+
+
+    def parsed_record(self):
+        """Return tupel (data, missing_bytes) of record"""
+        fragpos = 0
+        data = []
+        while 1:
+            frag = buffer(self.record, fragpos)
+            fraghead = buffer(self.record, fragpos, FRAG_HEADER_LEN)
+            data.append(frag[4:])
+
+            if len(frag) < FRAG_HEADER_LEN:
+                return ("".join(data), FRAG_HEADER_LEN - len(frag))
+            
+            len_from_head = self.frag_length(fraghead)
+            if len(frag) < len_from_head:
+                # Incomplete fragment
+                return ("".join(data), len_from_head - len(frag))
+            elif len(frag) == len_from_head:
+                # Complete fragment
+                if self.frag_last(fraghead):
+                    # No need to read anything more
+                    return ("".join(data), 0)
+                else:
+                    # Read another fragment
+                    return ("".join(data), FRAG_HEADER_LEN)
+            elif len(frag) > len_from_head:
+                # There are more fragments, check them
+                fragpos += len(frag)
+            else:
+                assert 0
+
+
+    def read_record(self):
+        """Read RPC record. Returns true if record complete"""
+        self.assert_sock()
+        assert self.state == STATE_READING
+        bytes_to_read = self.parsed_record()[1]
+        if bytes_to_read == 0:
+            return 1
+        
+        data = self.sock.recv(bytes_to_read)
+
+        if data == "":
+            self.eof_event()
+            return 0
+
+        self.record += data
+        return self.parsed_record()[1] == 0
+
+
+class ServerConnection(RPCConnection):
+    def __init__(self, host, port):
+        RPCConnection.__init__(self)
+        self.host = host
+        self.port = port
+        self.calls = [] # A list of ServerCalls
+        self.state = STATE_WAITING
+        self.current_cb = None
+
+
+    def eof_event(self):
+        """Overridden eof_event, which re-connects"""
+        print >>sys.stderr, "Lost connection to server, trying to reconnect."
+        # Discard the current call
+        self.current_cb("")
+        self.current_cb = None
+        if self.calls:
+            self.state = STATE_WRITING
+        else:
+            self.state = STATE_WAITING
+
+        # Re-create socket
+        self.sock.close()
+        self.sock = None
+        self.assert_sock()
+
+
+    def assert_sock(self):
+        """Overridden assert_sock, which connects dynamically"""
+        if self.sock is None:
+            srv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            while 1:
+                try:
+                    srv_sock.connect((self.host, self.port))
+                    print >>sys.stderr, "Connected to %s:%d" % (self.host, self.port)
+                    break
+                except socket.error, e:
+                    print >>sys.stderr, "Connection to %s:%d failed: %s" % (self.host, self.port, e[1])
+                    time.sleep(5)
+            self.set_sock(srv_sock)
+
+
+    def call(self, servercall):
+        """Put another call on the call queue. The call argument is a
+        stream, without RMs. The callback will be called with result"""
+        self.calls.append(servercall)
+        if self.state == STATE_WAITING:
+            self.state = STATE_WRITING
+
+
+    def handle_read(self):
+        """Called when socket is ready for read"""
+        if self.read_record():
+            self.current_cb(self.parsed_record()[0])
+            self.current_cb = None
+            if self.calls:
+                self.state = STATE_WRITING
+            else:
+                self.state = STATE_WAITING
+
+
+    def handle_write(self):
+        """Called when socket is ready for write"""
+        assert self.state == STATE_WRITING
+        if self.current_cb is None:
+            # Start working on another request
+            servercall = self.calls.pop(0)
+            self.record = self.rm_stream(servercall.data)
+            self.current_cb = servercall.callback
+
+        assert self.current_cb
+        if self.write_record():
+            self.state = STATE_READING
+            self.record = ""
+
+
+class ClientConnection(RPCConnection):
+    def __init__(self, sock, addr, srv):
+        RPCConnection.__init__(self)
+        self.set_sock(sock)
+        self.addr = addr
+        self.srv = srv
+        self.state = STATE_READING
+
+        
+    def handle_read(self):
+        """Called when socket is ready for read"""
+        if self.read_record():
+            self.state = STATE_WAITING
+            self.srv.call(ServerCall(self.parsed_record()[0], self.got_response))
+
+
+    def handle_write(self):
+        """Called when socket is ready for write"""
+        assert self.state == STATE_WRITING
+        if self.write_record():
+            self.state = STATE_READING
+            self.record = ""
+
+
+    def got_response(self, data):
+        """Callback: We got a response from the server"""
+        # send to client
+        self.state = STATE_WRITING
+        self.record = self.rm_stream(data)
+
+
+def usage():
+    sys.exit("Usage: %s [bind_address:]port:host:hostport ..." % sys.argv[0])
+
+
+def parse_arg(arg):
+    """Parse a command argument, specifying hosts and ports.
+    Returns tuple (bind_address, port, host, hostport)"""
+    fields = arg.split(":")
+    if len(fields) == 3:
+        fields.insert(0, "127.0.0.1")
+
+    if len(fields) != 4:
+        usage()
+
+    bind_address, port, host, hostport = fields
+    port = int(port)
+    hostport = int(hostport)
+    return bind_address, port, host, hostport
+
+
+def main():
+    if len(sys.argv) < 2:
+        usage()
+
+    pe = ProxyEngine()
+
+    #
+    # Determine hosts and ports
+    #
+    for arg in sys.argv[1:]:
+        pe.add_proxy(*parse_arg(arg))
+
+    #
+    # Select loop
+    #
+    while 1:
+        # Set up sets
+        read_set = []
+        read_set.extend(pe.proxies)
+        write_set = []
+        for conn in pe.connections:
+            if conn.readable():
+                read_set.append(conn)
+            if conn.writable():
+                write_set.append(conn)
+
+        rlist, wlist, xlist = select.select(read_set, write_set, [])
+
+        for obj in rlist:
+            obj.handle_read()
+
+        for obj in wlist:
+            obj.handle_write()
+
+        for conn in pe.connections:
+            if conn.eof():
+                pe.connections.remove(conn)
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except KeyboardInterrupt:
+        sys.exit(0)
diff --git a/unfs3/daemon.c b/unfs3/daemon.c
new file mode 100644 (file)
index 0000000..d14af45
--- /dev/null
@@ -0,0 +1,974 @@
+
+/*
+ * UNFS3 server framework
+ * Originally generated using rpcgen
+ * Portions (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#ifndef WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+#else                                 /* WIN32 */
+#include <winsock.h>
+#endif                                /* WIN32 */
+
+#include <fcntl.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#if HAVE_RPC_SVC_SOC_H == 1
+# include <rpc/svc_soc.h>
+#endif
+
+#include "nfs.h"
+#include "mount.h"
+#include "xdr.h"
+#include "fh.h"
+#include "fh_cache.h"
+#include "fd_cache.h"
+#include "user.h"
+#include "daemon.h"
+#include "backend.h"
+#include "Config/exports.h"
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#define UNFS_NAME "UNFS3 unfsd " PACKAGE_VERSION " (C) 2006, Pascal Schmidt <unfs3-server@ewetel.net>\n"
+
+/* write verifier */
+writeverf3 wverf;
+
+/* readdir cookie */
+cookie3 rcookie = 0;
+
+/* options and default values */
+int opt_detach = TRUE;
+char *opt_exports = "/etc/exports";
+int opt_cluster = FALSE;
+char *opt_cluster_path = "/";
+int opt_tcponly = FALSE;
+unsigned int opt_nfs_port = NFS_PORT;  /* 0 means RPC_ANYSOCK */
+unsigned int opt_mount_port = NFS_PORT;
+int opt_singleuser = FALSE;
+int opt_brute_force = FALSE;
+int opt_testconfig = FALSE;
+struct in_addr opt_bind_addr;
+int opt_readable_executables = FALSE;
+char *opt_pid_file = NULL;
+
+/* Register with portmapper? */
+int opt_portmapper = TRUE;
+
+/*
+ * output message to syslog or stdout
+ */
+void logmsg(int prio, const char *fmt, ...)
+{
+    va_list ap;
+
+#if HAVE_VSYSLOG == 0
+    char mesg[1024];
+#endif
+
+    va_start(ap, fmt);
+    if (opt_detach) {
+#if HAVE_VSYSLOG == 1
+       vsyslog(prio, fmt, ap);
+#else
+       vsnprintf(mesg, 1024, fmt, ap);
+       syslog(prio, mesg, 1024);
+#endif
+    } else {
+       vprintf(fmt, ap);
+       putchar('\n');
+    }
+    va_end(ap);
+}
+
+/*
+ * return remote address from svc_req structure
+ */
+struct in_addr get_remote(struct svc_req *rqstp)
+{
+    return (svc_getcaller(rqstp->rq_xprt))->sin_addr;
+}
+
+/*
+ * return remote port from svc_req structure
+ */
+short get_port(struct svc_req *rqstp)
+{
+    return (svc_getcaller(rqstp->rq_xprt))->sin_port;
+}
+
+/*
+ * return the socket type of the request (SOCK_STREAM or SOCK_DGRAM)
+ */
+int get_socket_type(struct svc_req *rqstp)
+{
+    int v, res;
+    socklen_t l;
+
+    l = sizeof(v);
+
+#if HAVE_STRUCT___RPC_SVCXPRT_XP_FD == 1
+    res = getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET, SO_TYPE, &v, &l);
+#else
+    res = getsockopt(rqstp->rq_xprt->xp_sock, SOL_SOCKET, SO_TYPE, &v, &l);
+#endif
+
+    if (res < 0) {
+       logmsg(LOG_CRIT, "unable to determine socket type");
+       return -1;
+    }
+
+    return v;
+}
+
+/*
+ * write current pid to a file
+ */
+static void create_pid_file(void)
+{
+    char buf[16];
+    int fd, res, len;
+
+    if (!opt_pid_file)
+       return;
+
+    fd = backend_open_create(opt_pid_file, O_RDWR | O_CREAT | O_TRUNC, 0644);
+    if (fd == -1) {
+       logmsg(LOG_WARNING, "failed to create pid file `%s'", opt_pid_file);
+       return;
+    }
+#if defined(LOCK_EX) && defined(LOCK_NB)
+    res = backend_flock(fd, LOCK_EX | LOCK_NB);
+    if (res == -1) {
+       logmsg(LOG_WARNING, "failed to lock pid file `%s'", opt_pid_file);
+       backend_close(fd);
+       return;
+    }
+#endif
+
+    sprintf(buf, "%i\n", backend_getpid());
+    len = strlen(buf);
+
+    res = backend_pwrite(fd, buf, len, 0);
+    backend_close(fd);
+    if (res != len) {
+       logmsg(LOG_WARNING, "failed to write pid file `%s'", opt_pid_file);
+    }
+}
+
+/*
+ * remove pid file
+ */
+static void remove_pid_file(void)
+{
+    int res;
+
+    if (!opt_pid_file)
+       return;
+
+    res = backend_remove(opt_pid_file);
+    if (res == -1 && errno != ENOENT) {
+       logmsg(LOG_WARNING, "failed to remove pid file `%s'", opt_pid_file);
+    }
+}
+
+/*
+ * parse command line options
+ */
+static void parse_options(int argc, char **argv)
+{
+    int opt = 0;
+    char *optstring = "bcC:de:hl:m:n:prstTuwi:";
+
+    while (opt != -1) {
+       opt = getopt(argc, argv, optstring);
+       switch (opt) {
+           case 'b':
+               opt_brute_force = TRUE;
+               break;
+#ifdef WANT_CLUSTER
+           case 'c':
+               opt_cluster = TRUE;
+               break;
+           case 'C':
+               opt_cluster_path = optarg;
+               break;
+#endif
+           case 'd':
+               printf(UNFS_NAME);
+               opt_detach = FALSE;
+               break;
+           case 'e':
+#ifndef WIN32
+               if (optarg[0] != '/') {
+                   /* A relative path won't work for re-reading the exports
+                      file on SIGHUP, since we are changing directory */
+                   fprintf(stderr, "Error: relative path to exports file\n");
+                   exit(1);
+               }
+#endif
+               opt_exports = optarg;
+               break;
+           case 'h':
+               printf(UNFS_NAME);
+               printf("Usage: %s [options]\n", argv[0]);
+               printf("\t-h          display this short option summary\n");
+               printf("\t-u          use unprivileged port for services\n");
+               printf("\t-d          do not detach from terminal\n");
+               printf("\t-e <file>   file to use instead of /etc/exports\n");
+               printf("\t-i <file>   write daemon pid to given file\n");
+#ifdef WANT_CLUSTER
+               printf("\t-c          enable cluster extensions\n");
+               printf("\t-C <path>   set path for cluster extensions\n");
+#endif
+               printf("\t-n <port>   port to use for NFS service\n");
+               printf("\t-m <port>   port to use for MOUNT service\n");
+               printf
+                   ("\t-t          TCP only, do not listen on UDP ports\n");
+               printf("\t-p          do not register with the portmapper\n");
+               printf("\t-s          single user mode\n");
+               printf("\t-b          enable brute force file searching\n");
+               printf
+                   ("\t-l <addr>   bind to interface with specified address\n");
+               printf
+                   ("\t-r          report unreadable executables as readable\n");
+               printf("\t-T          test exports file and exit\n");
+               exit(0);
+               break;
+           case 'l':
+               opt_bind_addr.s_addr = inet_addr(optarg);
+               if (opt_bind_addr.s_addr == (unsigned) -1) {
+                   fprintf(stderr, "Invalid bind address\n");
+                   exit(1);
+               }
+               break;
+           case 'm':
+               opt_mount_port = strtol(optarg, NULL, 10);
+               if (opt_mount_port == 0) {
+                   fprintf(stderr, "Invalid port\n");
+                   exit(1);
+               }
+               break;
+           case 'n':
+               opt_nfs_port = strtol(optarg, NULL, 10);
+               if (opt_nfs_port == 0) {
+                   fprintf(stderr, "Invalid port\n");
+                   exit(1);
+               }
+               break;
+           case 'p':
+               opt_portmapper = FALSE;
+               break;
+           case 'r':
+               opt_readable_executables = TRUE;
+               break;
+           case 's':
+               opt_singleuser = TRUE;
+#ifndef WIN32
+               if (backend_getuid() == 0) {
+                   logmsg(LOG_WARNING,
+                          "Warning: running as root with -s is dangerous");
+                   logmsg(LOG_WARNING,
+                          "All clients will have root access to all exported files!");
+               }
+#endif
+               break;
+           case 't':
+               opt_tcponly = TRUE;
+               break;
+           case 'T':
+               opt_testconfig = TRUE;
+               break;
+           case 'u':
+               opt_nfs_port = 0;
+               opt_mount_port = 0;
+               break;
+           case 'i':
+               opt_pid_file = optarg;
+               break;
+           case '?':
+               exit(1);
+               break;
+       }
+    }
+}
+
+/*
+ * signal handler and error exit function
+ */
+void daemon_exit(int error)
+{
+#ifndef WIN32
+    if (error == SIGHUP) {
+       get_squash_ids();
+       exports_parse();
+       return;
+    }
+
+    if (error == SIGUSR1) {
+       if (fh_cache_use > 0)
+           logmsg(LOG_INFO, "fh entries %i access %i hit %i miss %i",
+                  fh_cache_max, fh_cache_use, fh_cache_hit,
+                  fh_cache_use - fh_cache_hit);
+       else
+           logmsg(LOG_INFO, "fh cache unused");
+       logmsg(LOG_INFO, "open file descriptors: read %i, write %i",
+              fd_cache_readers, fd_cache_writers);
+       return;
+    }
+#endif                                /* WIN32 */
+
+    if (opt_portmapper) {
+       svc_unregister(MOUNTPROG, MOUNTVERS1);
+       svc_unregister(MOUNTPROG, MOUNTVERS3);
+    }
+
+    if (opt_portmapper) {
+       svc_unregister(NFS3_PROGRAM, NFS_V3);
+    }
+
+    if (error == SIGSEGV)
+       logmsg(LOG_EMERG, "segmentation fault");
+
+    fd_cache_purge();
+
+    if (opt_detach)
+       closelog();
+
+    remove_pid_file();
+    backend_shutdown();
+
+    exit(1);
+}
+
+/*
+ * NFS service dispatch function
+ * generated by rpcgen
+ */
+static void nfs3_program_3(struct svc_req *rqstp, register SVCXPRT * transp)
+{
+    union {
+       GETATTR3args nfsproc3_getattr_3_arg;
+       SETATTR3args nfsproc3_setattr_3_arg;
+       LOOKUP3args nfsproc3_lookup_3_arg;
+       ACCESS3args nfsproc3_access_3_arg;
+       READLINK3args nfsproc3_readlink_3_arg;
+       READ3args nfsproc3_read_3_arg;
+       WRITE3args nfsproc3_write_3_arg;
+       CREATE3args nfsproc3_create_3_arg;
+       MKDIR3args nfsproc3_mkdir_3_arg;
+       SYMLINK3args nfsproc3_symlink_3_arg;
+       MKNOD3args nfsproc3_mknod_3_arg;
+       REMOVE3args nfsproc3_remove_3_arg;
+       RMDIR3args nfsproc3_rmdir_3_arg;
+       RENAME3args nfsproc3_rename_3_arg;
+       LINK3args nfsproc3_link_3_arg;
+       READDIR3args nfsproc3_readdir_3_arg;
+       READDIRPLUS3args nfsproc3_readdirplus_3_arg;
+       FSSTAT3args nfsproc3_fsstat_3_arg;
+       FSINFO3args nfsproc3_fsinfo_3_arg;
+       PATHCONF3args nfsproc3_pathconf_3_arg;
+       COMMIT3args nfsproc3_commit_3_arg;
+    } argument;
+    char *result;
+    xdrproc_t _xdr_argument, _xdr_result;
+    char *(*local) (char *, struct svc_req *);
+
+    switch (rqstp->rq_proc) {
+       case NFSPROC3_NULL:
+           _xdr_argument = (xdrproc_t) xdr_void;
+           _xdr_result = (xdrproc_t) xdr_void;
+           local = (char *(*)(char *, struct svc_req *)) nfsproc3_null_3_svc;
+           break;
+
+       case NFSPROC3_GETATTR:
+           _xdr_argument = (xdrproc_t) xdr_GETATTR3args;
+           _xdr_result = (xdrproc_t) xdr_GETATTR3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_getattr_3_svc;
+           break;
+
+       case NFSPROC3_SETATTR:
+           _xdr_argument = (xdrproc_t) xdr_SETATTR3args;
+           _xdr_result = (xdrproc_t) xdr_SETATTR3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_setattr_3_svc;
+           break;
+
+       case NFSPROC3_LOOKUP:
+           _xdr_argument = (xdrproc_t) xdr_LOOKUP3args;
+           _xdr_result = (xdrproc_t) xdr_LOOKUP3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_lookup_3_svc;
+           break;
+
+       case NFSPROC3_ACCESS:
+           _xdr_argument = (xdrproc_t) xdr_ACCESS3args;
+           _xdr_result = (xdrproc_t) xdr_ACCESS3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_access_3_svc;
+           break;
+
+       case NFSPROC3_READLINK:
+           _xdr_argument = (xdrproc_t) xdr_READLINK3args;
+           _xdr_result = (xdrproc_t) xdr_READLINK3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_readlink_3_svc;
+           break;
+
+       case NFSPROC3_READ:
+           _xdr_argument = (xdrproc_t) xdr_READ3args;
+           _xdr_result = (xdrproc_t) xdr_READ3res;
+           local = (char *(*)(char *, struct svc_req *)) nfsproc3_read_3_svc;
+           break;
+
+       case NFSPROC3_WRITE:
+           _xdr_argument = (xdrproc_t) xdr_WRITE3args;
+           _xdr_result = (xdrproc_t) xdr_WRITE3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_write_3_svc;
+           break;
+
+       case NFSPROC3_CREATE:
+           _xdr_argument = (xdrproc_t) xdr_CREATE3args;
+           _xdr_result = (xdrproc_t) xdr_CREATE3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_create_3_svc;
+           break;
+
+       case NFSPROC3_MKDIR:
+           _xdr_argument = (xdrproc_t) xdr_MKDIR3args;
+           _xdr_result = (xdrproc_t) xdr_MKDIR3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_mkdir_3_svc;
+           break;
+
+       case NFSPROC3_SYMLINK:
+           _xdr_argument = (xdrproc_t) xdr_SYMLINK3args;
+           _xdr_result = (xdrproc_t) xdr_SYMLINK3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_symlink_3_svc;
+           break;
+
+       case NFSPROC3_MKNOD:
+           _xdr_argument = (xdrproc_t) xdr_MKNOD3args;
+           _xdr_result = (xdrproc_t) xdr_MKNOD3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_mknod_3_svc;
+           break;
+
+       case NFSPROC3_REMOVE:
+           _xdr_argument = (xdrproc_t) xdr_REMOVE3args;
+           _xdr_result = (xdrproc_t) xdr_REMOVE3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_remove_3_svc;
+           break;
+
+       case NFSPROC3_RMDIR:
+           _xdr_argument = (xdrproc_t) xdr_RMDIR3args;
+           _xdr_result = (xdrproc_t) xdr_RMDIR3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_rmdir_3_svc;
+           break;
+
+       case NFSPROC3_RENAME:
+           _xdr_argument = (xdrproc_t) xdr_RENAME3args;
+           _xdr_result = (xdrproc_t) xdr_RENAME3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_rename_3_svc;
+           break;
+
+       case NFSPROC3_LINK:
+           _xdr_argument = (xdrproc_t) xdr_LINK3args;
+           _xdr_result = (xdrproc_t) xdr_LINK3res;
+           local = (char *(*)(char *, struct svc_req *)) nfsproc3_link_3_svc;
+           break;
+
+       case NFSPROC3_READDIR:
+           _xdr_argument = (xdrproc_t) xdr_READDIR3args;
+           _xdr_result = (xdrproc_t) xdr_READDIR3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_readdir_3_svc;
+           break;
+
+       case NFSPROC3_READDIRPLUS:
+           _xdr_argument = (xdrproc_t) xdr_READDIRPLUS3args;
+           _xdr_result = (xdrproc_t) xdr_READDIRPLUS3res;
+           local = (char *(*)(char *, struct svc_req *))
+               nfsproc3_readdirplus_3_svc;
+           break;
+
+       case NFSPROC3_FSSTAT:
+           _xdr_argument = (xdrproc_t) xdr_FSSTAT3args;
+           _xdr_result = (xdrproc_t) xdr_FSSTAT3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_fsstat_3_svc;
+           break;
+
+       case NFSPROC3_FSINFO:
+           _xdr_argument = (xdrproc_t) xdr_FSINFO3args;
+           _xdr_result = (xdrproc_t) xdr_FSINFO3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_fsinfo_3_svc;
+           break;
+
+       case NFSPROC3_PATHCONF:
+           _xdr_argument = (xdrproc_t) xdr_PATHCONF3args;
+           _xdr_result = (xdrproc_t) xdr_PATHCONF3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_pathconf_3_svc;
+           break;
+
+       case NFSPROC3_COMMIT:
+           _xdr_argument = (xdrproc_t) xdr_COMMIT3args;
+           _xdr_result = (xdrproc_t) xdr_COMMIT3res;
+           local =
+               (char *(*)(char *, struct svc_req *)) nfsproc3_commit_3_svc;
+           break;
+
+       default:
+           svcerr_noproc(transp);
+           return;
+    }
+    memset((char *) &argument, 0, sizeof(argument));
+    if (!svc_getargs(transp, (xdrproc_t) _xdr_argument, (caddr_t) & argument)) {
+       svcerr_decode(transp);
+       return;
+    }
+    result = (*local) ((char *) &argument, rqstp);
+    if (result != NULL &&
+       !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
+       svcerr_systemerr(transp);
+       logmsg(LOG_CRIT, "unable to send RPC reply");
+    }
+    if (!svc_freeargs
+       (transp, (xdrproc_t) _xdr_argument, (caddr_t) & argument)) {
+       logmsg(LOG_CRIT, "unable to free XDR arguments");
+    }
+    return;
+}
+
+/*
+ * mount protocol dispatcher
+ * generated by rpcgen
+ */
+static void mountprog_3(struct svc_req *rqstp, register SVCXPRT * transp)
+{
+    union {
+       dirpath mountproc_mnt_3_arg;
+       dirpath mountproc_umnt_3_arg;
+    } argument;
+    char *result;
+    xdrproc_t _xdr_argument, _xdr_result;
+    char *(*local) (char *, struct svc_req *);
+
+    switch (rqstp->rq_proc) {
+       case MOUNTPROC_NULL:
+           _xdr_argument = (xdrproc_t) xdr_void;
+           _xdr_result = (xdrproc_t) xdr_void;
+           local =
+               (char *(*)(char *, struct svc_req *)) mountproc_null_3_svc;
+           break;
+
+       case MOUNTPROC_MNT:
+           _xdr_argument = (xdrproc_t) xdr_dirpath;
+           _xdr_result = (xdrproc_t) xdr_mountres3;
+           local = (char *(*)(char *, struct svc_req *)) mountproc_mnt_3_svc;
+           break;
+
+       case MOUNTPROC_DUMP:
+           _xdr_argument = (xdrproc_t) xdr_void;
+           _xdr_result = (xdrproc_t) xdr_mountlist;
+           local =
+               (char *(*)(char *, struct svc_req *)) mountproc_dump_3_svc;
+           break;
+
+       case MOUNTPROC_UMNT:
+           _xdr_argument = (xdrproc_t) xdr_dirpath;
+           _xdr_result = (xdrproc_t) xdr_void;
+           local =
+               (char *(*)(char *, struct svc_req *)) mountproc_umnt_3_svc;
+           break;
+
+       case MOUNTPROC_UMNTALL:
+           _xdr_argument = (xdrproc_t) xdr_void;
+           _xdr_result = (xdrproc_t) xdr_void;
+           local =
+               (char *(*)(char *, struct svc_req *)) mountproc_umntall_3_svc;
+           break;
+
+       case MOUNTPROC_EXPORT:
+           _xdr_argument = (xdrproc_t) xdr_void;
+           _xdr_result = (xdrproc_t) xdr_exports;
+           local =
+               (char *(*)(char *, struct svc_req *)) mountproc_export_3_svc;
+           break;
+
+       default:
+           svcerr_noproc(transp);
+           return;
+    }
+    memset((char *) &argument, 0, sizeof(argument));
+    if (!svc_getargs(transp, (xdrproc_t) _xdr_argument, (caddr_t) & argument)) {
+       svcerr_decode(transp);
+       return;
+    }
+    result = (*local) ((char *) &argument, rqstp);
+    if (result != NULL &&
+       !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
+       svcerr_systemerr(transp);
+       logmsg(LOG_CRIT, "unable to send RPC reply");
+    }
+    if (!svc_freeargs
+       (transp, (xdrproc_t) _xdr_argument, (caddr_t) & argument)) {
+       logmsg(LOG_CRIT, "unable to free XDR arguments");
+    }
+    return;
+}
+
+static void register_nfs_service(SVCXPRT * udptransp, SVCXPRT * tcptransp)
+{
+    if (opt_portmapper) {
+       pmap_unset(NFS3_PROGRAM, NFS_V3);
+    }
+
+    if (udptransp != NULL) {
+       /* Register NFS service for UDP */
+       if (!svc_register
+           (udptransp, NFS3_PROGRAM, NFS_V3, nfs3_program_3,
+            opt_portmapper ? IPPROTO_UDP : 0)) {
+           fprintf(stderr, "%s\n",
+                   "unable to register (NFS3_PROGRAM, NFS_V3, udp).");
+           daemon_exit(0);
+       }
+    }
+
+    if (tcptransp != NULL) {
+       /* Register NFS service for TCP */
+       if (!svc_register
+           (tcptransp, NFS3_PROGRAM, NFS_V3, nfs3_program_3,
+            opt_portmapper ? IPPROTO_TCP : 0)) {
+           fprintf(stderr, "%s\n",
+                   "unable to register (NFS3_PROGRAM, NFS_V3, tcp).");
+           daemon_exit(0);
+       }
+    }
+}
+
+static void register_mount_service(SVCXPRT * udptransp, SVCXPRT * tcptransp)
+{
+    if (opt_portmapper) {
+       pmap_unset(MOUNTPROG, MOUNTVERS1);
+       pmap_unset(MOUNTPROG, MOUNTVERS3);
+    }
+
+    if (udptransp != NULL) {
+       /* Register MOUNT service (v1) for UDP */
+       if (!svc_register
+           (udptransp, MOUNTPROG, MOUNTVERS1, mountprog_3,
+            opt_portmapper ? IPPROTO_UDP : 0)) {
+           fprintf(stderr, "%s\n",
+                   "unable to register (MOUNTPROG, MOUNTVERS1, udp).");
+           daemon_exit(0);
+       }
+
+       /* Register MOUNT service (v3) for UDP */
+       if (!svc_register
+           (udptransp, MOUNTPROG, MOUNTVERS3, mountprog_3,
+            opt_portmapper ? IPPROTO_UDP : 0)) {
+           fprintf(stderr, "%s\n",
+                   "unable to register (MOUNTPROG, MOUNTVERS3, udp).");
+           daemon_exit(0);
+       }
+    }
+
+    if (tcptransp != NULL) {
+       /* Register MOUNT service (v1) for TCP */
+       if (!svc_register
+           (tcptransp, MOUNTPROG, MOUNTVERS1, mountprog_3,
+            opt_portmapper ? IPPROTO_TCP : 0)) {
+           fprintf(stderr, "%s\n",
+                   "unable to register (MOUNTPROG, MOUNTVERS1, tcp).");
+           daemon_exit(0);
+       }
+
+       /* Register MOUNT service (v3) for TCP */
+       if (!svc_register
+           (tcptransp, MOUNTPROG, MOUNTVERS3, mountprog_3,
+            opt_portmapper ? IPPROTO_TCP : 0)) {
+           fprintf(stderr, "%s\n",
+                   "unable to register (MOUNTPROG, MOUNTVERS3, tcp).");
+           daemon_exit(0);
+       }
+    }
+}
+
+static SVCXPRT *create_udp_transport(unsigned int port)
+{
+    SVCXPRT *transp = NULL;
+    struct sockaddr_in sin;
+    int sock;
+    const int on = 1;
+
+    if (port == 0)
+       sock = RPC_ANYSOCK;
+    else {
+       sin.sin_family = AF_INET;
+       sin.sin_port = htons(port);
+       sin.sin_addr.s_addr = opt_bind_addr.s_addr;
+       sock = socket(PF_INET, SOCK_DGRAM, 0);
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
+       if (bind(sock, (struct sockaddr *) &sin, sizeof(struct sockaddr))) {
+           perror("bind");
+           fprintf(stderr, "Couldn't bind to udp port %d\n", port);
+           exit(1);
+       }
+    }
+
+    transp = svcudp_bufcreate(sock, NFS_MAX_UDP_PACKET, NFS_MAX_UDP_PACKET);
+
+    if (transp == NULL) {
+       fprintf(stderr, "%s\n", "cannot create udp service.");
+       daemon_exit(0);
+    }
+
+    return transp;
+}
+
+static SVCXPRT *create_tcp_transport(unsigned int port)
+{
+    SVCXPRT *transp = NULL;
+    struct sockaddr_in sin;
+    int sock;
+    const int on = 1;
+
+    if (port == 0)
+       sock = RPC_ANYSOCK;
+    else {
+       sin.sin_family = AF_INET;
+       sin.sin_port = htons(port);
+       sin.sin_addr.s_addr = opt_bind_addr.s_addr;
+       sock = socket(PF_INET, SOCK_STREAM, 0);
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
+       if (bind(sock, (struct sockaddr *) &sin, sizeof(struct sockaddr))) {
+           perror("bind");
+           fprintf(stderr, "Couldn't bind to tcp port %d\n", port);
+           exit(1);
+       }
+    }
+
+    transp = svctcp_create(sock, 0, 0);
+
+    if (transp == NULL) {
+       fprintf(stderr, "%s\n", "cannot create tcp service.");
+       daemon_exit(0);
+    }
+
+    return transp;
+}
+
+/* Run RPC service. This is our own implementation of svc_run(), which
+   allows us to handle other events as well. */
+static void unfs3_svc_run()
+{
+    fd_set readfds;
+    struct timeval tv;
+
+    for (;;) {
+       fd_cache_close_inactive();
+       readfds = svc_fdset;
+       tv.tv_sec = 1;
+       tv.tv_usec = 0;
+       /* Note: On Windows, it's not possible to call select with all sets
+          empty; to use it as a sleep function. In our case, however,
+          readfds should never be empty, since we always have our listen
+          socket. Well, at least hope that our Windows RPC library works
+          like that. oncrpc-ms does. */
+       switch (select(FD_SETSIZE, &readfds, NULL, NULL, &tv)) {
+           case -1:
+               if (errno == EINTR) {
+                   continue;
+               }
+               perror("unfs3_svc_run: select failed");
+               return;
+           case 0:
+               /* timeout */
+               continue;
+           default:
+               svc_getreqset(&readfds);
+       }
+    }
+}
+
+/*
+ * Generate write verifier based on PID and current time
+ */
+void regenerate_write_verifier(void)
+{
+    *(wverf + 0) = (uint32) getpid();
+    *(wverf + 0) ^= rand();
+    *(wverf + 4) = (uint32) time(NULL);
+}
+
+/*
+ * Change readdir cookie value
+ */
+void change_readdir_cookie(void)
+{
+    rcookie = rcookie >> 32;
+    ++rcookie;
+    rcookie = rcookie << 32;
+}
+
+/*
+ * NFSD main function
+ * originally generated by rpcgen
+ * forking, logging, options, and signal handler stuff added
+ */
+int main(int argc, char **argv)
+{
+    register SVCXPRT *tcptransp = NULL, *udptransp = NULL;
+    pid_t pid = 0;
+
+#ifndef WIN32
+    struct sigaction act;
+    sigset_t actset;
+#endif                                /* WIN32 */
+    int res;
+
+    opt_bind_addr.s_addr = INADDR_ANY;
+
+    parse_options(argc, argv);
+    if (optind < argc) {
+       fprintf(stderr, "Error: extra arguments on command line\n");
+       exit(1);
+    }
+
+    /* init write verifier */
+    regenerate_write_verifier();
+
+    res = backend_init();
+    if (res == -1) {
+       fprintf(stderr, "backend initialization failed\n");
+       daemon_exit(0);
+    }
+
+    /* config test mode */
+    if (opt_testconfig) {
+       res = exports_parse();
+       if (res) {
+           exit(0);
+       } else {
+           fprintf(stderr, "Parse error in `%s'\n", opt_exports);
+           exit(1);
+       }
+    }
+
+    if (opt_detach) {
+       /* prepare syslog access */
+       openlog("unfsd", LOG_CONS | LOG_PID, LOG_DAEMON);
+    } else {
+       /* flush stdout after each newline */
+       setvbuf(stdout, NULL, _IOLBF, 0);
+    }
+
+    /* NFS transports */
+    if (!opt_tcponly)
+       udptransp = create_udp_transport(opt_nfs_port);
+    tcptransp = create_tcp_transport(opt_nfs_port);
+
+    register_nfs_service(udptransp, tcptransp);
+
+    /* MOUNT transports. If ports are equal, then the MOUNT service can reuse 
+       the NFS transports. */
+    if (opt_mount_port != opt_nfs_port) {
+       if (!opt_tcponly)
+           udptransp = create_udp_transport(opt_mount_port);
+       tcptransp = create_tcp_transport(opt_mount_port);
+    }
+
+    register_mount_service(udptransp, tcptransp);
+
+#ifndef WIN32
+    if (opt_detach) {
+       pid = fork();
+       if (pid == -1) {
+           fprintf(stderr, "could not fork into background\n");
+           daemon_exit(0);
+       }
+    }
+#endif                                /* WIN32 */
+
+    if (!opt_detach || pid == 0) {
+#ifndef WIN32
+       sigemptyset(&actset);
+       act.sa_handler = daemon_exit;
+       act.sa_mask = actset;
+       act.sa_flags = 0;
+       sigaction(SIGHUP, &act, NULL);
+       sigaction(SIGTERM, &act, NULL);
+       sigaction(SIGINT, &act, NULL);
+       sigaction(SIGQUIT, &act, NULL);
+       sigaction(SIGSEGV, &act, NULL);
+       sigaction(SIGUSR1, &act, NULL);
+
+       act.sa_handler = SIG_IGN;
+       sigaction(SIGPIPE, &act, NULL);
+       sigaction(SIGUSR2, &act, NULL);
+       sigaction(SIGALRM, &act, NULL);
+
+       /* don't make directory we started in busy */
+       chdir("/");
+
+       /* detach from terminal */
+       if (opt_detach) {
+           setsid();
+           fclose(stdin);
+           fclose(stdout);
+           fclose(stderr);
+       }
+#endif                                /* WIN32 */
+
+       /* no umask to not screw up create modes */
+       umask(0);
+
+       /* create pid file if wanted */
+       create_pid_file();
+
+       /* initialize internal stuff */
+       fh_cache_init();
+       fd_cache_init();
+       get_squash_ids();
+       exports_parse();
+
+       unfs3_svc_run();
+       exit(1);
+       /* NOTREACHED */
+    }
+
+    return 0;
+}
diff --git a/unfs3/daemon.h b/unfs3/daemon.h
new file mode 100644 (file)
index 0000000..13b20af
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * UNFS3 server framework
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_DAEMON_H
+#define UNFS3_DAEMON_H
+
+#include "nfs.h"
+
+/* exit status for internal errors */
+#define CRISIS 99
+
+/* HP-UX does not have seteuid() and setegid() */
+#if HAVE_SETEUID == 0 && HAVE_SETRESUID == 1
+#define seteuid(u) setresuid(-1, u, -1)
+#endif
+#if HAVE_SETEGID == 0 && HAVE_SETRESGID == 1
+#define setegid(g) setresgid(-1, g, -1)
+#endif
+
+/* error handling */
+void daemon_exit(int);
+void logmsg(int, const char *, ...);
+
+/* remote address */
+struct in_addr get_remote(struct svc_req *);
+short get_port(struct svc_req *);
+int get_socket_type(struct svc_req *rqstp);
+
+/* write verifier */
+extern writeverf3 wverf;
+void regenerate_write_verifier(void);
+
+/* readdir cookie */
+extern cookie3 rcookie;
+void change_readdir_cookie(void);
+
+/* options */
+extern int     opt_detach;
+extern char    *opt_exports;
+extern int     opt_cluster;
+extern char    *opt_cluster_path;
+extern int     opt_singleuser;
+extern int     opt_brute_force;
+extern int     opt_readable_executables;
+
+#endif
diff --git a/unfs3/debian/changelog b/unfs3/debian/changelog
new file mode 100644 (file)
index 0000000..53e9705
--- /dev/null
@@ -0,0 +1,88 @@
+unfs3 (0.9.22+dfsg-1maemo2) unstable; urgency=low
+
+  * init.d script changed to start portmap 
+
+ -- Walter <walter.guerra@signove.com>  Thu,  4 Jun 2009 10:53:07 -0400
+
+unfs3 (0.9.22+dfsg-1maemo1) unstable; urgency=low
+
+  * Downgrading pacakge to debhelper 5.
+
+ -- Hallyson Melo <hallyson.melo@openbossa.org>  Mon, 26 Jan 2009 15:20:20 -0300
+
+unfs3 (0.9.22+dfsg-1) unstable; urgency=low
+
+  * Updating vcs fields in control file.
+  * Replacing obsolete dh_clean -k with dh_prep.
+  * Merging upstream version 0.9.22+dfsg.
+  * Prefixing debhelper files with package name.
+  * Upgrading package to debhelper 7.
+  * Updating year in copyright.
+  * Updating rules to current state of the art.
+
+ -- Daniel Baumann <daniel@debian.org>  Sun, 25 Jan 2009 02:50:00 +0100
+
+unfs3 (0.9.21+dfsg-1) unstable; urgency=low
+
+  * Merging upstream version 0.9.21+dfsg.
+
+ -- Daniel Baumann <daniel@debian.org>  Wed, 16 Jul 2008 01:09:00 +0200
+
+unfs3 (0.9.20+dfsg-2) unstable; urgency=low
+
+  * Don't hide make errors in clean target of rules.
+
+ -- Daniel Baumann <daniel@debian.org>  Sun, 23 Dec 2007 21:14:00 +0100
+
+unfs3 (0.9.20+dfsg-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Daniel Baumann <daniel@debian.org>  Sat, 22 Dec 2007 12:18:00 +0100
+
+unfs3 (0.9.19+dfsg-1) unstable; urgency=low
+
+  * New upstream release:
+    - fix auxiliary group support (Closes: #432896).
+
+ -- Daniel Baumann <daniel@debian.org>  Mon, 26 Nov 2007 21:52:00 +0100
+
+unfs3 (0.9.18+dfsg-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Daniel Baumann <daniel@debian.org>  Tue,  4 Sep 2007 23:19:00 +0200
+
+unfs3 (0.9.17+dfsg-1) unstable; urgency=low
+
+  * New upstream release.
+  * Added lsb header to the init script.
+
+ -- Daniel Baumann <daniel@debian.org>  Mon, 12 Mar 2007 10:07:00 +0100
+
+unfs3 (0.9.16+dfsg-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Daniel Baumann <daniel@debian.org>  Thu, 18 Jan 2007 11:27:00 +0100
+
+unfs3 (0.9.15+dfsg-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Daniel Baumann <daniel@debian.org>  Sat,  7 Oct 2006 00:31:00 +0200
+
+unfs3 (0.9.13+dfsg-2) unstable; urgency=low
+
+  * New email address.
+  * Bumped policy version.
+  * Added note about locking to the long-description (Closes: #358925).
+
+ -- Daniel Baumann <daniel@debian.org>  Sat,  8 Jul 2006 14:55:00 +0200
+
+unfs3 (0.9.13+dfsg-1) unstable; urgency=low
+
+  * Initial release (Closes: #214872).
+  * Rebuild upstream-tarball without contrib directory due to missing license.
+
+ -- Daniel Baumann <daniel.baumann@panthera-systems.net>  Sat, 28 Jan 2006 14:37:00 +0100
diff --git a/unfs3/debian/compat b/unfs3/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/unfs3/debian/control b/unfs3/debian/control
new file mode 100644 (file)
index 0000000..7f5efec
--- /dev/null
@@ -0,0 +1,23 @@
+Source: unfs3
+Section: net
+Priority: optional
+Maintainer: Daniel Baumann <daniel@debian.org>
+Build-Depends: debhelper (>= 5), autotools-dev, bison, flex
+Standards-Version: 3.8.0
+Homepage: http://unfs3.sourceforge.net/
+Vcs-Browser: http://git.debian.net/?p=debian/unfs3.git
+Vcs-Git: git://git.debian.net/git/debian/unfs3.git
+
+Package: unfs3
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, portmap
+Conflicts: nfs-server
+Replaces: nfs-server
+Provides: nfs-server
+Description: User-space NFSv3 Server
+ UNFS3 is a user-space implementation of the NFSv3 server specification. It
+ provides a daemon for the MOUNT and NFS protocols, which are used by NFS
+ clients for accessing files on the server.
+ .
+ Unlike nfs-user-server, unfs3 is capable of handling files larger than 2GB, but
+ there is currently no support for file locking.
diff --git a/unfs3/debian/copyright b/unfs3/debian/copyright
new file mode 100644 (file)
index 0000000..5433015
--- /dev/null
@@ -0,0 +1,81 @@
+Author: Pascal Schmidt <unfs3-server@ewetel.net>
+Download: http://unfs3.sourceforge.net/
+
+Files: *
+Copyright: (C) 2003-2009 Pascal Schmidt <unfs3-server@ewetel.net>
+License: BSD
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ .
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ .
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ .
+ 3. The name of the author may not be used to endorse or promote products
+    derived from this software without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+Files: md5.*
+Copyright (C) 1999-2002 Aladdin Enterprises <http://www.aladdin.com/>
+License: BSD
+ This software is provided 'as-is', without any express or implied
+ warranty.  In no event will the authors be held liable for any damages
+ arising from the use of this software.
+ .
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+ .
+ 1. The origin of this software must not be misrepresented; you must not
+    claim that you wrote the original software. If you use this software
+    in a product, an acknowledgment in the product documentation would be
+    appreciated but is not required.
+ .
+ 2. Altered source versions must be plainly marked as such, and must not be
+    misrepresented as being the original software.
+ .
+ 3. This notice may not be removed or altered from any source distribution.
+
+Files: debian/*
+Copyright: (C) 2006-2009 Daniel Baumann <daniel@debian.org>
+License: BSD
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ .
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ .
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ .
+ 3. The name of the author may not be used to endorse or promote products
+    derived from this software without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
diff --git a/unfs3/debian/rules b/unfs3/debian/rules
new file mode 100755 (executable)
index 0000000..fb2332c
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/make -f
+
+DEB_HOST_GNU_TYPE      ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE     ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE))
+       CROSS=CC=$(DEB_HOST_GNU_TYPE)-gcc
+else
+       CROSS=
+endif
+
+upstream:
+       rm -rf contrib # No license attached.
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp
+       rm -f config.guess config.sub
+
+       [ ! -f Makefile ] || $(MAKE) distclean
+
+       dh_clean
+
+config.status: configure
+       dh_testdir
+
+ifneq "$(wildcard /usr/share/misc/config.sub)" ""
+       cp -f /usr/share/misc/config.sub config.sub
+endif
+ifneq "$(wildcard /usr/share/misc/config.guess)" ""
+       cp -f /usr/share/misc/config.guess config.guess
+endif
+
+       ./configure $(CROSS) --prefix=/usr --mandir=\$${prefix}/share/man --enable-cluster CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs"
+
+build: build-stamp
+build-stamp: config.status
+       dh_testdir
+
+       $(MAKE)
+
+       touch build-stamp
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k
+       dh_installdirs
+
+       $(MAKE) install prefix=$(CURDIR)/debian/unfs3/usr
+
+binary: binary-arch
+
+binary-arch: install
+       dh_testdir
+       dh_testroot
+       dh_installchangelogs
+       dh_installdocs
+       dh_installinit --update-rcd-params='defaults 25'
+       dh_installman
+       dh_strip
+       dh_compress
+       dh_fixperms
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary-indep:
+
+.PHONY: clean build install binary binary-arch binary-indep
diff --git a/unfs3/debian/unfs3.default b/unfs3/debian/unfs3.default
new file mode 100644 (file)
index 0000000..f93fdbe
--- /dev/null
@@ -0,0 +1,88 @@
+# Defaults for unfs3 (user-space NFSv3 server)
+
+# Unprivileged port
+# Use an unprivileged port for NFS and MOUNT service. Normally, unfsd will use
+# port number 2049, which is the standard port for NFS. When this option is in
+# effect, arbitrary ports chosen by the RPC library will be used. You may need
+# to use this option when running unfsd from a normal user account.
+PORT_UNPRIVILEGED="-u"
+
+# Specified port
+# Use the specified port for the NFS service.
+PORT_NFS="2049"
+
+# Specified port (MOUNT service)
+# Use the specified port for the MOUNT service. The default is to use port
+# number 2049, the same as for the NFS service. You can use the same port for
+# both services if you want.
+PORT_MOUNT="2049"
+
+# TCP only operation
+# By default, unfsd provides its services to clients using either UDP or TCP as
+# communications protocol. When this option is present, only TCP connections are
+# serviced.
+#TCP_ONLY="-t"
+
+# Register with the portmapper
+# Do not register with the portmapper. This will prevent other hosts from
+# finding out the port numbers used for the MOUNT and NFS services by querying
+# the portmap daemon. Clients will need to manually specify the port numbers to
+# use (on Linux clients, use the mountport and port mount options).
+#NO_PORTMAPPER="-p"
+
+# Expiring write cache
+# Allow the built-in file descriptor cache to expire writers. For performance
+# reasons, unfsd keeps file descriptors open across multiple READ or WRITE
+# requests. Normally, only READ file descriptors will be expired from the cache
+# when it fills up. Setting this option allows file descriptors from WRITE
+# operations to be expired, too. When this  happens, pending data will be
+# written to the server filesystem. However, if an error occurs while doing
+# this, there is no way to notify the NFS client of the error. A message
+# indicating the problem will be sent to the system log on the server.
+#NO_WRITE_CACHE="-w"
+
+# Cluster extensions
+# Enable cluster extensions. When this option is enabled, so-called tagged
+# files are handled differently from normal files, making it possible to
+# serve different file  contents to different clients for the same filename.
+# See tags(7) for a description of tagged files. This option causes a
+# performance hit.
+#CLUSTER_EXTENSION="-c"
+
+# Cluster path
+# Limit the use of cluster extensions to a list of colon-seperated directories.
+# When this option is present, the performance hit caused by clustering
+# extensions only applies to the listed directories and their subdirectories.
+#CLUSTER_PATH="-C /"
+
+# Single user mode
+# Activate basic uid translation. This option is useful when the server and
+# client are using different user and group ids. All requests from the client
+# will be served from the user id that started unfsd, no user id switching will
+# take  place (even if unfsd was started by root). Ownership is reported as
+# follows: files belonging to the user id running unfsd will look as if they
+# are owned by the client's user. Other files will look as if they are owned by
+# root. The same principle applies to group ownership.
+#SINGLE_USER="-s"
+
+# Brute force file searching
+# Normally, when you rename a file across several directories on an NFS volume,
+# the filehandle for that file becomes stale. When this option is enabled, unfsd
+# will attempt a recursive search on the relevant server filesystem to find the
+# file referenced by the filehandle. This can have a huge performance impact as
+# this will also happen for files that were really deleted (by another NFS
+# client) instead of moved, and cannot be found.
+#BRUTE_FORCE="-b"
+
+# Bind to interface with specified address
+# The default is to bind to all local interfaces.
+#INTERFACE="-l 127.0.0.1"
+
+# Debug mode
+# When this option is present, unfsd will not fork into the background at
+# startup, and all messages that would normally go to the system log go to
+# stdout instead.
+#DEBUG="-d"
+
+# Additional options that are passed to the Daemon.
+DAEMON_OPTS="$PORT_UNPRIVILEGED -n $PORT_NFS -m $PORT_MOUNT $TCP_ONLY $NO_PORTMAPPER $NO_WRITE_CACHE $CLUSTER_EXTENSION $CLUSTER_PATH $SINGLE_USER $BRUTE_FORCE $INTERFACE $DEBUG"
diff --git a/unfs3/debian/unfs3.docs b/unfs3/debian/unfs3.docs
new file mode 100644 (file)
index 0000000..b577397
--- /dev/null
@@ -0,0 +1,4 @@
+CREDITS
+NEWS
+README
+README.nfsroot
diff --git a/unfs3/debian/unfs3.init b/unfs3/debian/unfs3.init
new file mode 100644 (file)
index 0000000..6dc1952
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides:          unfs3
+# Required-Start:    $network
+# Required-Stop:     $network
+# Should-Start:
+# Should-Stop:
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: starts user-space NFSv3 server
+# Description:       UNFS3 is a user-space implementation of the NFSv3 server
+#                    specification.
+### END INIT INFO
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/sbin/unfsd
+NAME=unfs3
+DESC=unfs3
+
+test -x $DAEMON -a -f /etc/exports || exit 0
+
+# Include #PACKAGE# defaults if available
+if [ -f /etc/default/unfs3 ]
+then
+       . /etc/default/unfs3
+fi
+
+set -e
+
+case "$1" in
+       start)
+               echo -n "Starting $DESC: "
+               /etc/init.d/portmap start
+               start-stop-daemon --start --quiet --oknodo --exec $DAEMON \
+                       -- $DAEMON_OPTS
+               echo "$NAME."
+               ;;
+
+       stop)
+               echo -n "Stopping $DESC: "
+               start-stop-daemon --stop --oknodo --quiet --exec $DAEMON
+               echo "$NAME."
+               ;;
+
+       restart|force-reload)
+               echo -n "Restarting $DESC: "
+               start-stop-daemon --stop --oknodo --quiet --exec $DAEMON
+               sleep 1
+               start-stop-daemon --start --quiet --oknodo --exec $DAEMON \
+                       -- $DAEMON_OPTS
+               echo "$NAME."
+               ;;
+
+       *)
+               N=/etc/init.d/$NAME
+               echo "Usage: $N {start|stop|restart|force-reload}" >&2
+               exit 1
+               ;;
+esac
+
+exit 0
diff --git a/unfs3/debian/unfs3.postinst b/unfs3/debian/unfs3.postinst
new file mode 100644 (file)
index 0000000..868d0ea
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+set -e
+
+case "${1}" in
+       configure)
+               if [ ! -f /etc/exports ]
+               then
+
+cat > /etc/exports << EOF
+# /etc/exports: the access control list for filesystems which may be exported
+#               to NFS clients.  See unfsd(8).
+EOF
+
+               fi
+               ;;
+
+       abort-upgrade|abort-remove|abort-deconfigure)
+
+               ;;
+
+       *)
+               echo "postinst called with unknown argument \`${1}'" >&2
+               exit 1
+               ;;
+esac
+
+#DEBHELPER#
+
+exit 0
diff --git a/unfs3/debian/unfs3.postrm b/unfs3/debian/unfs3.postrm
new file mode 100644 (file)
index 0000000..8a1bac4
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+set -e
+
+case "${1}" in
+       purge)
+               if [ -e /etc/exports ]
+               then
+                       rm -f /etc/exports
+               fi
+               ;;
+
+       remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+
+               ;;
+
+       *)
+               echo "postrm called with unknown argument \`${1}'" >&2
+               exit 1
+               ;;
+esac
+
+#DEBHELPER#
+
+exit 0
diff --git a/unfs3/doc/README.win b/unfs3/doc/README.win
new file mode 100644 (file)
index 0000000..55fa4d7
--- /dev/null
@@ -0,0 +1,51 @@
+
+Building unfs3 for Windows
+==========================
+
+Building unfs3 using MinGW
+--------------------------
+1. Build and install the oncrpc-ms library (http://oncrpc-ms.sourceforge.net/)
+
+2. Configure unfs3 with:
+
+LIBS="/myprefix/oncrpc-ms/lib/liboncrpc.a -lwsock32" CFLAGS="-I/myprefix/oncrpc-ms/include -DONCRPCDLL" ./configure --host=`gcc -dumpmachine`
+
+
+Using unfs3 on Windows
+======================
+
+Limitations
+-----------
+* Single-user mode is required. Internally, unfs3 assumes that it is
+  running with uid 0. 
+
+* Foreground mode is required. 
+
+* Inode numbers are emulated by hashing file paths. This means that
+  there's a small risk that two files ends up with the same inode
+  number.
+
+
+Path syntax
+-----------
+unfs3 implements a virtual file system. The root "/" directory
+contains the drive letters of the drives present on the system. Below
+each drive letter, the actual drive contents is found. unfs3 uses
+forward slashes instead of backslashes. This applies both to mount
+requests and the exports-file. 
+
+
+Example exports file
+--------------------
+/a (ro)
+/c/windows/temp (rw)
+
+
+Example invocation
+------------------
+unfsd.exe -s -d -p -n 4711 -m 4711 -e exports
+
+
+Example mount command from a Linux host
+---------------------------------------
+mount -o port=4711,mountport=4711,mountvers=3,nfsvers=3,nolock,tcp mywindowshost:/c/windows/temp /mnt
diff --git a/unfs3/doc/TODO b/unfs3/doc/TODO
new file mode 100644 (file)
index 0000000..291bd76
--- /dev/null
@@ -0,0 +1,12 @@
+
+* In some cases, unfs3 issues system calls like stat() with a path
+  beginning with double slashes. This happens to work on most UNIX
+  systems, but this is not guaranteed by POSIX. See
+  http://www.opengroup.org/onlinepubs/009695399/xrat/xbd_chap04.html
+  or section 2.2.2.102 in POSIX-1003.2-Draft-12. 
+
+Windows platform:
+
+* Cluster extensions have not been tried on this platform.
+
+* Investigate possibilities for changing times on directory
diff --git a/unfs3/doc/kirch1.txt b/unfs3/doc/kirch1.txt
new file mode 100644 (file)
index 0000000..eb0ea25
--- /dev/null
@@ -0,0 +1,271 @@
+
+                   Re: A multi-threaded NFS server for Linux
+
+   Olaf Kirch (okir@monad.swb.de)
+   Tue, 26 Nov 1996 23:09:08 +0100
+
+     * Messages  sorted  by:  [1][  date ][2][ thread ][3][ subject ][4][
+       author ]
+     * Next message: [5]Olaf Kirch: "Re: rpc.lockd/rpc.statd"
+     * Previous message: [6]Paul Christenson: "smail SPAM filter?"
+     * Next  in  thread:  [7]Linus  Torvalds:  "Re:  A multi-threaded NFS
+       server for Linux"
+     * Reply:  [8]Linus  Torvalds:  "Re:  A multi-threaded NFS server for
+       Linux"
+     _________________________________________________________________
+
+   Hi all,
+
+   here are some ramblings about implementing nfsd, the differences
+   between kernel- and user-space, and life in general. It's become quite
+   long, so if you're not interested in either of these topics,
+   just skip it...
+
+   On Sun, 24 Nov 1996 12:01:01 PST, "H.J. Lu" wrote:
+   > With the upcoming the Linux C library 6.0, it is possible to
+   > implement a multi-threaded NFS server in the user space using
+   > the kernel-based pthread and MT-safe API included in libc 6.0.
+
+   In  my  opinion,  servicing NFS from user space is an idea that should
+   die.
+   The current unfsd (and I'm pretty sure this will hold for any other
+   implementation) has a host of problems:
+
+   1. Speed.
+
+   This is only partly related to nfsd being single-threaded. I have
+   run some benchmarks a while ago comparing my kernel-based nfsd to
+   the user-space nfsd.
+
+   In  the  unfsd  case,  I  was  running 4 daemons in parallel (which is
+   possible
+   even now as long as you restrict yourself to read-only access), and
+   found  the  upper  limit  for peak throughput was around 800 KBps; the
+   rate
+   for sustained reads was even lower. In comparison, the kernel-based
+   nfsd achieved around than 1.1 MBps peak throughput which is almost
+   the  theoretical  cheapernet  limit;  its  sustained rate was around 1
+   MBps.
+   Testers of my recent knfsd implementation reported a sustained rate
+   of 3.8 MBps over 100 Mbps Ethernet.
+
+   Even  though  some tweaking of the unfsd source (especially by getting
+   rid
+   of  the  Sun  RPC  code)  may  improve  performance some more, I don't
+   believe
+   the  user-space  can  be  pushed  much  further.  [Speaking of the RPC
+   library,
+   a rewrite would be required anyway to safely support NFS over TCP. You
+   can easily hang a vanilla RPC server by sending an incomplete request
+   over TCP and keeping the connection open]
+
+   Now add to that the synchronization overhead required to keep the file
+   handle cache in sync between the various threads...
+
+   This leads me straight to the next topic:
+
+   2. File Handle Layout
+
+   Traditional  nfsds usually stuff a file's device and inode number into
+   the
+   file handle, along with some information on the exported inode. Since
+   a user space program has no way of opening a file just given its inode
+   number,  unfsd  takes  a  different  approach.  It basically creates a
+   hashed
+   version  of  the  file's  path. Each path component is stat'ed, and an
+   8bit
+   hash of the component's device and inode number is used.
+
+   The first problem is that this kind of file handle is not invariant
+   against renames from one directory to another. Agreed, this doesn't
+   happen too often, but it does break Unix semantics. Try this on an
+   nfs-mounted file system (with appropriate foo and bar):
+
+   (mv bar foo/bar; cat) < bar
+
+   The second problem is a lot worse. When unfsd is presented with a file
+   handle it does not have in its cache, it must map it to a valid path
+   name. This is basically done in the following way:
+
+   path = "/";
+   depth = 0;
+   while (depth < length(fhandle)) {
+   deeper:
+   dirp = opendir(path);
+   while ((entry = readdir(dirp)) != NULL) {
+   if (hash(dev,ino) matches fhandle component) {
+   remember dirp
+   append entry to path
+   depth++;
+   goto deeper;
+   }
+   }
+   closedir(dirp);
+   backtrack;
+   }
+
+   Needless to say, this is not very fast. The file handle cache helps
+   a lot here, but this kind of mapping operation occurs far more often
+   than one might expect (consider a development tree where files get
+   created   and   deleted   continuously).   In  addition,  the  current
+   implementation
+   discards conflicting handles when there's a hash collision.
+
+   This file handle layout also leaves little room for any additional
+   baggage. Unfsd currently uses 4 bytes for an inode hash of the file
+   itself and 28 bytes for the hashed path, but as soon as you add other
+   information like the inode generation number, you will sooner or
+   later run out of room.
+
+   Last not least, the file handle cache must be strictly synchronized
+   between different nfsd processes/threads. Suppose you rename foo to
+   bar,  which  is performed by thread1, then try to read the file, which
+   is
+   performed  by  thread2.  If the latter doesn't know the cached path is
+   stale,
+   it  will  fail.  You  could of course retry every operation that fails
+   with
+   ENOENT, but this will add even more clutter and overhead to the code.
+
+   3. Adherence to the NFSv2 specification
+
+   The  Linux  nfsd  currently  does  not  fulfill  the NFSv2 spec in its
+   entirety.
+   Especially  when  it  comes  to  safe  writes, it is really a fake. It
+   neither
+   makes  an  attempt  to  sync  file  data before replying to the client
+   (which
+   could be implemented, along with the `async' export option for turning
+   off this kind of behavior), nor does it sync meta-data after inode
+   operations (which is impossible from user space). To most people this
+   is no big loss, but this behavior is definitely not acceptable if you
+   want industry-strengh NFS.
+
+   But  even  if  you  did  implement at least synchronous file writes in
+   unfsd,
+   be it as an option or as the default, there seems to be no way to
+   implement some of the more advanced techniques like gathered writes.
+   When implementing gathered writes, the server tries to detect whether
+   other nfsd threads are writing to the file at the same time (which
+   frequently happens when the client's biods flush out the data on file
+   close),  and  if  they  do,  it  delays  syncing  file  data for a few
+   milliseconds
+   so  the  others can finish first, and then flushes all data in one go.
+   You
+   can do this in kernel-land by watching inode->i_writecount, but you're
+   totally at a loss in user-space.
+
+   4. Supporting NFSv3
+
+   A   user-space   NFS  server  is  not  particularly  well  suited  for
+   implementing
+   NFSv3.  For  instance,  NFSv3  tries  to help cache consistency on the
+   client
+   by   providing  pre-operation  attributes  for  some  operations,  for
+   instance
+   the WRITE call. When a client finds that the pre-operation attributes
+   returned by the server agree with those it has cached, it can safely
+   assume that any data it has cached was still valid when the server
+   replied  to  its  call,  so there's no need to discard the cached file
+   data
+   and meta-data.
+
+   However, pre-op attributes can only be provided safely when the server
+   retains  exclusive  access to the inode throughout the operation. This
+   is
+   impossible from user space.
+
+   A similar example is the exclusive create operation where a verifier
+   is stored in the inode's atime/mtime fields by the server to guarantee
+   exactly-once  behavior  even  in  the face of request retransmissions.
+   These
+   values cannot be checked atomically by a user-space server.
+
+   What this boils down to is that a user-space server cannot, without
+   violating the protocol spec, implement many of the advanced features
+   of NFSv3.
+
+   5. File locking over NFS
+
+   Supporting lockd in user-space is close to impossible. I've tried it,
+   and have run into a large number of problems. Some of the highlights:
+
+   * lockd can provide only a limited number of locks at the same
+   time because it has only a limited number of file descriptors.
+
+   * When lockd blocks a client's lock request because of a lock held
+   by a local process on the server, it must continuously poll
+   /proc/locks to see whether the request could be granted. What's
+   more, if there's heavy contention for the file, it may take
+   a long time before it succeeds because it cannot add itself
+   to the inode's lock wait list in the kernel. That is, unless
+   you want it to create a new thread just for blocking on this
+   lock.
+
+   * Lockd must synchronize its file handle cache with that of
+   the NFS servers. Unfortunately, lockd is also needed when
+   running as an NFS client only, so you run into problems with
+   who owns the file handle cache, and how to share it between
+   these to services.
+
+   6. Conclusion
+
+   Alright,  this  has  become  rather  long.  Some  of the problems I've
+   described
+   above  may  be  solvable with more or less effort, but I believe that,
+   taken
+   as a whole, they make a pretty strong argument against sticking with
+   a user-space nfsd.
+
+   In  kernel-space,  most of these issues are addressed most easily, and
+   more
+   efficiently. My current kernel nfsd is fairly small. Together with the
+   RPC  core,  which  is  used  by  both  client  and server, it takes up
+   something
+   like 20 pages--don't quote me on the exact number. As mentioned above,
+   it is also pretty fast, and I hope I'll be able to also provide fully
+   functional file locking soon.
+
+   If you want to take a look at the current snapshot, it's available at
+   ftp.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/linux-nfs-X.Y.ta
+   r.gz.
+   This version still has a bug in the nfsd readdir implementation, but
+   I'll  release  an  updated  (and  fixed) version as soon as I have the
+   necessary
+   lockd rewrite sorted out.
+
+   I  would  particularly  welcome  comments  from  Keepers of the Source
+   whether
+   my NFS rewrite has any chance of being incorporated into the kernel at
+   some time... that would definitely motivate me to sick more time into
+   it than I currently do.
+
+   Happy hacking
+   Olaf
+--
+Olaf Kirch         |  --- o --- Nous sommes du soleil we love when we play
+okir@monad.swb.de  |    / | \   sol.dhoop.naytheet.ah kin.ir.samse.qurax
+             For my PGP public key, finger okir@brewhq.swb.de.
+     _________________________________________________________________
+
+     * Next message: [9]Olaf Kirch: "Re: rpc.lockd/rpc.statd"
+     * Previous message: [10]Paul Christenson: "smail SPAM filter?"
+     * Next  in  thread:  [11]Linus  Torvalds:  "Re: A multi-threaded NFS
+       server for Linux"
+     * Reply:  [12]Linus  Torvalds:  "Re: A multi-threaded NFS server for
+       Linux"
+
+Referenser
+
+   1. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/date.html#18
+   2. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/index.html#18
+   3. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/subject.html#18
+   4. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/author.html#18
+   5. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/0019.html
+   6. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/0017.html
+   7. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/0020.html
+   8. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/0020.html
+   9. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/0019.html
+  10. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/0017.html
+  11. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/0020.html
+  12. http://www.ussg.iu.edu/hypermail/linux/net/9611.3/0020.html
diff --git a/unfs3/doc/passwords.txt b/unfs3/doc/passwords.txt
new file mode 100644 (file)
index 0000000..f7c73d1
--- /dev/null
@@ -0,0 +1,46 @@
+
+unfs3 mount-time password support
+=================================
+
+Example exports file:
+
+/tmp (rw,password=gazonk)
+
+Syntax note: The password may contain any character, except
+whitespace, comma and right parenthesis.
+
+
+Mounting with clear-text passwords
+----------------------------------
+
+mount yourhost:@password:gazonk/tmp /mnt
+
+Note: When using @password, the password cannot contain slashes. 
+
+
+Mounting with one-time passwords
+--------------------------------
+
+1. Communicate with the mount server and issue a mount for
+   "@getnonce". The returned filehandle is your nonce. 
+
+2. Concatenate the nonce with the password, and run it through
+   MD5. Convert the result to a hexascii representation. 
+
+3. Example mount command:
+
+mount yourhost:@otp:851d689b11ed4779dd5fbb084b136c52/tmp /mnt
+
+Step 1 and 2 can be done by using the "nfsotpclient" found in the
+contrib directory.
+
+Note: With otp:s, a race condition exists. Consider the following:
+
+1) client A requests nonce, gets NA
+2) client B requests nonce, gets NB
+3) client A sends otp mount using NA
+4) client B sends otp mount using NB
+
+Step 3 will not succeed because the server only stores the last
+nonce. Step 4 will succeed, though. Client A needs to fetch a new
+nonce and retry the mount.
diff --git a/unfs3/error.c b/unfs3/error.c
new file mode 100644 (file)
index 0000000..920d974
--- /dev/null
@@ -0,0 +1,280 @@
+
+/*
+ * UNFS3 error translation
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+/*
+ * translations from Unix errno to NFS error numbers
+ */
+
+#include "config.h"
+
+#include <rpc/rpc.h>
+#include <errno.h>
+
+#include "nfs.h"
+#include "error.h"
+#include "backend.h"
+
+static int is_stale(void)
+{
+    if (errno == ENOTDIR || errno == ELOOP || errno == ENOENT ||
+       errno == ENAMETOOLONG)
+       return -1;
+    else
+       return 0;
+}
+
+nfsstat3 symlink_err(void)
+{
+    if (errno == EACCES || errno == EPERM)
+       return NFS3ERR_ACCES;
+    else if (is_stale())
+       return NFS3ERR_STALE;
+    else if (errno == EROFS)
+       return NFS3ERR_ROFS;
+    else if (errno == EEXIST)
+       return NFS3ERR_EXIST;
+    else if (errno == ENOSPC)
+       return NFS3ERR_NOSPC;
+#ifdef EDQUOT
+    else if (errno == EDQUOT)
+       return NFS3ERR_DQUOT;
+#endif
+    else if (errno == ENOSYS)
+       return NFS3ERR_NOTSUPP;
+    else if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 mkdir_err(void)
+{
+    return symlink_err();
+}
+
+nfsstat3 mknod_err(void)
+{
+    return symlink_err();
+}
+
+nfsstat3 link_err(void)
+{
+    if (errno == EXDEV)
+       return NFS3ERR_XDEV;
+    else if (errno == EMLINK)
+       return NFS3ERR_MLINK;
+#ifdef EDQUOT
+    else if (errno == EDQUOT)
+       return NFS3ERR_DQUOT;
+#endif
+    else
+       return symlink_err();
+}
+
+nfsstat3 lookup_err(void)
+{
+    if (errno == ENOENT)
+       return NFS3ERR_NOENT;
+#ifdef ENOMEDIUM
+    else if (errno == ENOMEDIUM)
+       return NFS3ERR_NOENT;
+#endif
+    else if (errno == EACCES)
+       return NFS3ERR_ACCES;
+    else if (errno == ENOTDIR || errno == ELOOP || errno == ENAMETOOLONG)
+       return NFS3ERR_STALE;
+    else if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 readlink_err(void)
+{
+    if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else if (errno == EACCES)
+       return NFS3ERR_ACCES;
+    else if (errno == ENOSYS)
+       return NFS3ERR_NOTSUPP;
+    else if (is_stale())
+       return NFS3ERR_STALE;
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 read_err(void)
+{
+    if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else if (is_stale())
+       return NFS3ERR_STALE;
+    else if (errno == EACCES)
+       return NFS3ERR_ACCES;
+    else if (errno == ENXIO || errno == ENODEV)
+       return NFS3ERR_NXIO;
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 write_open_err(void)
+{
+    if (errno == EACCES)
+       return NFS3ERR_ACCES;
+    else if (is_stale())
+       return NFS3ERR_STALE;
+    else if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else if (errno == EROFS)
+       return NFS3ERR_ROFS;
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 write_write_err(void)
+{
+    if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else if (errno == EFBIG)
+       return NFS3ERR_FBIG;
+    else if (errno == ENOSPC)
+       return NFS3ERR_NOSPC;
+#ifdef EDQUOT
+    else if (errno == EDQUOT)
+       return NFS3ERR_DQUOT;
+#endif
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 create_err(void)
+{
+    if (errno == EACCES)
+       return NFS3ERR_ACCES;
+    else if (is_stale())
+       return NFS3ERR_STALE;
+    else if (errno == EROFS)
+       return NFS3ERR_ROFS;
+    else if (errno == ENOSPC)
+       return NFS3ERR_NOSPC;
+    else if (errno == EEXIST)
+       return NFS3ERR_EXIST;
+#ifdef EDQUOT
+    else if (errno == EDQUOT)
+       return NFS3ERR_DQUOT;
+#endif
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 rename_err(void)
+{
+    if (errno == EISDIR)
+       return NFS3ERR_ISDIR;
+    else if (errno == EXDEV)
+       return NFS3ERR_XDEV;
+    else if (errno == EEXIST)
+       return NFS3ERR_EXIST;
+    else if (errno == ENOTEMPTY)
+       return NFS3ERR_NOTEMPTY;
+    else if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else if (errno == ENOTDIR)
+       return NFS3ERR_NOTDIR;
+    else if (errno == EACCES || errno == EPERM)
+       return NFS3ERR_ACCES;
+    else if (errno == ENOENT)
+       return NFS3ERR_NOENT;
+    else if (errno == ELOOP || errno == ENAMETOOLONG)
+       return NFS3ERR_STALE;
+    else if (errno == EROFS)
+       return NFS3ERR_ROFS;
+    else if (errno == ENOSPC)
+       return NFS3ERR_NOSPC;
+#ifdef EDQUOT
+    else if (errno == EDQUOT)
+       return NFS3ERR_DQUOT;
+#endif
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 remove_err(void)
+{
+    if (errno == EACCES || errno == EPERM)
+       return NFS3ERR_ACCES;
+    else if (errno == ENOENT)
+       return ENOENT;
+    else if (errno == ENOTDIR || errno == ELOOP || errno == ENAMETOOLONG)
+       return NFS3ERR_STALE;
+    else if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else if (errno == EROFS)
+       return NFS3ERR_ROFS;
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 rmdir_err(void)
+{
+    if (errno == ENOTEMPTY)
+       return NFS3ERR_NOTEMPTY;
+    else
+       return remove_err();
+}
+
+nfsstat3 setattr_err(void)
+{
+    if (errno == EPERM)
+       return NFS3ERR_PERM;
+    else if (errno == EROFS)
+       return NFS3ERR_ROFS;
+    else if (is_stale())
+       return NFS3ERR_STALE;
+    else if (errno == EACCES)
+       return NFS3ERR_ACCES;
+#ifdef EDQUOT
+    else if (errno == EDQUOT)
+       return NFS3ERR_DQUOT;
+#endif
+    else if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else
+       return NFS3ERR_IO;
+}
+
+nfsstat3 readdir_err(void)
+{
+    if (errno == EPERM)
+       return NFS3ERR_PERM;
+    else if (errno == EACCES)
+       return NFS3ERR_ACCES;
+    else if (errno == ENOTDIR)
+       return NFS3ERR_NOTDIR;
+    else if (is_stale())
+       return NFS3ERR_STALE;
+    else if (errno == EINVAL)
+       return NFS3ERR_INVAL;
+    else
+       return NFS3ERR_IO;
+}
+
+/*
+ * combine two error values
+ */
+nfsstat3 join(nfsstat3 x, nfsstat3 y)
+{
+    return (x != NFS3_OK) ? x : y;
+}
+
+/*
+ * combine three error values
+ */
+nfsstat3 join3(nfsstat3 x, nfsstat3 y, nfsstat3 z)
+{
+    return (x != NFS3_OK) ? x : join(y, z);
+}
diff --git a/unfs3/error.h b/unfs3/error.h
new file mode 100644 (file)
index 0000000..1787a09
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * UNFS3 error translation
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_ERROR_H
+#define UNFS3_ERROR_H
+
+nfsstat3 symlink_err(void);
+nfsstat3 remove_err(void);
+nfsstat3 rmdir_err(void);
+nfsstat3 setattr_err(void);
+nfsstat3 readdir_err(void);
+nfsstat3 mkdir_err(void);
+nfsstat3 mknod_err(void);
+nfsstat3 link_err(void);
+nfsstat3 lookup_err(void);
+nfsstat3 readlink_err(void);
+nfsstat3 read_err(void);
+nfsstat3 write_open_err(void);
+nfsstat3 write_write_err(void);
+nfsstat3 create_err(void);
+nfsstat3 rename_err(void);
+
+nfsstat3 join(nfsstat3, nfsstat3);
+nfsstat3 join3(nfsstat3, nfsstat3, nfsstat3);
+
+#endif
diff --git a/unfs3/fd_cache.c b/unfs3/fd_cache.c
new file mode 100644 (file)
index 0000000..e22b636
--- /dev/null
@@ -0,0 +1,398 @@
+
+/*
+ * UNFS3 file descriptor cache
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <time.h>
+#ifndef WIN32
+#include <syslog.h>
+#include <unistd.h>
+#endif                                /* WIN32 */
+
+#include "nfs.h"
+#include "mount.h"
+#include "fh.h"
+#include "daemon.h"
+#include "Config/exports.h"
+#include "fd_cache.h"
+#include "backend.h"
+
+/*
+ * intention of the file descriptor cache
+ *
+ * for READ operations, the intent is to open() the file on the first
+ * access and to close() it when we hit EOF or after two seconds of
+ * inactivity.
+ *
+ * for WRITE operations, the intent is to open() the file on the first
+ * UNSTABLE access and to close() it when COMMIT is called or after
+ * two seconds of inactivity. 
+ * 
+ * There are three states of an entry:
+ * 1) Unused. use == 0. 
+ * 2) Open fd. use != 0, fd != -1. 
+ * 3) Pending fsync/close error, to be reported in next COMMIT or WRITE. use != 0, fd == -1. 
+ *
+ * Handling fsync/close errors 100% correctly is very difficult for a
+ * user space server. Although rare, fsync/close might fail, for
+ * example when out of quota or closing a file on a NFS file
+ * system. The most correct way of handling these errors would be to
+ * keep track of "dirty" and failed ranges. However, this would
+ * require runtime memory allocation, with no known upper bound, which
+ * in turn can lead to DOS attacks etc. Our solution returns a
+ * fsync/close error in the first WRITE or COMMIT
+ * response. Additionally, the write verifier is changed. Subsequent
+ * COMMITs may succeed even though data has been lost, but since the
+ * verifier is changed, clients will notice this and re-send their
+ * data. Eventually, with some luck, all clients will get an IO error.
+ */
+
+/* number of entries in fd cache */
+#define FD_ENTRIES     256
+
+/* The number of seconds to wait before closing inactive fd */
+#define INACTIVE_TIMEOUT 2
+
+/* The number of seconds to keep pending errors */
+#define PENDING_ERROR_TIMEOUT 7200     /* 2 hours */
+
+typedef struct {
+    int fd;                    /* open file descriptor */
+    int kind;                  /* read or write */
+    time_t use;                        /* last use */
+    uint32 dev;                        /* device */
+    uint64 ino;                        /* inode */
+    uint32 gen;                        /* inode generation */
+} fd_cache_t;
+
+static fd_cache_t fd_cache[FD_ENTRIES];
+
+/* statistics */
+int fd_cache_readers = 0;
+int fd_cache_writers = 0;
+
+/*
+ * initialize the fd cache
+ */
+void fd_cache_init(void)
+{
+    int i;
+
+    for (i = 0; i < FD_ENTRIES; i++) {
+       fd_cache[i].fd = -1;
+       fd_cache[i].kind = UNFS3_FD_READ;
+       fd_cache[i].use = 0;
+       fd_cache[i].dev = 0;
+       fd_cache[i].ino = 0;
+       fd_cache[i].gen = 0;
+    }
+}
+
+/*
+ * find cache index to use for new entry
+ * returns an empty slot if found, else return error
+ */
+static int fd_cache_unused(void)
+{
+    int i;
+    static time_t last_warning = 0;
+
+    for (i = 0; i < FD_ENTRIES; i++) {
+       if (fd_cache[i].use == 0)
+           return i;
+    }
+
+    /* Do not print warning more than once per 10 second */
+    if (time(NULL) > last_warning + 10) {
+       last_warning = time(NULL);
+       logmsg(LOG_INFO,
+              "fd cache full due to more than %d active files or pending IO errors",
+              FD_ENTRIES);
+    }
+
+    return -1;
+}
+
+/*
+
+ * remove an entry from the cache. The keep_on_error variable
+ * indicates if the entry should be kept in the cache upon
+ * fsync/close failures. It should be set to TRUE when fd_cache_del is
+ * called from a code path which cannot report an IO error back to the
+ * client through WRITE or COMMIT. 
+ */
+static int fd_cache_del(int idx, int keep_on_error)
+{
+    int res1, res2;
+
+    res1 = -1;
+
+    if (fd_cache[idx].fd != -1) {
+       if (fd_cache[idx].kind == UNFS3_FD_WRITE) {
+           /* sync file data if writing descriptor */
+           fd_cache_writers--;
+           res1 = backend_fsync(fd_cache[idx].fd);
+       } else {
+           fd_cache_readers--;
+           res1 = 0;
+       }
+       res2 = backend_close(fd_cache[idx].fd);
+       fd_cache[idx].fd = -1;
+
+       /* return -1 if something went wrong during sync or close */
+       if (res1 == -1 || res2 == -1) {
+           res1 = -1;
+       }
+    } else
+       /* pending error */
+       errno = EIO;
+
+    if (res1 == -1 && !keep_on_error) {
+       /* The verifier should not be changed until we actually report &
+          remove the error */
+       regenerate_write_verifier();
+    }
+
+    if (res1 != -1 || !keep_on_error) {
+       fd_cache[idx].fd = -1;
+       fd_cache[idx].use = 0;
+       fd_cache[idx].dev = 0;
+       fd_cache[idx].ino = 0;
+       fd_cache[idx].gen = 0;
+    }
+
+    return res1;
+}
+
+/*
+ * add an entry to the cache
+ */
+static void fd_cache_add(int fd, unfs3_fh_t * ufh, int kind)
+{
+    int idx;
+
+    idx = fd_cache_unused();
+    if (idx != -1) {
+       /* update statistics */
+       if (kind == UNFS3_FD_READ)
+           fd_cache_readers++;
+       else
+           fd_cache_writers++;
+
+       fd_cache[idx].fd = fd;
+       fd_cache[idx].kind = kind;
+       fd_cache[idx].use = time(NULL);
+       fd_cache[idx].dev = ufh->dev;
+       fd_cache[idx].ino = ufh->ino;
+       fd_cache[idx].gen = ufh->gen;
+    }
+}
+
+/*
+ * find entry by operating system fd number
+ */
+static int idx_by_fd(int fd, int kind)
+{
+    int i;
+    int idx = -1;
+
+    for (i = 0; i < FD_ENTRIES; i++)
+       if (fd_cache[i].fd == fd && fd_cache[i].kind == kind) {
+           idx = i;
+           break;
+       }
+    return idx;
+}
+
+/*
+ * find entry by fh (device, inode, and generation number)
+ */
+static int idx_by_fh(unfs3_fh_t * ufh, int kind)
+{
+    int i;
+    int idx = -1;
+
+    for (i = 0; i < FD_ENTRIES; i++)
+       if (fd_cache[i].kind == kind) {
+           if (fd_cache[i].dev == ufh->dev && fd_cache[i].ino == ufh->ino &&
+               fd_cache[i].gen == ufh->gen) {
+               idx = i;
+               break;
+           }
+       }
+    return idx;
+}
+
+/*
+ * open a file descriptor
+ * uses fd from cache if possible
+ */
+int fd_open(const char *path, nfs_fh3 nfh, int kind, int allow_caching)
+{
+    int idx, res, fd;
+    backend_statstruct buf;
+    unfs3_fh_t *fh = (void *) nfh.data.data_val;
+
+    idx = idx_by_fh(fh, kind);
+
+    if (idx != -1) {
+       if (fd_cache[idx].fd == -1) {
+           /* pending error, report to client and remove from cache */
+           fd_cache_del(idx, FALSE);
+           return -1;
+       }
+       return fd_cache[idx].fd;
+    } else {
+       /* call open to obtain new fd */
+       if (kind == UNFS3_FD_READ)
+           fd = backend_open(path, O_RDONLY);
+       else
+           fd = backend_open(path, O_WRONLY);
+       if (fd == -1)
+           return -1;
+
+       /* check for local fs race */
+       res = backend_fstat(fd, &buf);
+       if ((res == -1) ||
+           (fh->dev != buf.st_dev || fh->ino != buf.st_ino ||
+            fh->gen != backend_get_gen(buf, fd, path))) {
+           /* 
+            * local fs changed meaning of path between
+            * calling NFS operation doing fh_decomp and
+            * arriving here
+            *
+            * set errno to ELOOP to make calling NFS
+            * operation return NFS3ERR_STALE
+            */
+           backend_close(fd);
+           errno = ELOOP;
+           return -1;
+       }
+
+       /* 
+        * success, add to cache for later use
+        */
+       if (allow_caching)
+           fd_cache_add(fd, fh, kind);
+       return fd;
+    }
+}
+
+/*
+ * close a file descriptor
+ * returns error number from real close() if applicable
+ */
+int fd_close(int fd, int kind, int really_close)
+{
+    int idx, res1 = 0, res2 = 0;
+
+    idx = idx_by_fd(fd, kind);
+    if (idx != -1) {
+       /* update usage time of cache entry */
+       fd_cache[idx].use = time(NULL);
+
+       if (really_close == FD_CLOSE_REAL)
+           /* delete entry on real close, will close() fd */
+           return fd_cache_del(idx, FALSE);
+       else
+           return 0;
+    } else {
+       /* not in cache, sync and close directly */
+       if (kind == UNFS3_FD_WRITE)
+           res1 = backend_fsync(fd);
+
+       res2 = backend_close(fd);
+
+       if (res1 != 0)
+           return res1;
+       else
+           return res2;
+    }
+}
+
+/*
+ * sync file descriptor data to disk
+ */
+int fd_sync(nfs_fh3 nfh)
+{
+    int idx;
+    unfs3_fh_t *fh = (void *) nfh.data.data_val;
+
+    idx = idx_by_fh(fh, UNFS3_FD_WRITE);
+    if (idx != -1)
+       /* delete entry, will fsync() and close() the fd */
+       return fd_cache_del(idx, FALSE);
+    else
+       return 0;
+}
+
+/*
+ * purge/shutdown the cache
+ */
+void fd_cache_purge(void)
+{
+    int i;
+
+    /* close any open file descriptors we still have */
+    for (i = 0; i < FD_ENTRIES; i++) {
+       if (fd_cache[i].use != 0) {
+           if (fd_cache_del(i, TRUE) == -1)
+               logmsg(LOG_CRIT,
+                      "Error during shutdown fsync/close for dev %lu, inode %lu",
+                      fd_cache[i].dev, fd_cache[i].ino);
+
+       }
+    }
+}
+
+/*
+ * close inactive fds
+ */
+void fd_cache_close_inactive(void)
+{
+    time_t now;
+    int i;
+    int found_error = 0;
+    int active_error = 0;
+
+    now = time(NULL);
+    for (i = 0; i < FD_ENTRIES; i++) {
+       /* Check for inactive open fds */
+       if (fd_cache[i].use && fd_cache[i].fd != -1 &&
+           fd_cache[i].use + INACTIVE_TIMEOUT < now) {
+           fd_cache_del(i, TRUE);
+       }
+
+       /* Check for inactive pending errors */
+       if (fd_cache[i].use && fd_cache[i].fd == -1) {
+           found_error = 1;
+           if (fd_cache[i].use + PENDING_ERROR_TIMEOUT > now)
+               active_error = 1;
+       }
+    }
+
+    if (found_error && !active_error) {
+       /* All pending errors are old. Delete them all from the table and
+          generate new verifier. This is done to prevent the table from
+          filling up with old pending errors, perhaps for files that never
+          will be written again. In this case, we throw away the errors, and 
+          change the server verifier. If clients has pending COMMITs, they
+          will notify the changed verifier and re-send. */
+       for (i = 0; i < FD_ENTRIES; i++) {
+           if (fd_cache[i].use && fd_cache[i].fd == -1) {
+               fd_cache_del(i, FALSE);
+           }
+       }
+       regenerate_write_verifier();
+    }
+}
diff --git a/unfs3/fd_cache.h b/unfs3/fd_cache.h
new file mode 100644 (file)
index 0000000..3812c5d
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * UNFS3 file descriptor cache
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_FD_CACHE_H
+#define UNFS3_FD_CACHE_H
+
+/* FD_READ and FD_WRITE are already defined on Win32 */
+#define UNFS3_FD_READ  0                       /* fd for READ */
+#define UNFS3_FD_WRITE 1                       /* fd for WRITE */
+
+#define FD_CLOSE_VIRT 0                /* virtually close the fd */
+#define FD_CLOSE_REAL 1                /* really close the fd */
+
+/* statistics */
+extern int fd_cache_readers;
+extern int fd_cache_writers;
+
+void fd_cache_init(void);
+
+int fd_open(const char *path, nfs_fh3 fh, int kind, int allow_caching);
+int fd_close(int fd, int kind, int really_close);
+int fd_sync(nfs_fh3 nfh);
+void fd_cache_purge(void);
+void fd_cache_close_inactive(void);
+
+#endif
diff --git a/unfs3/fh.c b/unfs3/fh.c
new file mode 100644 (file)
index 0000000..46c66cd
--- /dev/null
@@ -0,0 +1,473 @@
+
+/*
+ * UNFS3 low-level filehandle routines
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef WIN32
+#include <sys/ioctl.h>
+#include <syslog.h>
+#endif                                /* WIN32 */
+#include <rpc/rpc.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if HAVE_LINUX_EXT2_FS_H == 1
+
+/*
+ * presence of linux/ext2_fs.h is a hint that we are on Linux, really
+ * including that file doesn't work on Debian, so define the ioctl
+ * number here
+ */
+#define EXT2_IOC_GETVERSION    0x80047601
+#endif
+
+#include "nfs.h"
+#include "mount.h"
+#include "daemon.h"
+#include "fh.h"
+#include "backend.h"
+#include "Config/exports.h"
+
+/*
+ * hash function for inode numbers
+ */
+#define FH_HASH(n) ((n ^ (n >> 8) ^ (n >> 16) ^ (n >> 24) ^ (n >> 32) ^ (n >> 40) ^ (n >> 48) ^ (n >> 56)) & 0xFF)
+
+/*
+ * stat cache
+ */
+int st_cache_valid = FALSE;
+backend_statstruct st_cache;
+
+/*
+ * --------------------------------
+ * INODE GENERATION NUMBER HANDLING
+ * --------------------------------
+ */
+
+/*
+ * obtain inode generation number if possible
+ *
+ * obuf: filled out stat buffer (must be given!)
+ * fd:   open fd to file or FD_NONE (-1) if no fd open
+ * path: path to object in case we need to open it here
+ *
+ * returns 0 on failure
+ */
+uint32 get_gen(backend_statstruct obuf, U(int fd), U(const char *path))
+{
+#if HAVE_STRUCT_STAT_ST_GEN == 1
+    return obuf.st_gen;
+#endif
+
+#if HAVE_STRUCT_STAT_ST_GEN == 0 && HAVE_LINUX_EXT2_FS_H == 1
+    int newfd, res;
+    uint32 gen;
+    uid_t euid;
+    gid_t egid;
+
+    if (!S_ISREG(obuf.st_mode) && !S_ISDIR(obuf.st_mode))
+       return 0;
+
+    euid = backend_geteuid();
+    egid = backend_getegid();
+    backend_setegid(0);
+    backend_seteuid(0);
+
+    if (fd != FD_NONE) {
+       res = ioctl(fd, EXT2_IOC_GETVERSION, &gen);
+       if (res == -1)
+           gen = 0;
+    } else {
+       newfd = backend_open(path, O_RDONLY);
+       if (newfd == -1)
+           gen = 0;
+       else {
+           res = ioctl(newfd, EXT2_IOC_GETVERSION, &gen);
+           close(newfd);
+
+           if (res == -1)
+               gen = 0;
+       }
+    }
+
+    backend_setegid(egid);
+    backend_seteuid(euid);
+
+    if (backend_geteuid() != euid || backend_getegid() != egid) {
+       logmsg(LOG_EMERG, "euid/egid switching failed, aborting");
+       daemon_exit(CRISIS);
+    }
+
+    return gen;
+#endif
+
+#if HAVE_STRUCT_STAT_ST_GEN == 0 && HAVE_LINUX_EXT2_FS_H == 0
+    return obuf.st_ino;
+#endif
+}
+
+/*
+ * --------------------------------
+ * FILEHANDLE COMPOSITION FUNCTIONS
+ * --------------------------------
+ */
+
+/*
+ * check whether an NFS filehandle is valid
+ */
+int nfh_valid(nfs_fh3 fh)
+{
+    unfs3_fh_t *obj = (void *) fh.data.data_val;
+
+    /* too small? */
+    if (fh.data.data_len < FH_MINLEN)
+       return FALSE;
+
+    /* encoded length different from real length? */
+    if (fh.data.data_len != fh_length(obj))
+       return FALSE;
+
+    return TRUE;
+}
+
+/*
+ * check whether a filehandle is valid
+ */
+int fh_valid(unfs3_fh_t fh)
+{
+    /* invalid filehandles have zero device and inode */
+    return (int) (fh.dev != 0 || fh.ino != 0);
+}
+
+/*
+ * invalid fh for error returns
+ */
+#ifdef __GNUC__
+static const unfs3_fh_t invalid_fh = {.dev = 0,.ino = 0,.gen = 0,.len =
+       0,.inos = {0}
+};
+#else
+static const unfs3_fh_t invalid_fh = { 0, 0, 0, 0, {0} };
+#endif
+
+/*
+ * compose a filehandle for a given path
+ * path:     path to compose fh for
+ * rqstp:    If not NULL, generate special FHs for removables
+ * need_dir: if not 0, path must point to a directory
+ */
+unfs3_fh_t fh_comp_raw(const char *path, struct svc_req *rqstp, int need_dir)
+{
+    char work[NFS_MAXPATHLEN];
+    unfs3_fh_t fh;
+    backend_statstruct buf;
+    int res;
+    char *last;
+    int pos = 0;
+
+    fh.len = 0;
+
+    /* special case for removable device export point: return preset fsid and 
+       inod 1. */
+    if (rqstp && export_point(path)) {
+       uint32 fsid;
+
+       if (exports_options(path, rqstp, NULL, &fsid) == -1) {
+           /* Shouldn't happen, unless the exports file changed after the
+              call to export_point() */
+           return invalid_fh;
+       }
+       if (exports_opts & OPT_REMOVABLE) {
+           fh.dev = fsid;
+           /* There's a small risk that the file system contains other file
+              objects with st_ino = 1. This should be fairly uncommon,
+              though. The FreeBSD fs(5) man page says:
+
+              "The root inode is the root of the file system.  Inode 0
+              cannot be used for normal purposes and historically bad blocks 
+              were linked to inode 1, thus the root inode is 2 (inode 1 is
+              no longer used for this purpose, however numerous dump tapes
+              make this assumption, so we are stuck with it)."
+
+              In Windows, there's also a small risk that the hash ends up
+              being exactly 1. */
+           fh.ino = 0x1;
+           fh.gen = 0;
+           return fh;
+       }
+    }
+
+    res = backend_lstat(path, &buf);
+    if (res == -1)
+       return invalid_fh;
+
+    /* check for dir if need_dir is set */
+    if (need_dir != 0 && !S_ISDIR(buf.st_mode))
+       return invalid_fh;
+
+    fh.dev = buf.st_dev;
+    fh.ino = buf.st_ino;
+    fh.gen = backend_get_gen(buf, FD_NONE, path);
+
+    /* special case for root directory */
+    if (strcmp(path, "/") == 0)
+       return fh;
+
+    strcpy(work, path);
+    last = work;
+
+    do {
+       *last = '/';
+       last = strchr(last + 1, '/');
+       if (last != NULL)
+           *last = 0;
+
+       res = backend_lstat(work, &buf);
+       if (res == -1) {
+           return invalid_fh;
+       }
+
+       /* store 8 bit hash of the component's inode */
+       fh.inos[pos] = FH_HASH(buf.st_ino);
+       pos++;
+
+    } while (last && pos < FH_MAXLEN);
+
+    if (last)                         /* path too deep for filehandle */
+       return invalid_fh;
+
+    fh.len = pos;
+
+    return fh;
+}
+
+/*
+ * get real length of a filehandle
+ */
+u_int fh_length(const unfs3_fh_t * fh)
+{
+    return fh->len + sizeof(fh->len) + sizeof(fh->dev) + sizeof(fh->ino) +
+       sizeof(fh->gen) + sizeof(fh->pwhash);
+}
+
+/*
+ * extend a filehandle with a given device, inode, and generation number
+ */
+unfs3_fh_t *fh_extend(nfs_fh3 nfh, uint32 dev, uint64 ino, uint32 gen)
+{
+    static unfs3_fh_t new;
+    unfs3_fh_t *fh = (void *) nfh.data.data_val;
+
+    memcpy(&new, fh, fh_length(fh));
+
+    if (new.len == 0) {
+       char *path;
+
+       path = export_point_from_fsid(new.dev, NULL, NULL);
+       if (path != NULL) {
+           /* Our FH to extend refers to a removable device export point,
+              which lacks .inos. We need to construct a real FH to extend,
+              which can be done by passing rqstp=NULL to fh_comp_raw. */
+           new = fh_comp_raw(path, NULL, FH_ANY);
+           if (!fh_valid(new))
+               return NULL;
+       }
+    }
+
+    if (new.len == FH_MAXLEN)
+       return NULL;
+
+    new.dev = dev;
+    new.ino = ino;
+    new.gen = gen;
+    new.pwhash = export_password_hash;
+    new.inos[new.len] = FH_HASH(ino);
+    new.len++;
+
+    return &new;
+}
+
+/*
+ * get post_op_fh3 extended by device, inode, and generation number
+ */
+post_op_fh3 fh_extend_post(nfs_fh3 fh, uint32 dev, uint64 ino, uint32 gen)
+{
+    post_op_fh3 post;
+    unfs3_fh_t *new;
+
+    new = fh_extend(fh, dev, ino, gen);
+
+    if (new) {
+       post.handle_follows = TRUE;
+       post.post_op_fh3_u.handle.data.data_len = fh_length(new);
+       post.post_op_fh3_u.handle.data.data_val = (char *) new;
+    } else
+       post.handle_follows = FALSE;
+
+    return post;
+}
+
+/*
+ * extend a filehandle given a path and needed type
+ */
+post_op_fh3 fh_extend_type(nfs_fh3 fh, const char *path, unsigned int type)
+{
+    post_op_fh3 result;
+    backend_statstruct buf;
+    int res;
+
+    res = backend_lstat(path, &buf);
+    if (res == -1 || (buf.st_mode & type) != type) {
+       st_cache_valid = FALSE;
+       result.handle_follows = FALSE;
+       return result;
+    }
+
+    st_cache_valid = TRUE;
+    st_cache = buf;
+
+    return fh_extend_post(fh, buf.st_dev, buf.st_ino,
+                         backend_get_gen(buf, FD_NONE, path));
+}
+
+/*
+ * -------------------------------
+ * FILEHANDLE RESOLUTION FUNCTIONS
+ * -------------------------------
+ */
+
+/*
+ * filehandles have the following fields:
+ * dev:  device of the file system object fh points to
+ * ino:  inode of the file system object fh points to
+ * gen:  inode generation number, if available
+ * len:  number of entries in following inos array
+ * inos: array of max FH_MAXLEN directories needed to traverse to reach
+ *       object, for each name, an 8 bit hash of the inode number is stored
+ *
+ * - search functions traverse directory structure from the root looking
+ *   for directories matching the inode information stored
+ * - if such a directory is found, we descend into it trying to locate the
+ *   object
+ */
+
+/*
+ * recursive directory search
+ * fh:     filehandle being resolved
+ * pos:    position in filehandles path inode array
+ * lead:   current directory for search
+ * result: where to store path if seach is complete
+ */
+static int fh_rec(const unfs3_fh_t * fh, int pos, const char *lead,
+                 char *result)
+{
+    backend_dirstream *search;
+    struct dirent *entry;
+    backend_statstruct buf;
+    int res, rec;
+    char obj[NFS_MAXPATHLEN];
+
+    /* There's a slight risk of multiple files with the same st_ino on
+       Windows. Take extra care and make sure that there are no collisions */
+    unsigned short matches = 0;
+
+    /* went in too deep? */
+    if (pos == fh->len)
+       return FALSE;
+
+    search = backend_opendir(lead);
+    if (!search)
+       return FALSE;
+
+    entry = backend_readdir(search);
+
+    while (entry) {
+       if (strlen(lead) + strlen(entry->d_name) + 1 < NFS_MAXPATHLEN) {
+
+           sprintf(obj, "%s/%s", lead, entry->d_name);
+
+           res = backend_lstat(obj, &buf);
+           if (res == -1) {
+               buf.st_dev = 0;
+               buf.st_ino = 0;
+           }
+
+           if (buf.st_dev == fh->dev && buf.st_ino == fh->ino) {
+               /* found the object */
+               sprintf(result, "%s/%s", lead + 1, entry->d_name);
+               /* update stat cache */
+               st_cache_valid = TRUE;
+               st_cache = buf;
+               matches++;
+#ifndef WIN32
+               break;
+#endif
+           }
+
+           if (strcmp(entry->d_name, "..") != 0 &&
+               strcmp(entry->d_name, ".") != 0 &&
+               FH_HASH(buf.st_ino) == fh->inos[pos]) {
+               /* 
+                * might be directory we're looking for,
+                * try descending into it
+                */
+               rec = fh_rec(fh, pos + 1, obj, result);
+               if (rec) {
+                   /* object was found in dir */
+                   backend_closedir(search);
+                   return TRUE;
+               }
+           }
+       }
+       entry = backend_readdir(search);
+    }
+
+    backend_closedir(search);
+    switch (matches) {
+       case 0:
+           return FALSE;
+       case 1:
+           return TRUE;
+       default:
+#ifdef WIN32
+           logmsg(LOG_CRIT, "Hash collision detected for file %s!", result);
+#endif
+           return FALSE;
+    }
+}
+
+/*
+ * resolve a filehandle into a path
+ */
+char *fh_decomp_raw(const unfs3_fh_t * fh)
+{
+    int rec = 0;
+    static char result[NFS_MAXPATHLEN];
+
+    /* valid fh? */
+    if (!fh)
+       return NULL;
+
+    /* special case for root directory */
+    if (fh->len == 0)
+       return "/";
+
+    rec = fh_rec(fh, 0, "/", result);
+
+    if (rec)
+       return result;
+
+    /* could not find object */
+    return NULL;
+}
diff --git a/unfs3/fh.h b/unfs3/fh.h
new file mode 100644 (file)
index 0000000..341982f
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * UNFS3 low-level filehandle routines
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_FH_H
+#define UNFS3_FH_H
+
+#include "backend.h"
+
+/* minimum length of complete filehandle */
+#define FH_MINLEN 21
+
+/* maximum depth of pathname described by filehandle */
+#define FH_MAXLEN (64 - FH_MINLEN)
+
+#ifdef __GNUC__
+typedef struct {
+       uint32                  dev;
+       uint64                  ino;
+       uint32                  gen;
+        uint32                  pwhash;
+       unsigned char   len;
+       unsigned char   inos[FH_MAXLEN];
+} __attribute__((packed)) unfs3_fh_t;
+#else
+#pragma pack(1)
+typedef struct {
+       uint32                  dev;
+       uint64                  ino;
+       uint32                  gen;
+        uint32                  pwhash;
+       unsigned char   len;
+       unsigned char   inos[FH_MAXLEN];
+} unfs3_fh_t;
+#pragma pack(4)
+#endif
+
+#define FH_ANY 0
+#define FH_DIR 1
+
+#define FD_NONE (-1)                   /* used for get_gen */
+
+extern int st_cache_valid;             /* stat value is valid */
+extern backend_statstruct st_cache;    /* cached stat value */
+
+uint32 get_gen(backend_statstruct obuf, int fd, const char *path);
+
+int nfh_valid(nfs_fh3 fh);
+int fh_valid(unfs3_fh_t fh);
+
+unfs3_fh_t fh_comp_raw(const char *path, struct svc_req *rqstp, int need_dir);
+u_int fh_length(const unfs3_fh_t *fh);
+
+unfs3_fh_t *fh_extend(nfs_fh3 fh, uint32 dev, uint64 ino, uint32 gen);
+post_op_fh3 fh_extend_post(nfs_fh3 fh, uint32 dev, uint64 ino, uint32 gen);
+post_op_fh3 fh_extend_type(nfs_fh3 fh, const char *path, unsigned int type);
+
+char *fh_decomp_raw(const unfs3_fh_t *fh);
+
+#endif
diff --git a/unfs3/fh_cache.c b/unfs3/fh_cache.c
new file mode 100644 (file)
index 0000000..b411469
--- /dev/null
@@ -0,0 +1,347 @@
+
+/*
+ * UNFS3 filehandle cache
+ * (C) 2004
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "nfs.h"
+#include "fh.h"
+#include "locate.h"
+#include "fh_cache.h"
+#include "mount.h"
+#include "daemon.h"
+#include "Config/exports.h"
+#include "readdir.h"
+#include "backend.h"
+
+/* number of entries in fh cache */
+#define CACHE_ENTRIES  4096
+
+typedef struct {
+    uint32 dev;                        /* device */
+    uint64 ino;                        /* inode */
+    char path[NFS_MAXPATHLEN]; /* pathname */
+    unsigned int use;          /* last use */
+} unfs3_cache_t;
+
+static unfs3_cache_t fh_cache[CACHE_ENTRIES];
+
+/* statistics */
+int fh_cache_max = 0;
+int fh_cache_use = 0;
+int fh_cache_hit = 0;
+
+/* counter for LRU */
+static unsigned int fh_cache_time = 0;
+
+/*
+ * last returned entry
+ *
+ * this entry must not be overwritten before the next lookup, because
+ * operations such as CREATE may still be needing the path inside the
+ * entry for getting directory attributes
+ *
+ * this is needed since fh_cache_time can roll around to 0, thus
+ * making the entry evictable
+ */
+static int fh_last_entry = -1;
+
+/*
+ * return next pseudo-time value for LRU counter
+ */
+static unsigned int fh_cache_next(void)
+{
+    return ++fh_cache_time;
+}
+
+/*
+ * initialize cache
+ */
+void fh_cache_init(void)
+{
+    memset(fh_cache, 0, sizeof(unfs3_cache_t) * CACHE_ENTRIES);
+}
+
+/*
+ * find cache index to use for new entry
+ * returns either an empty slot or the least recently used slot if no
+ * empty slot is present
+ */
+static int fh_cache_lru(void)
+{
+    unsigned int best = UINT_MAX;
+    int best_idx = 0;
+    int i;
+
+    /* if cache is not full, we simply hand out the next slot */
+    if (fh_cache_max < CACHE_ENTRIES - 1)
+       return fh_cache_max++;
+
+    for (i = 0; i < CACHE_ENTRIES; i++) {
+       if (i == fh_last_entry)
+           continue;
+       if (fh_cache[i].use == 0)
+           return i;
+       if (fh_cache[i].use < best) {
+           best = fh_cache[i].use;
+           best_idx = i;
+       }
+    }
+
+    /* avoid stomping over last returned entry */
+    if (best_idx == 0 && fh_last_entry == 0)
+       best_idx = 1;
+
+    return best_idx;
+}
+
+/*
+ * invalidate (clear) a cache entry
+ */
+static void fh_cache_inval(int idx)
+{
+    fh_cache[idx].dev = 0;
+    fh_cache[idx].ino = 0;
+    fh_cache[idx].use = 0;
+    fh_cache[idx].path[0] = 0;
+}
+
+/*
+ * find index given device and inode number
+ */
+static int fh_cache_index(uint32 dev, uint64 ino)
+{
+    int i, res = -1;
+
+    for (i = 0; i < fh_cache_max + 1; i++)
+       if (fh_cache[i].dev == dev && fh_cache[i].ino == ino) {
+           res = i;
+           break;
+       }
+
+    return res;
+}
+
+/*
+ * add an entry to the filehandle cache
+ */
+char *fh_cache_add(uint32 dev, uint64 ino, const char *path)
+{
+    int idx;
+
+    /* if we already have a matching entry, overwrite that */
+    idx = fh_cache_index(dev, ino);
+
+    /* otherwise overwrite least recently used entry */
+    if (idx == -1)
+       idx = fh_cache_lru();
+
+    fh_cache[idx].dev = dev;
+    fh_cache[idx].ino = ino;
+    fh_cache[idx].use = fh_cache_next();
+
+    strcpy(fh_cache[idx].path, path);
+
+    return fh_cache[idx].path;
+}
+
+/*
+ * lookup an entry in the cache given a device, inode, and generation number
+ */
+static char *fh_cache_lookup(uint32 dev, uint64 ino)
+{
+    int i, res;
+    backend_statstruct buf;
+
+    i = fh_cache_index(dev, ino);
+
+    if (i != -1) {
+       /* check whether path to <dev,ino> relation still holds */
+       res = backend_lstat(fh_cache[i].path, &buf);
+       if (res == -1) {
+           /* object does not exist any more */
+           fh_cache_inval(i);
+           return NULL;
+       }
+       if (buf.st_dev == dev && buf.st_ino == ino) {
+           /* cache hit, update time on cache entry */
+           fh_cache[i].use = fh_cache_next();
+
+           /* update stat cache */
+           st_cache_valid = TRUE;
+           st_cache = buf;
+
+           /* prevent next fh_cache_add from overwriting entry */
+           fh_last_entry = i;
+
+           return fh_cache[i].path;
+       } else {
+           /* path to <dev,ino> relation has changed */
+           fh_cache_inval(i);
+           return NULL;
+       }
+    }
+
+    return NULL;
+}
+
+/*
+ * resolve a filename into a path
+ * cache-using wrapper for fh_decomp_raw
+ */
+char *fh_decomp(nfs_fh3 fh)
+{
+    char *result;
+    unfs3_fh_t *obj = (void *) fh.data.data_val;
+    time_t *last_mtime;
+    uint32 *dir_hash, new_dir_hash;
+
+    if (!nfh_valid(fh)) {
+       st_cache_valid = FALSE;
+       return NULL;
+    }
+
+    /* Does the fsid match some static fsid? */
+    if ((result =
+        export_point_from_fsid(obj->dev, &last_mtime, &dir_hash)) != NULL) {
+       if (obj->ino == 0x1) {
+           /* This FH refers to the export point itself */
+           /* Need to fill stat cache */
+           st_cache_valid = TRUE;
+
+           if (backend_lstat(result, &st_cache) == -1) {
+               /* export point does not exist. This probably means that we
+                  are using autofs and no media is inserted. Fill stat cache 
+                  with dummy information */
+               st_cache.st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
+               st_cache.st_nlink = 2;
+               st_cache.st_uid = 0;
+               st_cache.st_gid = 0;
+               st_cache.st_rdev = 0;
+               st_cache.st_size = 4096;
+               st_cache.st_blksize = 512;
+               st_cache.st_blocks = 8;
+           } else {
+               /* Stat was OK, but make sure the values are sane. Supermount 
+                  returns insane values when no media is inserted, for
+                  example. */
+               if (st_cache.st_nlink == 0)
+                   st_cache.st_nlink = 1;
+               if (st_cache.st_size == 0)
+                   st_cache.st_size = 4096;
+               if (st_cache.st_blksize == 0)
+                   st_cache.st_blksize = 512;
+               if (st_cache.st_blocks == 0)
+                   st_cache.st_blocks = 8;
+           }
+
+           st_cache.st_dev = obj->dev;
+           st_cache.st_ino = 0x1;
+
+           /* It's very important that we get mtime correct, since it's used 
+              as verifier in READDIR. The generation of mtime is tricky,
+              because with some filesystems, such as the Linux 2.4 FAT fs,
+              the mtime value for the mount point is set to *zero* on each
+              mount. I consider this a bug, but we need to work around it
+              anyway.
+
+              We store the last mtime returned. When stat returns a smaller
+              value than this, we double-check by doing a hash of the names
+              in the directory. If this hash is different from what we had
+              earlier, return current time.
+
+              Note: Since dir_hash is stored in memory, we have introduced a 
+              little statefulness here. This means that if unfsd is
+              restarted during two READDIR calls, NFS3ERR_BAD_COOKIE will be 
+              returned, and the client has to retry the READDIR operation
+              with a zero cookie */
+
+           if (st_cache.st_mtime > *last_mtime) {
+               /* stat says our directory has changed */
+               *last_mtime = st_cache.st_mtime;
+           } else if (*dir_hash != (new_dir_hash = directory_hash(result))) {
+               /* The names in the directory has changed. Return current
+                  time. */
+               st_cache.st_mtime = time(NULL);
+               *last_mtime = st_cache.st_mtime;
+               *dir_hash = new_dir_hash;
+           } else {
+               /* Hash unchanged. Returned stored mtime. */
+               st_cache.st_mtime = *last_mtime;
+           }
+
+           return result;
+       }
+    }
+
+    /* try lookup in cache, increase cache usage counter */
+    result = fh_cache_lookup(obj->dev, obj->ino);
+    fh_cache_use++;
+
+    if (!result) {
+       /* not found, resolve the hard way */
+       result = fh_decomp_raw(obj);
+
+       /* if still not found, do full recursive search) */
+       if (!result)
+           result = backend_locate_file(obj->dev, obj->ino);
+
+       if (result)
+           /* add to cache for later use if resolution ok */
+           result = fh_cache_add(obj->dev, obj->ino, result);
+       else
+           /* could not resolve in any way */
+           st_cache_valid = FALSE;
+    } else
+       /* found, update cache hit statistic */
+       fh_cache_hit++;
+
+    return result;
+}
+
+/*
+ * compose a filehandle for a path
+ * cache-using wrapper for fh_comp_raw
+ * exports_options must be called before
+ */
+unfs3_fh_t fh_comp(const char *path, struct svc_req * rqstp, int need_dir)
+{
+    unfs3_fh_t res;
+
+    res = fh_comp_raw(path, rqstp, need_dir);
+    if (fh_valid(res))
+       /* add to cache for later use */
+       fh_cache_add(res.dev, res.ino, path);
+
+    res.pwhash = export_password_hash;
+    return res;
+}
+
+/*
+ * return pointer to composed filehandle
+ * wrapper for fh_comp
+ */
+unfs3_fh_t *fh_comp_ptr(const char *path, struct svc_req * rqstp,
+                       int need_dir)
+{
+    static unfs3_fh_t res;
+
+    res = fh_comp(path, rqstp, need_dir);
+    if (fh_valid(res))
+       return &res;
+    else
+       return NULL;
+}
diff --git a/unfs3/fh_cache.h b/unfs3/fh_cache.h
new file mode 100644 (file)
index 0000000..5801709
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * UNFS3 filehandle cache
+ * (C) 2003, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_FH_CACHE_H
+#define UNFS3_FH_CACHE_H
+
+/* statistics */
+extern int fh_cache_max;
+extern int fh_cache_use;
+extern int fh_cache_hit;
+
+void fh_cache_init(void);
+
+char *fh_decomp(nfs_fh3 fh);
+unfs3_fh_t fh_comp(const char *path, struct svc_req *rqstp, int need_dir);
+unfs3_fh_t *fh_comp_ptr(const char *path, struct svc_req *rqstp, int need_dir);
+
+char *fh_cache_add(uint32 dev, uint64 ino, const char *path);
+
+#endif
diff --git a/unfs3/indent-all b/unfs3/indent-all
new file mode 100755 (executable)
index 0000000..5ba6bb7
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+indent -bad -bap -bbb -bli4 -br -brs -c40 -cbi4 -c40 -cdw -ce -ci4 -cli4 \
+       -cp40 -cs -d0 -di0 -fca -i4 -l78 -lc78 -lp -nbbo -nbc -nbfda -nhnl \
+       -npcs -nprs -npsl -pi0 -saf -sai -saw -sob -ss -ts8 -ut \
+       *.c Extras/*.c
diff --git a/unfs3/install-sh b/unfs3/install-sh
new file mode 100755 (executable)
index 0000000..e9de238
--- /dev/null
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+               chmodcmd=""
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/unfs3/locate.c b/unfs3/locate.c
new file mode 100644 (file)
index 0000000..c5ed219
--- /dev/null
@@ -0,0 +1,171 @@
+
+/*
+ * UNFS3 brute force file search
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if HAVE_MNTENT_H == 1
+#include <mntent.h>
+#endif
+
+#if HAVE_SYS_MNTTAB_H == 1
+#include <sys/mnttab.h>
+#endif
+
+#include "nfs.h"
+#include "fh.h"
+#include "daemon.h"
+
+/*
+ * these are the brute-force file searching routines that are used
+ * when both the filehandle cache and the hashed path inside the
+ * filehandle are unable to locate the file
+ *
+ * this can only happen if a file was rename(3)'d across directories
+ *
+ * these routines are slow, but better than returning ESTALE to
+ * clients
+ */
+
+#if HAVE_MNTENT_H == 1 || HAVE_SYS_MNTTAB_H == 1
+
+/*
+ * locate file given prefix, device, and inode number
+ */
+static int locate_pfx(const char *pfx, uint32 dev, uint64 ino, char *result)
+{
+    char path[NFS_MAXPATHLEN];
+    backend_dirstream *search;
+    struct dirent *ent;
+    struct stat buf;
+    int res;
+
+    search = opendir(pfx);
+    if (!search)
+       return FALSE;
+
+    while ((ent = readdir(search))) {
+       if (strlen(pfx) + strlen(ent->d_name) + 2 >= NFS_MAXPATHLEN)
+           continue;
+
+       sprintf(path, "%s/%s", pfx, ent->d_name);
+
+       res = lstat(path, &buf);
+       if (res != 0)
+           continue;
+
+       /* check for matching object */
+       if (buf.st_dev == dev && buf.st_ino == ino) {
+           strcpy(result, path);
+           st_cache = buf;
+           st_cache_valid = TRUE;
+           closedir(search);
+           return TRUE;
+       }
+
+       /* descend into directories with same dev */
+       if (buf.st_dev == dev && S_ISDIR(buf.st_mode) &&
+           strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0) {
+           res = locate_pfx(path, dev, ino, result);
+           if (res == TRUE) {
+               closedir(search);
+               return TRUE;
+           }
+       }
+    }
+
+    closedir(search);
+    return FALSE;
+}
+#endif
+
+/*
+ * locate file given device and inode number
+ *
+ * slow fallback in case other filehandle resolution functions fail
+ */
+char *locate_file(U(uint32 dev), U(uint64 ino))
+{
+#if HAVE_MNTENT_H == 1 || HAVE_SYS_MNTTAB_H == 1
+    static char path[NFS_MAXPATHLEN];
+    FILE *mtab;
+    struct stat buf;
+    int res;
+#endif
+
+#if HAVE_MNTENT_H == 1
+    struct mntent *ent;
+#endif
+
+#if HAVE_SYS_MNTTAB_H == 1
+    struct mnttab ent;
+    int found = FALSE;
+#endif
+
+    if (!opt_brute_force)
+       return NULL;
+
+#if HAVE_MNTENT_H == 1
+    mtab = setmntent("/etc/mtab", "r");
+    if (!mtab)
+       return NULL;
+
+    /* 
+     * look for mtab entry with matching device
+     */
+    while ((ent = getmntent(mtab))) {
+       res = lstat(ent->mnt_dir, &buf);
+
+       if (res == 0 && buf.st_dev == dev)
+           break;
+    }
+    endmntent(mtab);
+
+    /* found matching entry? */
+    if (ent) {
+       res = locate_pfx(ent->mnt_dir, dev, ino, path);
+       if (res == TRUE)
+           return path;
+    }
+#endif
+
+#if HAVE_SYS_MNTTAB_H == 1
+    mtab = fopen("/etc/mnttab", "r");
+    if (!mtab)
+       return NULL;
+
+    /* 
+     * look for mnttab entry with matching device
+     */
+    while (getmntent(mtab, &ent) == 0) {
+       res = lstat(ent.mnt_mountp, &buf);
+
+       if (res == 0 && buf.st_dev == dev) {
+           found = TRUE;
+           break;
+       }
+    }
+    fclose(mtab);
+
+    /* found matching entry? */
+    if (found) {
+       res = locate_pfx(ent.mnt_mountp, dev, ino, path);
+       if (res == TRUE)
+           return path;
+    }
+#endif
+
+    return NULL;
+}
diff --git a/unfs3/locate.h b/unfs3/locate.h
new file mode 100644 (file)
index 0000000..45ac305
--- /dev/null
@@ -0,0 +1,13 @@
+
+/*
+ * UNFS3 brute force file search
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_LOCATE_H
+#define UNFS3_LOCATE_H
+
+char *locate_file(uint32 dev, uint64 ino);
+
+#endif
diff --git a/unfs3/md5.c b/unfs3/md5.c
new file mode 100644 (file)
index 0000000..7a7c7a5
--- /dev/null
@@ -0,0 +1,379 @@
+
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+       http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+       either statically or dynamically; added missing #include <string.h>
+       in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+       type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+       unsigned in ANSI C, signed in traditional"; made test program
+       self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER                     /* 1 = big-endian, -1 = little-endian, 
+                                         0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#  define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+static void md5_process(md5_state_t * pms, const md5_byte_t * data     /* [64] 
+                                                                        */ )
+{
+    md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d =
+       pms->abcd[3];
+    md5_word_t t;
+
+#if BYTE_ORDER > 0
+    /* Define storage only for big-endian CPUs. */
+    md5_word_t X[16];
+#else
+    /* Define storage for little-endian or both types of CPUs. */
+    md5_word_t xbuf[16];
+    const md5_word_t *X;
+#endif
+
+    {
+#if BYTE_ORDER == 0
+       /* 
+        * Determine dynamically whether this is a big-endian or
+        * little-endian machine, since we can use a more efficient
+        * algorithm on the latter.
+        */
+       static const int w = 1;
+
+       if (*((const md5_byte_t *) &w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0                   /* little-endian */
+       {
+           /* 
+            * On little-endian machines, we can process properly aligned
+            * data without copying it.
+            */
+           if (!((data - (const md5_byte_t *) 0) & 3)) {
+               /* data are properly aligned */
+               X = (const md5_word_t *) data;
+           } else {
+               /* not aligned */
+               memcpy(xbuf, data, 64);
+               X = xbuf;
+           }
+       }
+#endif
+#if BYTE_ORDER == 0
+       else                           /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0                   /* big-endian */
+       {
+           /* 
+            * On big-endian machines, we must arrange the bytes in the
+            * right order.
+            */
+           const md5_byte_t *xp = data;
+           int i;
+
+#  if BYTE_ORDER == 0
+           X = xbuf;                  /* (dynamic only) */
+#  else
+#    define xbuf X                    /* (static only) */
+#  endif
+           for (i = 0; i < 16; ++i, xp += 4)
+               xbuf[i] =
+                   xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+       }
+#endif
+    }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] +
+       T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d, 0, 7, T1);
+    SET(d, a, b, c, 1, 12, T2);
+    SET(c, d, a, b, 2, 17, T3);
+    SET(b, c, d, a, 3, 22, T4);
+    SET(a, b, c, d, 4, 7, T5);
+    SET(d, a, b, c, 5, 12, T6);
+    SET(c, d, a, b, 6, 17, T7);
+    SET(b, c, d, a, 7, 22, T8);
+    SET(a, b, c, d, 8, 7, T9);
+    SET(d, a, b, c, 9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12, 7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+    /* Round 2. */
+    /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] +
+       T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d, 1, 5, T17);
+    SET(d, a, b, c, 6, 9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a, 0, 20, T20);
+    SET(a, b, c, d, 5, 5, T21);
+    SET(d, a, b, c, 10, 9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a, 4, 20, T24);
+    SET(a, b, c, d, 9, 5, T25);
+    SET(d, a, b, c, 14, 9, T26);
+    SET(c, d, a, b, 3, 14, T27);
+    SET(b, c, d, a, 8, 20, T28);
+    SET(a, b, c, d, 13, 5, T29);
+    SET(d, a, b, c, 2, 9, T30);
+    SET(c, d, a, b, 7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+    /* Round 3. */
+    /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] +
+       T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d, 5, 4, T33);
+    SET(d, a, b, c, 8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d, 1, 4, T37);
+    SET(d, a, b, c, 4, 11, T38);
+    SET(c, d, a, b, 7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13, 4, T41);
+    SET(d, a, b, c, 0, 11, T42);
+    SET(c, d, a, b, 3, 16, T43);
+    SET(b, c, d, a, 6, 23, T44);
+    SET(a, b, c, d, 9, 4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+    /* Round 4. */
+    /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] +
+       T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d, 0, 6, T49);
+    SET(d, a, b, c, 7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a, 5, 21, T52);
+    SET(a, b, c, d, 12, 6, T53);
+    SET(d, a, b, c, 3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a, 1, 21, T56);
+    SET(a, b, c, d, 8, 6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b, 6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d, 4, 6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b, 2, 15, T63);
+    SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+    /* Then perform the following additions. (That is increment each of the
+       four registers by the value it had before this block was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void md5_init(md5_state_t * pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /* 0xefcdab89 */ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /* 0x98badcfe */ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void md5_append(md5_state_t * pms, const md5_byte_t * data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t) (nbytes << 3);
+
+    if (nbytes <= 0)
+       return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+       pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+       int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+       memcpy(pms->buf + offset, p, copy);
+       if (offset + copy < 64)
+           return;
+       p += copy;
+       left -= copy;
+       md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+       md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+       memcpy(pms->buf, p, left);
+}
+
+void md5_finish(md5_state_t * pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+       0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+       data[i] = (md5_byte_t) (pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+       digest[i] = (md5_byte_t) (pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/unfs3/md5.h b/unfs3/md5.h
new file mode 100644 (file)
index 0000000..bc49597
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+       http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+       references to Ghostscript; clarified derivation from RFC 1321;
+       now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+       added conditionalization for C++ compilation from Martin
+       Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];       /* message length in bits, lsw first */
+    md5_word_t abcd[4];                /* digest buffer */
+    md5_byte_t buf[64];                /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/unfs3/mount.c b/unfs3/mount.c
new file mode 100644 (file)
index 0000000..d14956e
--- /dev/null
@@ -0,0 +1,289 @@
+
+/*
+ * UNFS3 mount protocol procedures
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef WIN32
+#include <syslog.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif                                /* WIN32 */
+#include <fcntl.h>
+
+#include "nfs.h"
+#include "mount.h"
+#include "daemon.h"
+#include "fh.h"
+#include "fh_cache.h"
+#include "fd_cache.h"
+#include "Config/exports.h"
+#include "password.h"
+#include "backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX      4096
+#endif
+
+#define IS_SECURE(port) ((port) < 1024)
+
+/*
+ * number of active mounts
+ *
+ * only a guess since clients can crash and/or not sent UMNT calls
+ */
+static int mount_cnt = 0;
+
+/* list of currently mounted directories */
+static mountlist mount_list = NULL;
+
+static char nonce[32] = "";
+
+/*
+ * add entry to mount list
+ */
+static void add_mount(const char *path, struct svc_req *rqstp)
+{
+    mountlist new;
+    mountlist iter;
+    char *host;
+
+    new = malloc(sizeof(struct mountbody));
+    if (!new) {
+       logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
+       return;
+    }
+
+    host = inet_ntoa(get_remote(rqstp));
+    new->ml_hostname = malloc(strlen(host) + 1);
+    if (!new->ml_hostname) {
+       logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
+       free(new);
+       return;
+    }
+
+    new->ml_directory = malloc(strlen(path) + 1);
+    if (!new->ml_directory) {
+       logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
+       free(new->ml_hostname);
+       free(new);
+       return;
+    }
+
+    /* initialize the new entry */
+    new->ml_next = NULL;
+    strcpy(new->ml_hostname, host);
+    strcpy(new->ml_directory, path);
+
+    iter = mount_list;
+    if (iter) {
+       while (iter->ml_next)
+           iter = iter->ml_next;
+       iter->ml_next = new;
+    } else
+       mount_list = new;
+
+    mount_cnt++;
+}
+
+/*
+ * remove entries from mount list
+ */
+static void remove_mount(const char *path, struct svc_req *rqstp)
+{
+    mountlist iter, next, prev = NULL;
+    char *host;
+
+    host = inet_ntoa(get_remote(rqstp));
+
+    iter = mount_list;
+    while (iter) {
+       if (strcmp(iter->ml_hostname, host) == 0 &&
+           (!path || strcmp(iter->ml_directory, path) == 0)) {
+           if (prev)
+               prev->ml_next = iter->ml_next;
+           else
+               mount_list = iter->ml_next;
+
+           next = iter->ml_next;
+
+           free(iter->ml_hostname);
+           free(iter->ml_directory);
+           free(iter);
+
+           iter = next;
+
+           /* adjust mount count */
+           if (mount_cnt > 0)
+               mount_cnt--;
+       } else {
+           prev = iter;
+           iter = iter->ml_next;
+       }
+    }
+}
+
+void *mountproc_null_3_svc(U(void *argp), U(struct svc_req *rqstp))
+{
+    static void *result = NULL;
+
+    return &result;
+}
+
+mountres3 *mountproc_mnt_3_svc(dirpath * argp, struct svc_req * rqstp)
+{
+    char buf[PATH_MAX];
+    static unfs3_fh_t fh;
+    static mountres3 result;
+    static int auth = AUTH_UNIX;
+    int authenticated = 0;
+    char *password;
+
+    /* We need to modify the *argp pointer. Make a copy. */
+    char *dpath = *argp;
+
+    /* error out if not version 3 */
+    if (rqstp->rq_vers != 3) {
+       logmsg(LOG_INFO,
+              "%s attempted mount with unsupported protocol version",
+              inet_ntoa(get_remote(rqstp)));
+       result.fhs_status = MNT3ERR_INVAL;
+       return &result;
+    }
+
+    /* Check for "mount commands" */
+    if (strncmp(dpath, "@getnonce", sizeof("@getnonce") - 1) == 0) {
+       if (backend_gen_nonce(nonce) < 0) {
+           result.fhs_status = MNT3ERR_IO;
+       } else {
+           result.fhs_status = MNT3_OK;
+           result.mountres3_u.mountinfo.fhandle.fhandle3_len = 32;
+           result.mountres3_u.mountinfo.fhandle.fhandle3_val = nonce;
+           result.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1;
+           result.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
+               &auth;
+       }
+       return &result;
+    } else if (strncmp(dpath, "@password:", sizeof("@password:") - 1) == 0) {
+       char pw[PASSWORD_MAXLEN + 1];
+
+       mnt_cmd_argument(&dpath, "@password:", pw, PASSWORD_MAXLEN);
+       if (exports_options(dpath, rqstp, &password, NULL) != -1) {
+           authenticated = !strcmp(password, pw);
+       }
+       /* else leave authenticated unchanged */
+    } else if (strncmp(dpath, "@otp:", sizeof("@otp:") - 1) == 0) {
+       /* The otp from the client */
+       char otp[PASSWORD_MAXLEN + 1];
+
+       /* Our calculated otp */
+       char hexdigest[32];
+
+       mnt_cmd_argument(&dpath, "@otp:", otp, PASSWORD_MAXLEN);
+       if (exports_options(dpath, rqstp, &password, NULL) != -1) {
+           otp_digest(nonce, password, hexdigest);
+
+           /* Compare our calculated digest with what the client submitted */
+           authenticated = !strncmp(hexdigest, otp, 32);
+
+           /* Change nonce */
+           backend_gen_nonce(nonce);
+       }
+       /* else leave authenticated unchanged */
+    }
+
+    if ((exports_opts & OPT_REMOVABLE) && export_point(dpath)) {
+       /* Removable media export point. Do not call realpath; simply copy
+          path */
+       strncpy(buf, dpath, PATH_MAX);
+    } else if (!backend_realpath(dpath, buf)) {
+       /* the given path does not exist */
+       result.fhs_status = MNT3ERR_NOENT;
+       return &result;
+    }
+
+    if (strlen(buf) + 1 > NFS_MAXPATHLEN) {
+       logmsg(LOG_INFO, "%s attempted to mount jumbo path",
+              inet_ntoa(get_remote(rqstp)));
+       result.fhs_status = MNT3ERR_NAMETOOLONG;
+       return &result;
+    }
+
+    if ((exports_options(buf, rqstp, &password, NULL) == -1)
+       || (!authenticated && password[0])
+       || (!(exports_opts & OPT_INSECURE) &&
+           !IS_SECURE(ntohs(get_port(rqstp))))
+       ) {
+       /* not exported to this host or at all, or a password defined and not 
+          authenticated */
+       result.fhs_status = MNT3ERR_ACCES;
+       return &result;
+    }
+
+    fh = fh_comp(buf, rqstp, FH_DIR);
+
+    if (!fh_valid(fh)) {
+       logmsg(LOG_INFO, "%s attempted to mount non-directory",
+              inet_ntoa(get_remote(rqstp)));
+       result.fhs_status = MNT3ERR_NOTDIR;
+       return &result;
+    }
+
+    add_mount(dpath, rqstp);
+
+    result.fhs_status = MNT3_OK;
+    result.mountres3_u.mountinfo.fhandle.fhandle3_len = fh_length(&fh);
+    result.mountres3_u.mountinfo.fhandle.fhandle3_val = (char *) &fh;
+    result.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1;
+    result.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = &auth;
+
+    return &result;
+}
+
+mountlist *mountproc_dump_3_svc(U(void *argp), U(struct svc_req *rqstp))
+{
+    return &mount_list;
+}
+
+void *mountproc_umnt_3_svc(dirpath * argp, struct svc_req *rqstp)
+{
+    /* RPC times out if we use a NULL pointer */
+    static void *result = NULL;
+
+    remove_mount(*argp, rqstp);
+
+    /* if no more mounts are active, flush all open file descriptors */
+    if (mount_cnt == 0)
+       fd_cache_purge();
+
+    return &result;
+}
+
+void *mountproc_umntall_3_svc(U(void *argp), struct svc_req *rqstp)
+{
+    /* RPC times out if we use a NULL pointer */
+    static void *result = NULL;
+
+    remove_mount(NULL, rqstp);
+
+    /* if no more mounts are active, flush all open file descriptors */
+    if (mount_cnt == 0)
+       fd_cache_purge();
+
+    return &result;
+}
+
+exports *mountproc_export_3_svc(U(void *argp), U(struct svc_req *rqstp))
+{
+    return &exports_nfslist;
+}
diff --git a/unfs3/mount.h b/unfs3/mount.h
new file mode 100644 (file)
index 0000000..94327f3
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * UNFS3 mount protocol definitions
+ * Generated by rpcgen
+ */
+
+#ifndef _MOUNT_H_RPCGEN
+#define _MOUNT_H_RPCGEN
+
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+#define FHSIZE3 64
+
+typedef struct {
+       u_int fhandle3_len;
+       char *fhandle3_val;
+} fhandle3;
+
+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;
+
+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;
+
+typedef char *dirpath;
+
+typedef char *name;
+
+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;
+
+#define MOUNTPROG 100005
+#define MOUNTVERS1 1
+#define MOUNTVERS3 3
+
+#define MOUNTPROC_NULL 0
+extern  void * mountproc_null_3_svc(void *, struct svc_req *);
+#define MOUNTPROC_MNT 1
+extern  mountres3 * mountproc_mnt_3_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_DUMP 2
+extern  mountlist * mountproc_dump_3_svc(void *, struct svc_req *);
+#define MOUNTPROC_UMNT 3
+extern  void * mountproc_umnt_3_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_UMNTALL 4
+extern  void * mountproc_umntall_3_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORT 5
+extern  exports * mountproc_export_3_svc(void *, struct svc_req *);
+extern int mountprog_3_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#endif /* !_MOUNT_H_RPCGEN */
diff --git a/unfs3/nfs.c b/unfs3/nfs.c
new file mode 100644 (file)
index 0000000..5d99bf8
--- /dev/null
@@ -0,0 +1,1083 @@
+
+/*
+ * UNFS3 NFS protocol procedures
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>                /* needed for statfs() on NetBSD */
+#if HAVE_SYS_MOUNT_H == 1
+#include <sys/mount.h>                /* dito */
+#endif
+#if HAVE_SYS_VMOUNT_H == 1
+#include <sys/vmount.h>                       /* AIX */
+#endif
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utime.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif                                /* WIN32 */
+
+#if HAVE_STATVFS == 1
+# include <sys/statvfs.h>
+#else
+# define statvfs statfs
+#endif
+
+#include "nfs.h"
+#include "mount.h"
+#include "fh.h"
+#include "fh_cache.h"
+#include "attr.h"
+#include "readdir.h"
+#include "user.h"
+#include "error.h"
+#include "fd_cache.h"
+#include "daemon.h"
+#include "backend.h"
+#include "Config/exports.h"
+#include "Extras/cluster.h"
+
+/*
+ * decompose filehandle and switch user if permitted access
+ * otherwise zero result structure and return with error status
+ */
+#define PREP(p,f) do {                                         \
+                      unfs3_fh_t *fh = (void *)f.data.data_val; \
+                      switch_to_root();                                \
+                      p = fh_decomp(f);                                \
+                      if (exports_options(p, rqstp, NULL, NULL) == -1) { \
+                          memset(&result, 0, sizeof(result));  \
+                          if (p)                               \
+                              result.status = NFS3ERR_ACCES;   \
+                          else                                 \
+                              result.status = NFS3ERR_STALE;   \
+                          return &result;                      \
+                      }                                                \
+                      if (fh->pwhash != export_password_hash) { \
+                          memset(&result, 0, sizeof(result));  \
+                          result.status = NFS3ERR_STALE;        \
+                          return &result;                       \
+                      }                                         \
+                      switch_user(rqstp);                      \
+                  } while (0)
+
+/*
+ * cat an object name onto a path, checking for illegal input
+ */
+nfsstat3 cat_name(const char *path, const char *name, char *result)
+{
+    char *last;
+
+    if (!path)
+       return NFS3ERR_STALE;
+
+    if (!name)
+       return NFS3ERR_ACCES;
+
+    if (name[0] == 0 || strchr(name, '/') != NULL)
+       return NFS3ERR_ACCES;
+
+    if (strlen(path) + strlen(name) + 2 > NFS_MAXPATHLEN)
+       return NFS3ERR_NAMETOOLONG;
+
+    if (strcmp(name, ".") == 0) {
+       strcpy(result, path);
+       return NFS3_OK;
+    }
+
+    /* 
+     * Irix clients do lookups for .. and then use the
+     * resulting filehandle for more lookups, causing them
+     * to get filehandles that fh_decomp_raw will refuse to
+     * resolve. Export list handling will also get very
+     * confused if we allow such filehandles.
+     */
+    if (strcmp(name, "..") == 0) {
+       last = strrchr(path, '/');
+       if (!last || last == path)
+           strcpy(result, "/");
+       else {
+           *last = 0;
+           strcpy(result, path);
+           *last = '/';
+       }
+       return NFS3_OK;
+    }
+
+    sprintf(result, "%s/%s", path, name);
+    return NFS3_OK;
+}
+
+void *nfsproc3_null_3_svc(U(void *argp), U(struct svc_req *rqstp))
+{
+    static void *result = NULL;
+
+    return &result;
+}
+
+GETATTR3res *nfsproc3_getattr_3_svc(GETATTR3args * argp,
+                                   struct svc_req * rqstp)
+{
+    static GETATTR3res result;
+    char *path;
+    post_op_attr post;
+
+    PREP(path, argp->object);
+    post = get_post_cached(rqstp);
+
+    result.status = NFS3_OK;
+    result.GETATTR3res_u.resok.obj_attributes =
+       post.post_op_attr_u.attributes;
+
+    return &result;
+}
+
+/*
+ * check ctime guard for SETATTR procedure
+ */
+static nfsstat3 in_sync(sattrguard3 guard, pre_op_attr pre)
+{
+    if (!pre.attributes_follow)
+       return NFS3ERR_STALE;
+
+    if (!guard.check)
+       return NFS3_OK;
+
+    if (guard.sattrguard3_u.obj_ctime.seconds !=
+       pre.pre_op_attr_u.attributes.ctime.seconds)
+       return NFS3ERR_NOT_SYNC;
+
+    return NFS3_OK;
+}
+
+SETATTR3res *nfsproc3_setattr_3_svc(SETATTR3args * argp,
+                                   struct svc_req * rqstp)
+{
+    static SETATTR3res result;
+    pre_op_attr pre;
+    char *path;
+
+    PREP(path, argp->object);
+    pre = get_pre_cached();
+    result.status = join(in_sync(argp->guard, pre), exports_rw());
+
+    if (result.status == NFS3_OK)
+       result.status = set_attr(path, argp->object, argp->new_attributes);
+
+    /* overlaps with resfail */
+    result.SETATTR3res_u.resok.obj_wcc.before = pre;
+    result.SETATTR3res_u.resok.obj_wcc.after = get_post_stat(path, rqstp);
+
+    return &result;
+}
+
+LOOKUP3res *nfsproc3_lookup_3_svc(LOOKUP3args * argp, struct svc_req * rqstp)
+{
+    static LOOKUP3res result;
+    unfs3_fh_t *fh;
+    char *path;
+    char obj[NFS_MAXPATHLEN];
+    backend_statstruct buf;
+    int res;
+    uint32 gen;
+
+    PREP(path, argp->what.dir);
+    result.status = cat_name(path, argp->what.name, obj);
+
+    cluster_lookup(obj, rqstp, &result.status);
+
+    if (result.status == NFS3_OK) {
+       res = backend_lstat(obj, &buf);
+       if (res == -1)
+           result.status = lookup_err();
+       else {
+           if (strcmp(argp->what.name, ".") == 0 ||
+               strcmp(argp->what.name, "..") == 0) {
+               fh = fh_comp_ptr(obj, rqstp, 0);
+           } else {
+               gen = backend_get_gen(buf, FD_NONE, obj);
+               fh = fh_extend(argp->what.dir, buf.st_dev, buf.st_ino, gen);
+               fh_cache_add(buf.st_dev, buf.st_ino, obj);
+           }
+
+           if (fh) {
+               result.LOOKUP3res_u.resok.object.data.data_len =
+                   fh_length(fh);
+               result.LOOKUP3res_u.resok.object.data.data_val = (char *) fh;
+               result.LOOKUP3res_u.resok.obj_attributes =
+                   get_post_buf(buf, rqstp);
+           } else {
+               /* path was too long */
+               result.status = NFS3ERR_NAMETOOLONG;
+           }
+       }
+    }
+
+    /* overlaps with resfail */
+    result.LOOKUP3res_u.resok.dir_attributes = get_post_stat(path, rqstp);
+
+    return &result;
+}
+
+ACCESS3res *nfsproc3_access_3_svc(ACCESS3args * argp, struct svc_req * rqstp)
+{
+    static ACCESS3res result;
+    char *path;
+    post_op_attr post;
+    mode_t mode;
+    int access = 0;
+
+    PREP(path, argp->object);
+    post = get_post_cached(rqstp);
+    mode = post.post_op_attr_u.attributes.mode;
+
+    /* owner permissions */
+    if (is_owner(st_cache.st_uid, rqstp)) {
+       if (mode & S_IRUSR)
+           access |= ACCESS3_READ;
+       if (mode & S_IWUSR)
+           access |= ACCESS3_MODIFY | ACCESS3_EXTEND;
+       if (mode & S_IXUSR) {
+           access |= ACCESS3_EXECUTE;
+           if (opt_readable_executables)
+               access |= ACCESS3_READ;
+       }
+    } else if (has_group(st_cache.st_gid, rqstp)) {
+       /* group permissions */
+       if (mode & S_IRGRP)
+           access |= ACCESS3_READ;
+       if (mode & S_IWGRP)
+           access |= ACCESS3_MODIFY | ACCESS3_EXTEND;
+       if (mode & S_IXGRP) {
+           access |= ACCESS3_EXECUTE;
+           if (opt_readable_executables)
+               access |= ACCESS3_READ;
+       }
+    } else {
+       /* other permissions */
+       if (mode & S_IROTH)
+           access |= ACCESS3_READ;
+       if (mode & S_IWOTH)
+           access |= ACCESS3_MODIFY | ACCESS3_EXTEND;
+       if (mode & S_IXOTH) {
+           access |= ACCESS3_EXECUTE;
+           if (opt_readable_executables)
+               access |= ACCESS3_READ;
+       }
+    }
+
+    /* root is allowed everything */
+    if (get_uid(rqstp) == 0)
+       access |= ACCESS3_READ | ACCESS3_MODIFY | ACCESS3_EXTEND;
+
+    /* adjust if directory */
+    if (post.post_op_attr_u.attributes.type == NF3DIR) {
+       if (access & (ACCESS3_READ | ACCESS3_EXECUTE))
+           access |= ACCESS3_LOOKUP;
+       if (access & ACCESS3_MODIFY)
+           access |= ACCESS3_DELETE;
+       access &= ~ACCESS3_EXECUTE;
+    }
+
+    result.status = NFS3_OK;
+    result.ACCESS3res_u.resok.access = access & argp->access;
+    result.ACCESS3res_u.resok.obj_attributes = post;
+
+    return &result;
+}
+
+READLINK3res *nfsproc3_readlink_3_svc(READLINK3args * argp,
+                                     struct svc_req * rqstp)
+{
+    static READLINK3res result;
+    char *path;
+    static char buf[NFS_MAXPATHLEN];
+    int res;
+
+    PREP(path, argp->symlink);
+
+    res = backend_readlink(path, buf, NFS_MAXPATHLEN - 1);
+    if (res == -1)
+       result.status = readlink_err();
+    else {
+       /* readlink does not NULL-terminate */
+       buf[res] = 0;
+
+       result.status = NFS3_OK;
+       result.READLINK3res_u.resok.data = buf;
+    }
+
+    /* overlaps with resfail */
+    result.READLINK3res_u.resok.symlink_attributes =
+       get_post_stat(path, rqstp);
+
+    return &result;
+}
+
+READ3res *nfsproc3_read_3_svc(READ3args * argp, struct svc_req * rqstp)
+{
+    static READ3res result;
+    char *path;
+    int fd, res;
+    static char buf[NFS_MAXDATA_TCP + 1];
+    unsigned int maxdata;
+
+    if (get_socket_type(rqstp) == SOCK_STREAM)
+       maxdata = NFS_MAXDATA_TCP;
+    else
+       maxdata = NFS_MAXDATA_UDP;
+
+    PREP(path, argp->file);
+    result.status = is_reg();
+
+    /* handle reading of executables */
+    read_executable(rqstp, st_cache);
+
+    /* handle read of owned files */
+    read_by_owner(rqstp, st_cache);
+
+    /* if bigger than rtmax, truncate length */
+    if (argp->count > maxdata)
+       argp->count = maxdata;
+
+    if (result.status == NFS3_OK) {
+       fd = fd_open(path, argp->file, UNFS3_FD_READ, TRUE);
+       if (fd != -1) {
+           /* read one more to check for eof */
+           res = backend_pread(fd, buf, argp->count + 1, argp->offset);
+
+           /* eof if we could not read one more */
+           result.READ3res_u.resok.eof = (res <= (int64) argp->count);
+
+           /* close for real when hitting eof */
+           if (result.READ3res_u.resok.eof)
+               fd_close(fd, UNFS3_FD_READ, FD_CLOSE_REAL);
+           else {
+               fd_close(fd, UNFS3_FD_READ, FD_CLOSE_VIRT);
+               res--;
+           }
+
+           if (res >= 0) {
+               result.READ3res_u.resok.count = res;
+               result.READ3res_u.resok.data.data_len = res;
+               result.READ3res_u.resok.data.data_val = buf;
+           } else {
+               /* error during read() */
+
+               /* EINVAL means unreadable object */
+               if (errno == EINVAL)
+                   result.status = NFS3ERR_INVAL;
+               else
+                   result.status = NFS3ERR_IO;
+           }
+       } else
+           /* opening for read failed */
+           result.status = read_err();
+    }
+
+    /* overlaps with resfail */
+    result.READ3res_u.resok.file_attributes = get_post_stat(path, rqstp);
+
+    return &result;
+}
+
+WRITE3res *nfsproc3_write_3_svc(WRITE3args * argp, struct svc_req * rqstp)
+{
+    static WRITE3res result;
+    char *path;
+    int fd, res, res_close;
+
+    PREP(path, argp->file);
+    result.status = join(is_reg(), exports_rw());
+
+    /* handle write of owned files */
+    write_by_owner(rqstp, st_cache);
+
+    if (result.status == NFS3_OK) {
+       /* We allow caching of the fd only for unstable writes. This is to
+          prevent generating a new write verifier for failed stable writes,
+          when the fd was not in the cache. Besides, for stable writes, the
+          fd will be removed from the cache by fd_close() below, so adding
+          it to and removing it from the cache is just a waste of CPU cycles 
+        */
+       fd = fd_open(path, argp->file, UNFS3_FD_WRITE,
+                    (argp->stable == UNSTABLE));
+       if (fd != -1) {
+           res =
+               backend_pwrite(fd, argp->data.data_val, argp->data.data_len,
+                              argp->offset);
+
+           /* close for real if not UNSTABLE write */
+           if (argp->stable == UNSTABLE)
+               res_close = fd_close(fd, UNFS3_FD_WRITE, FD_CLOSE_VIRT);
+           else
+               res_close = fd_close(fd, UNFS3_FD_WRITE, FD_CLOSE_REAL);
+
+           /* we always do fsync(), never fdatasync() */
+           if (argp->stable == DATA_SYNC)
+               argp->stable = FILE_SYNC;
+
+           if (res != -1 && res_close != -1) {
+               result.WRITE3res_u.resok.count = res;
+               result.WRITE3res_u.resok.committed = argp->stable;
+               memcpy(result.WRITE3res_u.resok.verf, wverf,
+                      NFS3_WRITEVERFSIZE);
+           } else {
+               /* error during write or close */
+               result.status = write_write_err();
+           }
+       } else
+           /* could not open for writing */
+           result.status = write_open_err();
+    }
+
+    /* overlaps with resfail */
+    result.WRITE3res_u.resok.file_wcc.before = get_pre_cached();
+    result.WRITE3res_u.resok.file_wcc.after = get_post_stat(path, rqstp);
+
+    return &result;
+}
+
+#ifndef WIN32
+
+/*
+ * store verifier in atime and mtime 
+ */
+static int store_create_verifier(char *obj, createverf3 verf)
+{
+    struct utimbuf ubuf;
+
+    ubuf.actime = verf[0] | verf[1] << 8 | verf[2] << 16 | verf[3] << 24;
+    ubuf.modtime = verf[4] | verf[5] << 8 | verf[6] << 16 | verf[7] << 24;
+
+    return backend_utime(obj, &ubuf);
+}
+
+/*
+ * check if a create verifier matches
+ */
+static int check_create_verifier(backend_statstruct * buf, createverf3 verf)
+{
+    return ((buf->st_atime ==
+            (verf[0] | verf[1] << 8 | verf[2] << 16 | verf[3] << 24))
+           && (buf->st_mtime ==
+               (verf[4] | verf[5] << 8 | verf[6] << 16 | verf[7] << 24)));
+}
+#endif                                /* WIN32 */
+
+CREATE3res *nfsproc3_create_3_svc(CREATE3args * argp, struct svc_req * rqstp)
+{
+    static CREATE3res result;
+    char *path;
+    char obj[NFS_MAXPATHLEN];
+    sattr3 new_attr;
+    int fd = -1, res = -1;
+    backend_statstruct buf;
+    uint32 gen;
+    int flags = O_RDWR | O_CREAT | O_TRUNC | O_NONBLOCK;
+
+    PREP(path, argp->where.dir);
+    result.status = join(cat_name(path, argp->where.name, obj), exports_rw());
+
+    cluster_create(obj, rqstp, &result.status);
+
+    /* GUARDED and EXCLUSIVE maps to Unix exclusive create */
+    if (argp->how.mode != UNCHECKED)
+       flags = flags | O_EXCL;
+
+    if (argp->how.mode != EXCLUSIVE) {
+       new_attr = argp->how.createhow3_u.obj_attributes;
+       result.status = join(result.status, atomic_attr(new_attr));
+    }
+
+    /* Try to open the file */
+    if (result.status == NFS3_OK) {
+       if (argp->how.mode != EXCLUSIVE) {
+           fd = backend_open_create(obj, flags, create_mode(new_attr));
+       } else {
+           fd = backend_open_create(obj, flags, create_mode(new_attr));
+       }
+    }
+
+    if (fd != -1) {
+       /* Successful open */
+       res = backend_fstat(fd, &buf);
+       if (res != -1) {
+           /* Successful stat */
+           if (argp->how.mode == EXCLUSIVE) {
+               /* Save verifier in atime and mtime */
+               res =
+                   backend_store_create_verifier(obj,
+                                                 argp->how.createhow3_u.
+                                                 verf);
+           }
+       }
+
+       if (res != -1) {
+           /* So far, so good */
+           gen = backend_get_gen(buf, fd, obj);
+           fh_cache_add(buf.st_dev, buf.st_ino, obj);
+           backend_close(fd);
+
+           result.CREATE3res_u.resok.obj =
+               fh_extend_post(argp->where.dir, buf.st_dev, buf.st_ino, gen);
+           result.CREATE3res_u.resok.obj_attributes =
+               get_post_buf(buf, rqstp);
+       }
+
+       if (res == -1) {
+           /* backend_fstat() or backend_store_create_verifier() failed */
+           backend_close(fd);
+           result.status = NFS3ERR_IO;
+       }
+
+    } else if (result.status == NFS3_OK) {
+       /* open() failed */
+       if (argp->how.mode == EXCLUSIVE && errno == EEXIST) {
+           /* Check if verifier matches */
+           fd = backend_open(obj, O_NONBLOCK);
+           if (fd != -1) {
+               res = backend_fstat(fd, &buf);
+           }
+
+           if (res != -1) {
+               if (backend_check_create_verifier
+                   (&buf, argp->how.createhow3_u.verf)) {
+                   /* The verifier matched. Return success */
+                   gen = backend_get_gen(buf, fd, obj);
+                   fh_cache_add(buf.st_dev, buf.st_ino, obj);
+                   backend_close(fd);
+
+                   result.CREATE3res_u.resok.obj =
+                       fh_extend_post(argp->where.dir, buf.st_dev,
+                                      buf.st_ino, gen);
+                   result.CREATE3res_u.resok.obj_attributes =
+                       get_post_buf(buf, rqstp);
+               } else {
+                   /* The verifier doesn't match */
+                   result.status = NFS3ERR_EXIST;
+               }
+           }
+       }
+       if (res == -1) {
+           result.status = create_err();
+       }
+    }
+
+    /* overlaps with resfail */
+    result.CREATE3res_u.resok.dir_wcc.before = get_pre_cached();
+    result.CREATE3res_u.resok.dir_wcc.after = get_post_stat(path, rqstp);
+
+    return &result;
+}
+
+MKDIR3res *nfsproc3_mkdir_3_svc(MKDIR3args * argp, struct svc_req * rqstp)
+{
+    static MKDIR3res result;
+    char *path;
+    pre_op_attr pre;
+    post_op_attr post;
+    char obj[NFS_MAXPATHLEN];
+    int res;
+
+    PREP(path, argp->where.dir);
+    pre = get_pre_cached();
+    result.status =
+       join3(cat_name(path, argp->where.name, obj),
+             atomic_attr(argp->attributes), exports_rw());
+
+    cluster_create(obj, rqstp, &result.status);
+
+    if (result.status == NFS3_OK) {
+       res = backend_mkdir(obj, create_mode(argp->attributes));
+       if (res == -1)
+           result.status = mkdir_err();
+       else {
+           result.MKDIR3res_u.resok.obj =
+               fh_extend_type(argp->where.dir, obj, S_IFDIR);
+           result.MKDIR3res_u.resok.obj_attributes = get_post_cached(rqstp);
+       }
+    }
+
+    post = get_post_attr(path, argp->where.dir, rqstp);
+
+    /* overlaps with resfail */
+    result.MKDIR3res_u.resok.dir_wcc.before = pre;
+    result.MKDIR3res_u.resok.dir_wcc.after = post;
+
+    return &result;
+}
+
+SYMLINK3res *nfsproc3_symlink_3_svc(SYMLINK3args * argp,
+                                   struct svc_req * rqstp)
+{
+    static SYMLINK3res result;
+    char *path;
+    pre_op_attr pre;
+    post_op_attr post;
+    char obj[NFS_MAXPATHLEN];
+    int res;
+    mode_t new_mode;
+
+    PREP(path, argp->where.dir);
+    pre = get_pre_cached();
+    result.status =
+       join3(cat_name(path, argp->where.name, obj),
+             atomic_attr(argp->symlink.symlink_attributes), exports_rw());
+
+    cluster_create(obj, rqstp, &result.status);
+
+    if (argp->symlink.symlink_attributes.mode.set_it == TRUE)
+       new_mode = create_mode(argp->symlink.symlink_attributes);
+    else {
+       /* default rwxrwxrwx */
+       new_mode =
+           S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
+           S_IROTH | S_IWOTH | S_IXOTH;
+    }
+
+    if (result.status == NFS3_OK) {
+       umask(~new_mode);
+       res = backend_symlink(argp->symlink.symlink_data, obj);
+       umask(0);
+       if (res == -1)
+           result.status = symlink_err();
+       else {
+           result.SYMLINK3res_u.resok.obj =
+               fh_extend_type(argp->where.dir, obj, S_IFLNK);
+           result.SYMLINK3res_u.resok.obj_attributes =
+               get_post_cached(rqstp);
+       }
+    }
+
+    post = get_post_attr(path, argp->where.dir, rqstp);
+
+    /* overlaps with resfail */
+    result.SYMLINK3res_u.resok.dir_wcc.before = pre;
+    result.SYMLINK3res_u.resok.dir_wcc.after = post;
+
+    return &result;
+}
+
+#ifndef WIN32
+
+/*
+ * create Unix socket
+ */
+static int mksocket(const char *path, mode_t mode)
+{
+    int res, sock;
+    struct sockaddr_un addr;
+
+    sock = socket(PF_UNIX, SOCK_STREAM, 0);
+    addr.sun_family = AF_UNIX;
+    strcpy(addr.sun_path, path);
+    res = sock;
+    if (res != -1) {
+       umask(~mode);
+       res =
+           bind(sock, (struct sockaddr *) &addr,
+                sizeof(addr.sun_family) + strlen(addr.sun_path));
+       umask(0);
+       close(sock);
+    }
+    return res;
+}
+
+#endif                                /* WIN32 */
+
+/*
+ * check and process arguments to MKNOD procedure
+ */
+static nfsstat3 mknod_args(mknoddata3 what, const char *obj, mode_t * mode,
+                          dev_t * dev)
+{
+    sattr3 attr;
+
+    /* determine attributes */
+    switch (what.type) {
+       case NF3REG:
+       case NF3DIR:
+       case NF3LNK:
+           return NFS3ERR_INVAL;
+       case NF3SOCK:
+           if (strlen(obj) + 1 > UNIX_PATH_MAX)
+               return NFS3ERR_NAMETOOLONG;
+           /* fall thru */
+       case NF3FIFO:
+           attr = what.mknoddata3_u.pipe_attributes;
+           break;
+       case NF3BLK:
+       case NF3CHR:
+           attr = what.mknoddata3_u.device.dev_attributes;
+           *dev = (what.mknoddata3_u.device.spec.specdata1 << 8)
+               + what.mknoddata3_u.device.spec.specdata2;
+           break;
+    }
+
+    *mode = create_mode(attr);
+
+    /* adjust mode for creation of device special files */
+    switch (what.type) {
+       case NF3CHR:
+           *mode |= S_IFCHR;
+           break;
+       case NF3BLK:
+           *mode |= S_IFBLK;
+           break;
+       default:
+           break;
+    }
+
+    return atomic_attr(attr);
+}
+
+MKNOD3res *nfsproc3_mknod_3_svc(MKNOD3args * argp, struct svc_req * rqstp)
+{
+    static MKNOD3res result;
+    char *path;
+    pre_op_attr pre;
+    post_op_attr post;
+    char obj[NFS_MAXPATHLEN];
+    int res;
+    mode_t new_mode = 0;
+    dev_t dev = 0;
+
+    PREP(path, argp->where.dir);
+    pre = get_pre_cached();
+    result.status =
+       join3(cat_name(path, argp->where.name, obj),
+             mknod_args(argp->what, obj, &new_mode, &dev), exports_rw());
+
+    cluster_create(obj, rqstp, &result.status);
+
+    if (result.status == NFS3_OK) {
+       if (argp->what.type == NF3CHR || argp->what.type == NF3BLK)
+           res = backend_mknod(obj, new_mode, dev);    /* device */
+       else if (argp->what.type == NF3FIFO)
+           res = backend_mkfifo(obj, new_mode);        /* FIFO */
+       else
+           res = backend_mksocket(obj, new_mode);      /* socket */
+
+       if (res == -1) {
+           result.status = mknod_err();
+       } else {
+           result.MKNOD3res_u.resok.obj =
+               fh_extend_type(argp->where.dir, obj,
+                              type_to_mode(argp->what.type));
+           result.MKNOD3res_u.resok.obj_attributes = get_post_cached(rqstp);
+       }
+    }
+
+    post = get_post_attr(path, argp->where.dir, rqstp);
+
+    /* overlaps with resfail */
+    result.MKNOD3res_u.resok.dir_wcc.before = pre;
+    result.MKNOD3res_u.resok.dir_wcc.after = post;
+
+    return &result;
+}
+
+REMOVE3res *nfsproc3_remove_3_svc(REMOVE3args * argp, struct svc_req * rqstp)
+{
+    static REMOVE3res result;
+    char *path;
+    char obj[NFS_MAXPATHLEN];
+    int res;
+
+    PREP(path, argp->object.dir);
+    result.status =
+       join(cat_name(path, argp->object.name, obj), exports_rw());
+
+    cluster_lookup(obj, rqstp, &result.status);
+
+    if (result.status == NFS3_OK) {
+        change_readdir_cookie();
+       res = backend_remove(obj);
+       if (res == -1)
+           result.status = remove_err();
+    }
+
+    /* overlaps with resfail */
+    result.REMOVE3res_u.resok.dir_wcc.before = get_pre_cached();
+    result.REMOVE3res_u.resok.dir_wcc.after = get_post_stat(path, rqstp);
+
+    return &result;
+}
+
+RMDIR3res *nfsproc3_rmdir_3_svc(RMDIR3args * argp, struct svc_req * rqstp)
+{
+    static RMDIR3res result;
+    char *path;
+    char obj[NFS_MAXPATHLEN];
+    int res;
+
+    PREP(path, argp->object.dir);
+    result.status =
+       join(cat_name(path, argp->object.name, obj), exports_rw());
+
+    cluster_lookup(obj, rqstp, &result.status);
+
+    if (result.status == NFS3_OK) {
+        change_readdir_cookie();
+       res = backend_rmdir(obj);
+       if (res == -1)
+           result.status = rmdir_err();
+    }
+
+    /* overlaps with resfail */
+    result.RMDIR3res_u.resok.dir_wcc.before = get_pre_cached();
+    result.RMDIR3res_u.resok.dir_wcc.after = get_post_stat(path, rqstp);
+
+    return &result;
+}
+
+RENAME3res *nfsproc3_rename_3_svc(RENAME3args * argp, struct svc_req * rqstp)
+{
+    static RENAME3res result;
+    char *from;
+    char *to;
+    char from_obj[NFS_MAXPATHLEN];
+    char to_obj[NFS_MAXPATHLEN];
+    pre_op_attr pre;
+    post_op_attr post;
+    int res;
+
+    PREP(from, argp->from.dir);
+    pre = get_pre_cached();
+    result.status =
+       join(cat_name(from, argp->from.name, from_obj), exports_rw());
+
+    cluster_lookup(from_obj, rqstp, &result.status);
+
+    to = fh_decomp(argp->to.dir);
+
+    if (result.status == NFS3_OK) {
+       result.status =
+           join(cat_name(to, argp->to.name, to_obj),
+                exports_compat(to, rqstp));
+
+       cluster_create(to_obj, rqstp, &result.status);
+
+       if (result.status == NFS3_OK) {
+           change_readdir_cookie();
+           res = backend_rename(from_obj, to_obj);
+           if (res == -1)
+               result.status = rename_err();
+       }
+    }
+
+    post = get_post_attr(from, argp->from.dir, rqstp);
+
+    /* overlaps with resfail */
+    result.RENAME3res_u.resok.fromdir_wcc.before = pre;
+    result.RENAME3res_u.resok.fromdir_wcc.after = post;
+    result.RENAME3res_u.resok.todir_wcc.before = get_pre_cached();
+    result.RENAME3res_u.resok.todir_wcc.after = get_post_stat(to, rqstp);
+
+    return &result;
+}
+
+LINK3res *nfsproc3_link_3_svc(LINK3args * argp, struct svc_req * rqstp)
+{
+    static LINK3res result;
+    char *path, *old;
+    pre_op_attr pre;
+    post_op_attr post;
+    char obj[NFS_MAXPATHLEN];
+    int res;
+
+    PREP(path, argp->link.dir);
+    pre = get_pre_cached();
+    result.status = join(cat_name(path, argp->link.name, obj), exports_rw());
+
+    cluster_create(obj, rqstp, &result.status);
+
+    old = fh_decomp(argp->file);
+
+    if (old && result.status == NFS3_OK) {
+       result.status = exports_compat(old, rqstp);
+
+       if (result.status == NFS3_OK) {
+           res = backend_link(old, obj);
+           if (res == -1)
+               result.status = link_err();
+       }
+    } else if (!old)
+       result.status = NFS3ERR_STALE;
+
+    post = get_post_attr(path, argp->link.dir, rqstp);
+
+    /* overlaps with resfail */
+    result.LINK3res_u.resok.file_attributes = get_post_stat(old, rqstp);
+    result.LINK3res_u.resok.linkdir_wcc.before = pre;
+    result.LINK3res_u.resok.linkdir_wcc.after = post;
+
+    return &result;
+}
+
+READDIR3res *nfsproc3_readdir_3_svc(READDIR3args * argp,
+                                   struct svc_req * rqstp)
+{
+    static READDIR3res result;
+    char *path;
+
+    PREP(path, argp->dir);
+
+    result = read_dir(path, argp->cookie, argp->cookieverf, argp->count);
+    result.READDIR3res_u.resok.dir_attributes = get_post_stat(path, rqstp);
+
+    return &result;
+}
+
+READDIRPLUS3res *nfsproc3_readdirplus_3_svc(U(READDIRPLUS3args * argp),
+                                           U(struct svc_req * rqstp))
+{
+    static READDIRPLUS3res result;
+
+    /* 
+     * we don't do READDIRPLUS since it involves filehandle and
+     * attribute getting which is impossible to do atomically
+     * from user-space
+     */
+    result.status = NFS3ERR_NOTSUPP;
+    result.READDIRPLUS3res_u.resfail.dir_attributes.attributes_follow = FALSE;
+
+    return &result;
+}
+
+FSSTAT3res *nfsproc3_fsstat_3_svc(FSSTAT3args * argp, struct svc_req * rqstp)
+{
+    static FSSTAT3res result;
+    char *path;
+    backend_statvfsstruct buf;
+    int res;
+
+    PREP(path, argp->fsroot);
+
+    /* overlaps with resfail */
+    result.FSSTAT3res_u.resok.obj_attributes = get_post_cached(rqstp);
+
+    res = backend_statvfs(path, &buf);
+    if (res == -1) {
+       /* statvfs fell on its nose */
+       if ((exports_opts & OPT_REMOVABLE) && export_point(path)) {
+           /* Removable media export point; probably no media inserted.
+              Return dummy values. */
+           result.status = NFS3_OK;
+           result.FSSTAT3res_u.resok.tbytes = 0;
+           result.FSSTAT3res_u.resok.fbytes = 0;
+           result.FSSTAT3res_u.resok.abytes = 0;
+           result.FSSTAT3res_u.resok.tfiles = 0;
+           result.FSSTAT3res_u.resok.ffiles = 0;
+           result.FSSTAT3res_u.resok.afiles = 0;
+           result.FSSTAT3res_u.resok.invarsec = 0;
+       } else {
+           result.status = NFS3ERR_IO;
+       }
+    } else {
+       result.status = NFS3_OK;
+       result.FSSTAT3res_u.resok.tbytes =
+           (uint64) buf.f_blocks * buf.f_frsize;
+       result.FSSTAT3res_u.resok.fbytes = 
+           (uint64) buf.f_bfree * buf.f_frsize;
+       result.FSSTAT3res_u.resok.abytes =
+           (uint64) buf.f_bavail * buf.f_frsize;
+       result.FSSTAT3res_u.resok.tfiles = buf.f_files;
+       result.FSSTAT3res_u.resok.ffiles = buf.f_ffree;
+       result.FSSTAT3res_u.resok.afiles = buf.f_ffree;
+       result.FSSTAT3res_u.resok.invarsec = 0;
+    }
+
+    return &result;
+}
+
+FSINFO3res *nfsproc3_fsinfo_3_svc(FSINFO3args * argp, struct svc_req * rqstp)
+{
+    static FSINFO3res result;
+    char *path;
+    unsigned int maxdata;
+
+    if (get_socket_type(rqstp) == SOCK_STREAM)
+       maxdata = NFS_MAXDATA_TCP;
+    else
+       maxdata = NFS_MAXDATA_UDP;
+
+    PREP(path, argp->fsroot);
+
+    result.FSINFO3res_u.resok.obj_attributes = get_post_cached(rqstp);
+
+    result.status = NFS3_OK;
+    result.FSINFO3res_u.resok.rtmax = maxdata;
+    result.FSINFO3res_u.resok.rtpref = maxdata;
+    result.FSINFO3res_u.resok.rtmult = 4096;
+    result.FSINFO3res_u.resok.wtmax = maxdata;
+    result.FSINFO3res_u.resok.wtpref = maxdata;
+    result.FSINFO3res_u.resok.wtmult = 4096;
+    result.FSINFO3res_u.resok.dtpref = 4096;
+    result.FSINFO3res_u.resok.maxfilesize = ~0ULL;
+    result.FSINFO3res_u.resok.time_delta.seconds = backend_time_delta_seconds;
+    result.FSINFO3res_u.resok.time_delta.nseconds = 0;
+    result.FSINFO3res_u.resok.properties = backend_fsinfo_properties;
+
+    return &result;
+}
+
+PATHCONF3res *nfsproc3_pathconf_3_svc(PATHCONF3args * argp,
+                                     struct svc_req * rqstp)
+{
+    static PATHCONF3res result;
+    char *path;
+
+    PREP(path, argp->object);
+
+    result.PATHCONF3res_u.resok.obj_attributes = get_post_cached(rqstp);
+
+    result.status = NFS3_OK;
+    result.PATHCONF3res_u.resok.linkmax = 0xFFFFFFFF;
+    result.PATHCONF3res_u.resok.name_max = NFS_MAXPATHLEN;
+    result.PATHCONF3res_u.resok.no_trunc = TRUE;
+    result.PATHCONF3res_u.resok.chown_restricted = FALSE;
+    result.PATHCONF3res_u.resok.case_insensitive =
+       backend_pathconf_case_insensitive;
+    result.PATHCONF3res_u.resok.case_preserving = TRUE;
+
+    return &result;
+}
+
+COMMIT3res *nfsproc3_commit_3_svc(COMMIT3args * argp, struct svc_req * rqstp)
+{
+    static COMMIT3res result;
+    char *path;
+    int res;
+
+    PREP(path, argp->file);
+    result.status = join(is_reg(), exports_rw());
+
+    if (result.status == NFS3_OK) {
+       res = fd_sync(argp->file);
+       if (res != -1)
+           memcpy(result.COMMIT3res_u.resok.verf, wverf, NFS3_WRITEVERFSIZE);
+       else
+           /* error during fsync() or close() */
+           result.status = NFS3ERR_IO;
+    }
+
+    /* overlaps with resfail */
+    result.COMMIT3res_u.resfail.file_wcc.before = get_pre_cached();
+    result.COMMIT3res_u.resfail.file_wcc.after = get_post_stat(path, rqstp);
+
+    return &result;
+}
diff --git a/unfs3/nfs.h b/unfs3/nfs.h
new file mode 100644 (file)
index 0000000..63041fa
--- /dev/null
@@ -0,0 +1,1007 @@
+/*
+ * UNFS3 NFS protocol definitions
+ * Generated by rpcgen
+ */
+
+#ifndef _NFS_PROT_H_RPCGEN
+#define _NFS_PROT_H_RPCGEN
+
+/* for lack of a better place */
+#ifdef __GNUC__
+#define U(x) x __attribute__ ((unused))
+#else
+#define U(x) x
+#endif
+
+#if HAVE_STDINT_H == 1
+#include <stdint.h>
+#endif
+
+#include <rpc/rpc.h>
+
+#define UNIX_PATH_MAX 108
+
+#define NFS_PORT 2049
+#define NFS_MAXDATA_TCP 524288
+#define NFS_MAXDATA_UDP 32768
+#define NFS_MAX_UDP_PACKET (NFS_MAXDATA_UDP + 4096) /* The extra 4096 bytes are for the RPC header */
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_FIFO_DEV -1
+#define NFSMODE_FMT 0170000
+#define NFSMODE_DIR 0040000
+#define NFSMODE_CHR 0020000
+#define NFSMODE_BLK 0060000
+#define NFSMODE_REG 0100000
+#define NFSMODE_LNK 0120000
+#define NFSMODE_SOCK 0140000
+#define NFSMODE_FIFO 0010000
+
+typedef char *filename;
+
+typedef char *nfspath;
+#define NFS3_FHSIZE 64
+#define NFS3_COOKIEVERFSIZE 8
+#define NFS3_CREATEVERFSIZE 8
+#define NFS3_WRITEVERFSIZE 8
+
+#if HAVE_UINT64 == 0
+typedef uint64_t uint64;
+#endif
+
+#if HAVE_INT64 == 0
+typedef int64_t int64;
+#endif
+
+#if HAVE_UINT32 == 0
+#if HAVE_XDR_U_LONG == 1
+typedef u_long uint32;
+#else
+typedef uint32_t uint32;
+#endif
+#endif
+
+#if HAVE_INT32 == 0
+#if HAVE_XDR_LONG == 1
+typedef long int32;
+#else
+typedef int32_t int32;
+#endif
+#endif
+
+typedef char *filename3;
+
+typedef char *nfspath3;
+
+typedef uint64 fileid3;
+
+typedef uint64 cookie3;
+
+typedef char cookieverf3[NFS3_COOKIEVERFSIZE];
+
+typedef char createverf3[NFS3_CREATEVERFSIZE];
+
+typedef char writeverf3[NFS3_WRITEVERFSIZE];
+
+typedef uint32 uid3;
+
+typedef uint32 gid3;
+
+typedef uint64 size3;
+
+typedef uint64 offset3;
+
+typedef uint32 mode3;
+
+typedef uint32 count3;
+
+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 ftype3 {
+       NF3REG = 1,
+       NF3DIR = 2,
+       NF3BLK = 3,
+       NF3CHR = 4,
+       NF3LNK = 5,
+       NF3SOCK = 6,
+       NF3FIFO = 7,
+};
+typedef enum ftype3 ftype3;
+
+struct specdata3 {
+       uint32 specdata1;
+       uint32 specdata2;
+};
+typedef struct specdata3 specdata3;
+
+struct nfs_fh3 {
+       struct {
+               u_int data_len;
+               char *data_val;
+       } data;
+};
+typedef struct nfs_fh3 nfs_fh3;
+
+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;
+
+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 post_op_fh3 {
+       bool_t handle_follows;
+       union {
+               nfs_fh3 handle;
+       } post_op_fh3_u;
+};
+typedef struct post_op_fh3 post_op_fh3;
+
+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;
+
+struct diropargs3 {
+       nfs_fh3 dir;
+       filename3 name;
+};
+typedef struct diropargs3 diropargs3;
+
+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;
+
+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;
+
+struct LOOKUP3args {
+       diropargs3 what;
+};
+typedef struct LOOKUP3args LOOKUP3args;
+
+struct LOOKUP3resok {
+       post_op_attr dir_attributes;
+       nfs_fh3 object;
+       post_op_attr obj_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;
+
+#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 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 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;
+
+enum stable_how {
+       UNSTABLE = 0,
+       DATA_SYNC = 1,
+       FILE_SYNC = 2,
+};
+typedef enum stable_how stable_how;
+
+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;
+
+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;
+
+enum createmode3 {
+       UNCHECKED = 0,
+       GUARDED = 1,
+       EXCLUSIVE = 2,
+};
+typedef enum createmode3 createmode3;
+
+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 CREATE3resok {
+       wcc_data dir_wcc;
+       post_op_fh3 obj;
+       post_op_attr obj_attributes;
+};
+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 MKDIR3args {
+       diropargs3 where;
+       sattr3 attributes;
+};
+typedef struct MKDIR3args MKDIR3args;
+
+struct MKDIR3resok {
+       wcc_data dir_wcc;
+       post_op_fh3 obj;
+       post_op_attr obj_attributes;
+};
+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 symlinkdata3 {
+       sattr3 symlink_attributes;
+       nfspath3 symlink_data;
+};
+typedef struct symlinkdata3 symlinkdata3;
+
+struct SYMLINK3args {
+       diropargs3 where;
+       symlinkdata3 symlink;
+};
+typedef struct SYMLINK3args SYMLINK3args;
+
+struct SYMLINK3resok {
+       wcc_data dir_wcc;
+       post_op_fh3 obj;
+       post_op_attr obj_attributes;
+};
+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 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 {
+       wcc_data dir_wcc;
+       post_op_fh3 obj;
+       post_op_attr obj_attributes;
+};
+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 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 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 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 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 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 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;
+#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 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;
+
+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 NFS3_PROGRAM 100003
+#define NFS_V3 3
+
+#define NFSPROC3_NULL 0
+extern  void * nfsproc3_null_3_svc(void *, struct svc_req *);
+#define NFSPROC3_GETATTR 1
+extern  GETATTR3res * nfsproc3_getattr_3_svc(GETATTR3args *, struct svc_req *);
+#define NFSPROC3_SETATTR 2
+extern  SETATTR3res * nfsproc3_setattr_3_svc(SETATTR3args *, struct svc_req *);
+#define NFSPROC3_LOOKUP 3
+extern  LOOKUP3res * nfsproc3_lookup_3_svc(LOOKUP3args *, struct svc_req *);
+#define NFSPROC3_ACCESS 4
+extern  ACCESS3res * nfsproc3_access_3_svc(ACCESS3args *, struct svc_req *);
+#define NFSPROC3_READLINK 5
+extern  READLINK3res * nfsproc3_readlink_3_svc(READLINK3args *, struct svc_req *);
+#define NFSPROC3_READ 6
+extern  READ3res * nfsproc3_read_3_svc(READ3args *, struct svc_req *);
+#define NFSPROC3_WRITE 7
+extern  WRITE3res * nfsproc3_write_3_svc(WRITE3args *, struct svc_req *);
+#define NFSPROC3_CREATE 8
+extern  CREATE3res * nfsproc3_create_3_svc(CREATE3args *, struct svc_req *);
+#define NFSPROC3_MKDIR 9
+extern  MKDIR3res * nfsproc3_mkdir_3_svc(MKDIR3args *, struct svc_req *);
+#define NFSPROC3_SYMLINK 10
+extern  SYMLINK3res * nfsproc3_symlink_3_svc(SYMLINK3args *, struct svc_req *);
+#define NFSPROC3_MKNOD 11
+extern  MKNOD3res * nfsproc3_mknod_3_svc(MKNOD3args *, struct svc_req *);
+#define NFSPROC3_REMOVE 12
+extern  REMOVE3res * nfsproc3_remove_3_svc(REMOVE3args *, struct svc_req *);
+#define NFSPROC3_RMDIR 13
+extern  RMDIR3res * nfsproc3_rmdir_3_svc(RMDIR3args *, struct svc_req *);
+#define NFSPROC3_RENAME 14
+extern  RENAME3res * nfsproc3_rename_3_svc(RENAME3args *, struct svc_req *);
+#define NFSPROC3_LINK 15
+extern  LINK3res * nfsproc3_link_3_svc(LINK3args *, struct svc_req *);
+#define NFSPROC3_READDIR 16
+extern  READDIR3res * nfsproc3_readdir_3_svc(READDIR3args *, struct svc_req *);
+#define NFSPROC3_READDIRPLUS 17
+extern  READDIRPLUS3res * nfsproc3_readdirplus_3_svc(READDIRPLUS3args *, struct svc_req *);
+#define NFSPROC3_FSSTAT 18
+extern  FSSTAT3res * nfsproc3_fsstat_3_svc(FSSTAT3args *, struct svc_req *);
+#define NFSPROC3_FSINFO 19
+extern  FSINFO3res * nfsproc3_fsinfo_3_svc(FSINFO3args *, struct svc_req *);
+#define NFSPROC3_PATHCONF 20
+extern  PATHCONF3res * nfsproc3_pathconf_3_svc(PATHCONF3args *, struct svc_req *);
+#define NFSPROC3_COMMIT 21
+extern  COMMIT3res * nfsproc3_commit_3_svc(COMMIT3args *, struct svc_req *);
+extern int nfs3_program_3_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#endif /* !_NFS_PROT_H_RPCGEN */
diff --git a/unfs3/password.c b/unfs3/password.c
new file mode 100644 (file)
index 0000000..868bb4d
--- /dev/null
@@ -0,0 +1,116 @@
+
+/*
+ * UNFS3 mount password support routines
+ * (C) 2004, Peter Astrand <astrand@cendio.se>
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef WIN32
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/times.h>                /* times */
+#endif                                /* WIN32 */
+#include <fcntl.h>
+#include <sys/time.h>                 /* gettimeofday */
+#include "md5.h"
+#include "backend.h"
+#include "daemon.h"                   /* logmsg */
+
+#ifndef WIN32
+int gen_nonce(char *nonce)
+{
+    struct stat st;
+    struct tms tmsbuf;
+    md5_state_t state;
+    unsigned int *arr;
+    int bytes_read, fd;
+
+    if (((fd = open("/dev/urandom", O_RDONLY)) != -1)
+       || ((fd = open("/dev/random", O_RDONLY)) != -1)) {
+       bytes_read = read(fd, nonce, 32);
+       close(fd);
+       if (bytes_read == 32)
+           return 0;
+    }
+
+    /* No /dev/random; do it by hand */
+    arr = (unsigned int *) nonce;
+    stat("/tmp", &st);
+    arr[0] = st.st_mtime;
+    arr[1] = st.st_atime;
+    arr[2] = st.st_ctime;
+    arr[3] = times(&tmsbuf);
+    arr[4] = tmsbuf.tms_cutime;
+    arr[5] = tmsbuf.tms_cstime;
+    gettimeofday((struct timeval *) &arr[6], NULL);
+
+    md5_init(&state);
+    md5_append(&state, (md5_byte_t *) nonce, 32);
+    md5_finish(&state, (md5_byte_t *) nonce);
+    return 0;
+}
+#endif                                /* WIN32 */
+
+static char nibble_as_hexchar(unsigned char c)
+{
+    if (c <= 9)
+       return c + '0';
+
+    return c - 10 + 'a';
+}
+
+static void hexify(md5_byte_t digest[16], char hexdigest[32])
+{
+    int i, j;
+
+    for (i = j = 0; i < 16; i++) {
+       char c;
+
+       /* The first four bits */
+       c = (digest[i] >> 4) & 0xf;
+       hexdigest[j++] = nibble_as_hexchar(c);
+       /* The next four bits */
+       c = (digest[i] & 0xf);
+       hexdigest[j++] = nibble_as_hexchar(c);
+    }
+}
+
+/* Handle mount commands:
+ * Advance dpath to first slash
+ * Copy command arguments to arg. 
+*/
+void mnt_cmd_argument(char **dpath, const char *cmd, char *arg, size_t maxlen)
+{
+    char *slash;
+
+    *dpath += strlen(cmd);
+    strncpy(arg, *dpath, maxlen);
+    arg[maxlen] = '\0';
+
+    slash = strchr(arg, '/');
+    if (slash != NULL)
+       *slash = '\0';
+
+    *dpath += strlen(arg);
+}
+
+void otp_digest(char nonce[32], char *password, char hexdigest[32])
+{
+    md5_state_t state;
+    md5_byte_t digest[16];
+
+    /* Calculate the digest, in the same way as the client did */
+    md5_init(&state);
+    md5_append(&state, (md5_byte_t *) nonce, 32);
+    md5_append(&state, (md5_byte_t *) password, strlen(password));
+    md5_finish(&state, digest);
+    hexify(digest, hexdigest);
+}
diff --git a/unfs3/password.h b/unfs3/password.h
new file mode 100644 (file)
index 0000000..946a6d8
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * UNFS3 mount password support routines
+ * (C) 2004, Peter Astrand <astrand@cendio.se>
+ * see file LICENSE for license details
+ */
+
+int gen_nonce(char *nonce);
+
+void mnt_cmd_argument(char **dpath, const char *cmd, char *arg, size_t maxlen);
+
+void otp_digest(char nonce[32], 
+               char *password, 
+               char hexdigest[32]);
diff --git a/unfs3/readdir.c b/unfs3/readdir.c
new file mode 100644 (file)
index 0000000..0e7290c
--- /dev/null
@@ -0,0 +1,215 @@
+
+/*
+ * UNFS3 readdir routine
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "nfs.h"
+#include "mount.h"
+#include "fh.h"
+#include "readdir.h"
+#include "backend.h"
+#include "Config/exports.h"
+#include "daemon.h"
+#include "error.h"
+
+/*
+ * maximum number of entries in readdir results
+ *
+ * this is 4096 / 28 (the minimum size of an entry3)
+ */
+#define MAX_ENTRIES 143
+
+/*
+ * static READDIR3resok size with XDR overhead
+ *
+ * 88 bytes attributes, 8 bytes verifier, 4 bytes value_follows for
+ * first entry, 4 bytes eof flag
+ */
+#define RESOK_SIZE 104
+
+/*
+ * static entry3 size with XDR overhead
+ *
+ * 8 bytes fileid, 4 bytes name length, 8 bytes cookie, 4 byte value_follows
+ */
+#define ENTRY_SIZE 24
+
+/*
+ * size of a name with XDR overhead
+ *
+ * XDR pads to multiple of 4 bytes
+ */
+#define NAME_SIZE(x) (((strlen((x))+3)/4)*4)
+
+uint32 directory_hash(const char *path)
+{
+    backend_dirstream *search;
+    struct dirent *this;
+    uint32 hval = 0;
+
+    search = backend_opendir(path);
+    if (!search) {
+       return 0;
+    }
+
+    while ((this = backend_readdir(search)) != NULL) {
+       hval = fnv1a_32(this->d_name, hval);
+    }
+
+    backend_closedir(search);
+    return hval;
+}
+
+/*
+ * perform a READDIR operation
+ *
+ * fh_decomp must be called directly before to fill the stat cache
+ */
+READDIR3res read_dir(const char *path, cookie3 cookie, cookieverf3 verf,
+                    count3 count)
+{
+    READDIR3res result;
+    READDIR3resok resok;
+    cookie3 upper;
+    static entry3 entry[MAX_ENTRIES];
+    backend_statstruct buf;
+    int res;
+    backend_dirstream *search;
+    struct dirent *this;
+    count3 i, real_count;
+    static char obj[NFS_MAXPATHLEN * MAX_ENTRIES];
+    char scratch[NFS_MAXPATHLEN];
+
+    /* check upper part of cookie */
+    upper = cookie & 0xFFFFFFFF00000000ULL;
+    if (cookie != 0 && upper != rcookie) {
+      /* ignore cookie if unexpected so we restart from the beginning */
+      cookie = 0;
+    }
+    cookie &= 0xFFFFFFFFULL;
+
+    /* we refuse to return more than 4k from READDIR */
+    if (count > 4096)
+       count = 4096;
+
+    /* account for size of information heading resok structure */
+    real_count = RESOK_SIZE;
+
+    /* We are always returning zero as a cookie verifier. One reason for this 
+       is that stat() on Windows seems to return cached st_mtime values,
+       which gives spurious NFS3ERR_BAD_COOKIEs. Btw, here's what Peter
+       Staubach has to say about cookie verifiers:
+
+       "From my viewpoint, the cookieverifier was a failed experiment in NFS
+       Version 3.  The semantics were never well understood nor supported by
+       many local file systems.  The Solaris NFS server always returns zeros
+       in the cookieverifier field." */
+    memset(verf, 0, NFS3_COOKIEVERFSIZE);
+
+    search = backend_opendir(path);
+    if (!search) {
+       if ((exports_opts & OPT_REMOVABLE) && (export_point(path))) {
+           /* Removable media export point; probably no media inserted.
+              Return empty directory. */
+           memset(resok.cookieverf, 0, NFS3_COOKIEVERFSIZE);
+           resok.reply.entries = NULL;
+           resok.reply.eof = TRUE;
+           result.status = NFS3_OK;
+           result.READDIR3res_u.resok = resok;
+           return result;
+       } else {
+           result.status = readdir_err();
+           return result;
+       }
+    }
+
+    this = backend_readdir(search);
+    /* We cannot use telldir()/seekdir(), since the value from telldir() is
+       not valid after closedir(). */
+    for (i = 0; i < cookie; i++)
+       if (this)
+           this = backend_readdir(search);
+
+    i = 0;
+    entry[0].name = NULL;
+    while (this && real_count < count && i < MAX_ENTRIES) {
+       if (i > 0)
+           entry[i - 1].nextentry = &entry[i];
+
+       if (strlen(path) + strlen(this->d_name) + 1 < NFS_MAXPATHLEN) {
+
+           if (strcmp(path, "/") == 0)
+               sprintf(scratch, "/%s", this->d_name);
+           else
+               sprintf(scratch, "%s/%s", path, this->d_name);
+
+           res = backend_lstat(scratch, &buf);
+           if (res == -1) {
+               result.status = readdir_err();
+               backend_closedir(search);
+               return result;
+           }
+
+           strcpy(&obj[i * NFS_MAXPATHLEN], this->d_name);
+
+#ifdef WIN32
+           /* See comment in attr.c:get_post_buf */
+           entry[i].fileid = (buf.st_ino >> 32) ^ (buf.st_ino & 0xffffffff);
+#else
+           entry[i].fileid = buf.st_ino;
+#endif
+           entry[i].name = &obj[i * NFS_MAXPATHLEN];
+           entry[i].cookie = (cookie + 1 + i) | rcookie;
+           entry[i].nextentry = NULL;
+
+           /* account for entry size */
+           real_count += ENTRY_SIZE + NAME_SIZE(this->d_name);
+
+           /* whoops, overflowed the maximum size */
+           if (real_count > count && i > 0)
+               entry[i - 1].nextentry = NULL;
+           else {
+               /* advance to next entry */
+               this = backend_readdir(search);
+           }
+
+           i++;
+       } else {
+           result.status = NFS3ERR_IO;
+           backend_closedir(search);
+           return result;
+       }
+    }
+    backend_closedir(search);
+
+    if (entry[0].name)
+       resok.reply.entries = &entry[0];
+    else
+       resok.reply.entries = NULL;
+
+    if (this)
+       resok.reply.eof = FALSE;
+    else
+       resok.reply.eof = TRUE;
+
+    memcpy(resok.cookieverf, verf, NFS3_COOKIEVERFSIZE);
+
+    result.status = NFS3_OK;
+    result.READDIR3res_u.resok = resok;
+
+    return result;
+}
diff --git a/unfs3/readdir.h b/unfs3/readdir.h
new file mode 100644 (file)
index 0000000..3ec78bb
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * UNFS3 readdir routine
+ * (C) 2003, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_READDIR_H
+#define UNFS3_READDIR_H
+
+READDIR3res
+read_dir(const char *path, cookie3 cookie, cookieverf3 verf, count3 count);
+uint32 directory_hash(const char *path);
+
+#endif
diff --git a/unfs3/unfs3.spec b/unfs3/unfs3.spec
new file mode 100644 (file)
index 0000000..88859f0
--- /dev/null
@@ -0,0 +1,38 @@
+%define version 0.9.22
+Summary: UNFS3 user-space NFSv3 server
+Group: Applications/System
+Name: unfs3
+Version: %{version}
+Release: 1
+License: BSD
+Packager: Pascal Schmidt <unfs3-server@ewetel.net>
+Vendor: none
+Source: unfs3-%{version}.tar.gz
+Buildroot: /tmp/unfs3
+
+%prep
+%setup
+
+%build
+./configure --enable-cluster --prefix=/usr --mandir=/usr/share/man
+make
+
+%install
+[ -n "$RPM_BUILD_ROOT" ] && rm -rf "$RPM_BUILD_ROOT"
+make DESTDIR="$RPM_BUILD_ROOT" install install-init
+
+%clean
+[ -n "$RPM_BUILD_ROOT" ] && rm -rf "$RPM_BUILD_ROOT"
+
+%description
+UNFS3 is a user-space implementation of the NFS (Network File System)
+version 3 server specification. It provides a daemon that supports both
+the MOUNT and NFS protocol.
+
+%files
+%attr (  -, root, root) %doc CREDITS README README.nfsroot LICENSE NEWS contrib doc
+%attr (755, root, root) /usr/sbin/unfsd
+%attr (755, root, root) /etc/init.d/unfsd
+%attr (644, root, root) %doc /usr/share/man/man7/tags.7.gz
+%attr (644, root, root) %doc /usr/share/man/man8/unfsd.8.gz
+
diff --git a/unfs3/unfsd.8 b/unfs3/unfsd.8
new file mode 100644 (file)
index 0000000..eb7dd20
--- /dev/null
@@ -0,0 +1,394 @@
+.\"
+.\" (C) 2008, Pascal Schmidt
+.\"
+.TH unfsd 8 "05 Jan 2008"
+.SH NAME
+unfsd \- NFS server process
+.SH SYNOPSIS
+.BI "/usr/sbin/unfsd [" options "]"
+.SH DESCRIPTION
+The
+.B unfsd
+program implements the MOUNT and NFS version 3 protocols. It listens for
+client requests, performs them on the local filesystem of the server, and
+then returns the results of the operations to the clients.
+.P
+At startup,
+.B unfsd
+reads the exports file,
+.I /etc/exports
+by default, to find out which directories are available to clients
+and what options are in effect (see
+.B EXPORTS FILE
+section below for syntax and possible options).
+.P
+Normally, 
+.B unfsd
+should be run as the
+.B root
+user. It will then switch its effective
+user and group id to the numbers listed in incoming NFS requests. This
+means filesystem operations will be performed as if done by a local
+user with the same ids. If the incoming request is for user or group
+id 0 (meaning
+.BR root "), " unfsd
+will switch to the user and group id of the
+.B nobody
+user before performing filesystem operations (this is known as
+.BR "root squashing" ")."
+If the user
+.B nobody
+does not exist on the system, a user and group id of 65534 will be used.
+This behavior can be modified by use of the
+.B no_root_squash
+and
+.B all_squash
+options in the exports file as well as the
+.B anonuid
+and
+.B anongid
+options on a per-share basis.
+.P
+If
+.B unfsd
+is running as a normal unprivileged user, no switching of the effective
+user and group id will take place. Instead, all filesystem operations
+will be performed with the id of the user running
+.BR unfsd .
+.SH RESTRICTIONS
+Some NFS clients may attempt to perform operations that
+.B unfsd
+cannot fully support.
+.TP
+.B "Object Creation"
+When creating filesystem objects, it is only possible to specify the
+initial mode for the object. The initial user and group ownership,
+object size, and timestamps cannot be specified and will be set to
+default values.
+.TP
+.B "File Locking"
+The network lock manager (NLM) protocol is not supported. This means that
+clients may have to mount with special mount options, disabling locking
+on the mounted NFS volume (nolock for Linux clients).
+.SH OPTIONS
+.TP
+.B \-h
+Display a short option summary.
+.TP
+.BI "\-e " "\<file\>"
+Use the given file as the exports file, instead of using
+.IR /etc/exports .
+Note that the file needs to be specified using an absolute path.
+.TP
+.BI "\-i " "\<file\>"
+Use the given file as pid file. When the daemon starts up, it will
+write its pid (process id) to the given file. Upon exit, the daemon
+will remove the file. Failure to create or remove the pid file is
+not considered fatal and only reported to syslog.
+.TP
+.B \-u
+Use an unprivileged port for NFS and MOUNT service. Normally,
+.B unfsd
+will use port number 2049, which is the standard port for NFS.
+When this option is in effect, arbitrary ports chosen by the RPC library
+will be used. You may need to use this option when running
+.B unfsd
+from a normal user account.
+.TP
+.BI "\-n " "\<port\>"
+Use the specified port for the NFS service.
+.TP
+.BI "\-m " "\<port\>"
+Use the specified port for the MOUNT service. The default is to
+use port number 2049, the same as for the NFS service. You can use
+the same port for both services if you want.
+.TP
+.B \-t
+TCP only operation. By default,
+.B unfsd
+provides its services to clients using either UDP or TCP as communications
+protocol. When this option is present, only TCP connections are
+serviced.
+.TP
+.B \-p
+Do not register with the portmapper. This will prevent other hosts from
+finding out the port numbers used for the MOUNT and NFS services by
+querying the portmap daemon. Clients
+will need to manually specify the port numbers to use (on Linux clients,
+use the
+.BR mountport " and " port
+mount options).
+.TP
+.B \-c
+Enable cluster extensions. This feature is only available when
+.B unfsd
+was compiled with cluster support. When this option is enabled, so-called
+tagged files are handled differently from normal files, making it possible
+to serve different file contents to different clients for the same filename.
+See
+.BR tags (7)
+for a description of tagged files. This option causes a performance hit.
+.TP
+.BI "\-C" "\ <path>"
+Limit the use of cluster extensions to a list of colon-seperated
+directories. When this option is present, the performance hit caused by
+clustering extensions only applies to the listed directories and their
+subdirectories.
+.TP
+.B \-s 
+Single user mode; activate basic uid translation. This option is
+useful when the server and client are using different user and group
+ids. All requests from the client will be served from the user id that started 
+.B unfsd, 
+no user id switching will take place (even if unfsd was started by
+root).
+Ownership is reported as follows: files belonging to the user id 
+running
+.B unfsd
+will look as if they are owned by the client's user. Other files will
+look as if they are owned by root. The same principle applies to
+group ownership.
+.TP
+.B \-b
+Enable brute force file searching. Normally, when you rename a file
+across several directories on an NFS volume, the filehandle for that
+file becomes stale. When this option is enabled,
+.B unfsd
+will attempt a recursive search on the relevant server filesystem to
+find the file referenced by the filehandle. This can have a huge
+performance impact as this will also happen for files that were
+really deleted (by another NFS client) instead of moved, and cannot be found.
+.TP
+.B \-l <addr>
+Bind to interface with specified address. The default is to bind to
+all local interfaces. 
+.TP
+.B \-d
+Debug mode. When this option is present,
+.B unfsd
+will not fork into the background at startup, and all messages that
+would normally go to the system log go to stdout instead.
+.TP
+.B \-r
+Report unreadable executables as readable. This applies both to
+returned attributes and ACCESS requests. Please note that READ
+requests for unreadable executables are always allowed, if 
+.B unfsd 
+is running as root, regardless of this option.
+.TP
+.B \-T
+Test exports file and exit. When this option is given,
+.B unfsd
+will try to parse the exports file and exit with status 0 if this
+is successful. If there is a syntax error in the exports file,
+a message is printed on standard error and
+.B unfsd
+exits with status 1.
+.SH SIGNALS
+.TP
+.BR "SIGTERM " "and " SIGINT
+will cause
+.B unfsd
+to unregister itself from the portmapper and exit.
+.TP
+.B SIGHUP
+will cause
+.B unfsd
+to re-read its configuration data. Currently, this means the program
+will query the
+.I passwd
+database to find out the user and group id of user
+.BR nobody .
+.B unfsd
+will also attempt to reload the exports file. If the exports file
+contains errors,
+.B unfsd
+sends a warning message to the system log and nothing is exported until
+the situation is corrected and another
+.B SIGHUP
+is sent.
+.TP
+.B SIGUSR1
+will cause
+.B unfsd
+to output statistics about its filehandle and file descriptor cache
+to the system log. For the filehandle cache, it will output the number
+of filehandles in the cache, the total number of cache accesses, and the
+number of hits and misses. For the file descriptor cache, it will output
+the number of currently held open READ and WRITE file descriptors.
+.SH "EXPORTS FILE"
+The exports file,
+.I /etc/exports
+by default, determines which directories on the server can be accessed
+from NFS clients. An example:
+
+.nf
+# sample NFS exports file
+/home            trusted(rw,no_root_squash) (ro)
+"/with spaces"   weirdo
+/usr             1.2.3.4(rw) 192.168.2.0/24(ro,all_squash)
+/home/foo        bar(rw) 10.0.0.0/255.0.0.0(root_squash)
+/home/joe        joes_pc(anonuid=1100,anongid=1100,rw,all_squash)
+.fi
+
+Comments start with a # character and cause the rest of the line to be
+ignored. Extremely long exports can be split across multiple lines by
+escaping the intermediate newlines with a backslash character.
+.P
+Each line starts with a directory that is to be exported. If
+the directory name contains whitespace, it must be enclosed in double
+quotes. To the right of the directory name, a list of client
+specifications can be given. If this list is missing, the directory
+is exported to everyone, using default options
+.RB ( ro " and " root_squash ")."
+.P
+If the directory name contains symbolic links, they are expanded. This
+means that you have to force
+.B unfsd
+to reload the exports file if the symlinks happen to change.
+.P
+Clients can be specified using either a hostname, an IP address, or
+an IP network. Networks can be given by specifying the number of leading 1
+bits in the netmask or by giving the full netmask. If the hostname is
+empty, the directory is exported to everyone.
+.P
+Options can follow a client specification and have to be enclosed
+in parenthesis, with the opening paren directly following the client
+name or address. If no options are given,
+.B ro
+and
+.B root_squash
+are enabled by default. The following options are supported by
+.BR unfsd :
+.TP
+.B root_squash
+Enable root squashing, mapping all NFS request done with a user id of
+0 to the user id of the
+.B nobody
+user. This option is enabled by default.
+.TP
+.B no_root_squash
+Disable root squashing. When this option is present, NFS requests done
+with a user id of 0 will be done as the
+.B root
+user of the server, effectively disabling all permissions checks.
+.TP
+.B all_squash
+Squash all users. When this option is present, all NFS requests will
+be done as the
+.B nobody
+user of the server.
+.TP
+.B no_all_squash
+Don't squash all users. This option is enabled by default.
+.TP
+.B rw
+Allow read and write access on the exported directory. When this option
+is present, clients are allowed to modify files and directories on
+the server.
+.TP
+.B ro
+Allow only read access on the exported directory. When this option
+is present, clients are not allowed to modify files and directories
+on the server. This option is enabled by default.
+.TP
+.B anonuid/anongid
+Sets the uid and gid for anonymous mounts for this share - by default the
+uid for nobody will be used, but using these options you can change this
+on a per-share basis.
+.TP
+.B secure
+Allow only mount requests coming from a source port below 1024. Using
+these ports requires super-user privileges on many operating systems.
+This option is enabled by default.
+.TP
+.B insecure
+Allow mount requests coming from any source port.
+.TP
+.B removable
+Consider this directory to be on a removable medium. When this option
+is present,
+.B unfsd
+will not keep files open across multiple read or write requests. This
+allows unmounting of the underlying filesystem on the server at any time.
+Also, 
+.B unfsd
+will not require that the exported path exists at startup or mount
+time. If the path does not exist, an empty directory will be presented
+to the client. This is useful for exporting mount points handled by
+autofs. 
+.TP
+.B fixed
+Consider this directory to be on a fixed medium. This options is enabled
+by default and allows
+.B unfsd
+to keep files open between multiple read or write requests.
+.TP
+.B password=<password>
+To be able to mount this export, the specified password is
+required. The password needs be given in the mount request,
+as in "mount yourhost:@password:gazonk/tmp /mnt". One time passwords
+are also supported. When using passwords, the file handles
+will include a hash of the password. This means that 
+.B if you change the password, all clients will need to remount this export. 
+See the file "doc/passwords.txt" in the source for more information.
+.PP
+If options not present on this list are encountered by
+.BR unfsd ,
+they are silently ignored.
+.SH BUGS
+There are a few possible race conditions with other processes on the
+server. They can happen if
+.B unfsd
+is performing an operation on a filesystem object while another
+process is simultaneously first (a) removing the object and then (b)
+creating a new object of the same name. If this happens,
+.B unfsd
+will attempt to perform the operation on the wrong, new object.
+The time window in which this can happen is small.
+.PP
+When a client does a CREATE EXCLUSIVE procedure call,
+.B unfsd
+stores the verifier data in the mtime and atime attributes of the
+created file. Malicious processes on the server could manipulate
+those attributes, breaking the semantics of the exclusive create
+operation. A process attempting to do so would need to be able
+to see the NFS network traffic.
+.PP
+unfsd always uses the "nohide" semantics, which means that clients
+will see all file systems mounted below the exported path. However,
+some NFS clients do not cope well with this situation as, for
+instance, it is then possible for two files in the one apparent
+filesystem to have the same inode number. To avoid this, make sure
+that the client mounts each exported file system. 
+.PP
+Due to the way
+.B unfsd
+operates, it needs execute (lookup) and read permission on all directories
+from the root directory all the way up to exported directories.
+For example, if
+.I /usr/share
+is exported,
+.B unfsd
+is going to need permission for
+.IR / ", " /usr ", and " /usr/share .
+Since root squashing can be in effect,
+.B unfsd
+may run as the nobody user, which normally means having to grant
+execute (lookup) and read permission for everybody on the server.
+In the above example,
+.B unfsd
+also needs permission to access
+.IR /usr/share/.. ,
+which can be different from
+.I /usr
+for some special setups (for example when using bind mounts under
+Linux).
+.SH FILES
+.TP 20
+.I /etc/exports
+Default exports file.
+.SH AUTHOR
+Pascal Schmidt
+.SH "SEE ALSO"
+.BR tags (7)
diff --git a/unfs3/unfsd.init b/unfs3/unfsd.init
new file mode 100755 (executable)
index 0000000..0f66d2a
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/bash
+# -*- mode: shell-script; coding: UTF-8 -*-
+#
+# chkconfig: 235 99 10
+# description: Start or stop the unfs3 server
+#
+### BEGIN INIT INFO
+# Provides: unfsd
+# Required-Start: $network
+# Required-Stop: $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Description: Start or stop the unfs3 server
+### END INIT INFO
+
+description="unfs3 NFS server"
+lockfile="/var/lock/subsys/unfsd"
+pidfile="/var/run/unfsd.pid"
+
+
+case "$1" in
+    'start')
+        echo "Starting" ${description}
+        /usr/sbin/unfsd -i ${pidfile}
+        RETVAL=$?
+        if [ "${RETVAL}" = "0" ]; then
+            touch ${lockfile} >/dev/null 2>&1
+        fi
+        ;;
+    'stop')
+        echo "Shutting down" ${description}
+        if [ -s ${pidfile} ]; then
+            pid=`cat ${pidfile}`
+            kill -TERM ${pid} 2>/dev/null
+            sleep 2
+            if kill -0 ${pid} 2>/dev/null; then
+                kill -KILL ${pid}
+            fi
+        fi
+        rm -f ${lockfile} ${pidfile}
+        ;;
+    'status')
+        if [ -s ${pidfile} ]; then
+                pid=`cat ${pidfile}`
+                if kill -0 ${pid} 2>/dev/null; then
+                    echo "${description} (pid ${pid}) is running"
+                    RETVAL=0
+               else
+                    echo "${description} is stopped"
+                    RETVAL=1
+               fi
+       else
+            echo "${description} is stopped"
+            RETVAL=1
+       fi
+       ;;
+    'restart')
+       /etc/init.d/unfsd stop && /etc/init.d/unfsd start
+       RETVAL=$?
+       ;;
+    'condrestart')
+       [ -f /var/lock/subsys/unfsd ] && /etc/init.d/unfsd stop && /etc/init.d/unfsd start
+       RETVAL=$?
+       ;;
+    *)
+       echo "Usage: $0 {start|stop|restart|condrestart|status}"
+       RETVAL=1
+       ;;
+esac
+exit $RETVAL
+
diff --git a/unfs3/user.c b/unfs3/user.c
new file mode 100644 (file)
index 0000000..241da1e
--- /dev/null
@@ -0,0 +1,293 @@
+
+/*
+ * UNFS3 user and group id handling
+ * (C) 2003, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#ifndef WIN32
+#include <pwd.h>
+#include <grp.h>
+#include <syslog.h>
+#include <unistd.h>
+#endif                                /* WIN32 */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <stdlib.h>
+
+#include "nfs.h"
+#include "mount.h"
+#include "daemon.h"
+#include "user.h"
+#include "backend.h"
+#include "Config/exports.h"
+
+/* user and group id we squash to */
+static uid_t squash_uid = 65534;
+static gid_t squash_gid = 65534;
+
+/* whether we can use seteuid/setegid */
+static int can_switch = TRUE;
+
+/*
+ * initialize group and user id used for squashing
+ */
+void get_squash_ids(void)
+{
+    backend_passwdstruct *passwd;
+
+    if (can_switch) {
+       passwd = backend_getpwnam("nobody");
+       if (passwd) {
+           squash_uid = passwd->pw_uid;
+           squash_gid = passwd->pw_gid;
+       } else {
+           squash_uid = 65534;
+           squash_gid = 65534;
+       }
+    }
+}
+
+/*
+ * mangle an id
+ */
+static int mangle(int id, int squash)
+{
+    if (!can_switch || (exports_opts & OPT_ALL_SQUASH))
+       return squash;
+    else if (exports_opts & OPT_NO_ROOT_SQUASH)
+       return id;
+    else if (id == 0)
+       return squash;
+    else
+       return id;
+}
+
+/*
+ * Mangle a given user id according to current settings
+ */
+int mangle_uid(int id)
+{
+    int squash = squash_uid;
+
+    if (exports_anonuid() != ANON_NOTSPECIAL)
+       squash = exports_anonuid();
+
+    return mangle(id, squash);
+}
+
+/*
+ * Mangle a given group id according to current settings
+ */
+int mangle_gid(int id)
+{
+    int squash = squash_gid;
+
+    if (exports_anongid() != ANON_NOTSPECIAL)
+       squash = exports_anongid();
+
+    return mangle(id, squash);
+}
+
+/*
+ * return user id of a request
+ */
+int get_uid(struct svc_req *req)
+{
+    struct authunix_parms *auth = (void *) req->rq_clntcred;
+    int squash = squash_uid;
+
+    if (exports_anonuid() != ANON_NOTSPECIAL)
+       squash = exports_anonuid();
+
+    if (req->rq_cred.oa_flavor == AUTH_UNIX)
+       return mangle(auth->aup_uid, squash);
+    else
+       return squash;                 /* fallback if no uid given */
+}
+
+/*
+ * return group id of a request
+ */
+static int get_gid(struct svc_req *req)
+{
+    struct authunix_parms *auth = (void *) req->rq_clntcred;
+    int squash = squash_gid;
+
+    if (exports_anongid() != ANON_NOTSPECIAL)
+       squash = exports_anongid();
+
+    if (req->rq_cred.oa_flavor == AUTH_UNIX)
+       return mangle(auth->aup_gid, squash);
+    else
+       return squash;                 /* fallback if no gid given */
+}
+
+/*
+ * check whether a request comes from a given user id
+ */
+int is_owner(int owner, struct svc_req *req)
+{
+    return (int) (owner == get_uid(req));
+}
+
+/*
+ * check if a request comes from somebody who has a given group id
+ */
+int has_group(int group, struct svc_req *req)
+{
+    struct authunix_parms *auth = (void *) req->rq_clntcred;
+    unsigned int i;
+
+    if (req->rq_cred.oa_flavor == AUTH_UNIX) {
+       if (mangle(auth->aup_gid, squash_gid) == group)
+           return TRUE;
+
+       /* search groups */
+       for (i = 0; i < auth->aup_len; i++)
+           if (mangle(auth->aup_gids[i], squash_gid) == group)
+               return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*
+ * switch to root
+ */
+void switch_to_root()
+{
+    if (!can_switch)
+       return;
+
+    backend_setegid(0);
+    backend_seteuid(0);
+}
+
+/*
+ * switch auxiliary group ids
+ */
+static int switch_groups(struct svc_req *req)
+{
+    struct authunix_parms *auth = (void *) req->rq_clntcred;
+    unsigned int i, max;
+
+    max = (auth->aup_len <= 32) ? auth->aup_len : 32;
+
+    for (i = 0; i < max; ++i) {
+       auth->aup_gids[i] = mangle(auth->aup_gids[i], squash_gid);
+    }
+
+    return backend_setgroups(max, auth->aup_gids);
+}
+
+/*
+ * switch user and group id to values listed in request
+ */
+void switch_user(struct svc_req *req)
+{
+    int uid, gid, aid;
+
+    if (!can_switch)
+       return;
+
+    if (opt_singleuser || (backend_getuid() != 0)) {
+       /* 
+        * have uid/gid functions behave correctly by squashing
+        * all user and group ids to the current values
+        *
+        * otherwise ACCESS would malfunction
+        */
+       squash_uid = backend_getuid();
+       squash_gid = backend_getgid();
+
+       can_switch = FALSE;
+       return;
+    }
+
+    backend_setegid(0);
+    backend_seteuid(0);
+    gid = backend_setegid(get_gid(req));
+    aid = switch_groups(req);
+    uid = backend_seteuid(get_uid(req));
+
+    if (uid == -1 || gid == -1 || aid == -1) {
+       logmsg(LOG_EMERG, "euid/egid switching failed, aborting");
+       daemon_exit(CRISIS);
+    }
+}
+
+/*
+ * re-switch to root for reading executable files
+ */
+void read_executable(struct svc_req *req, backend_statstruct buf)
+{
+    int have_exec = 0;
+
+    if (is_owner(buf.st_uid, req)) {
+       if (!(buf.st_mode & S_IRUSR) && (buf.st_mode & S_IXUSR))
+           have_exec = 1;
+    } else if (has_group(buf.st_gid, req)) {
+       if (!(buf.st_mode & S_IRGRP) && (buf.st_mode & S_IXGRP))
+           have_exec = 1;
+    } else {
+       if (!(buf.st_mode & S_IROTH) && (buf.st_mode & S_IXOTH))
+           have_exec = 1;
+    }
+
+    if (have_exec) {
+       backend_setegid(0);
+       backend_seteuid(0);
+    }
+}
+
+/*
+ * re-switch to root for reading owned file
+ */
+void read_by_owner(struct svc_req *req, backend_statstruct buf)
+{
+    int have_owner = 0;
+    int have_read = 0;
+
+    have_owner = is_owner(buf.st_uid, req);
+
+    if (have_owner && (buf.st_mode & S_IRUSR)) {
+       have_read = 1;
+    } else if (has_group(buf.st_gid, req) && (buf.st_mode & S_IRGRP)) {
+       have_read = 1;
+    } else if (buf.st_mode & S_IROTH) {
+       have_read = 1;
+    }
+
+    if (have_owner && !have_read) {
+       backend_setegid(0);
+       backend_seteuid(0);
+    }
+}
+
+/*
+ * re-switch to root for writing owned file
+ */
+void write_by_owner(struct svc_req *req, backend_statstruct buf)
+{
+    int have_owner = 0;
+    int have_write = 0;
+
+    have_owner = is_owner(buf.st_uid, req);
+
+    if (have_owner && (buf.st_mode & S_IWUSR)) {
+       have_write = 1;
+    } else if (has_group(buf.st_gid, req) && (buf.st_mode & S_IWGRP)) {
+       have_write = 1;
+    } else if (buf.st_mode & S_IWOTH) {
+       have_write = 1;
+    }
+
+    if (have_owner && !have_write) {
+       backend_setegid(0);
+       backend_seteuid(0);
+    }
+}
diff --git a/unfs3/user.h b/unfs3/user.h
new file mode 100644 (file)
index 0000000..0109d25
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * UNFS3 user and group id handling
+ * (C) 2003, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#ifndef UNFS3_USER_H
+#define UNFS3_USER_H
+
+#include "backend.h"
+
+int get_uid(struct svc_req *req);
+
+int mangle_uid(int id);
+int mangle_gid(int id);
+
+int is_owner(int owner, struct svc_req *req);
+int has_group(int group, struct svc_req *req);
+
+void get_squash_ids(void);
+
+void switch_to_root();
+void switch_user(struct svc_req *req);
+
+void read_executable(struct svc_req *req, backend_statstruct buf);
+void read_by_owner(struct svc_req *req, backend_statstruct buf);
+void write_by_owner(struct svc_req *req, backend_statstruct buf);
+
+#endif
diff --git a/unfs3/winsupport.c b/unfs3/winsupport.c
new file mode 100644 (file)
index 0000000..15c37b0
--- /dev/null
@@ -0,0 +1,1062 @@
+
+/*
+ * unfs3 Windows compatibility layer
+ * Copyright 2006 Peter Ã…strand <astrand@cendio.se> for Cendio AB
+ * see file LICENSE for license details
+ */
+
+#ifdef WIN32
+#define _WIN32_WINDOWS 0x0410         /* We require Windows 98 or later For
+                                         GetLongPathName */
+#include <errno.h>
+#include <stdio.h>
+#include "winsupport.h"
+#include "Config/exports.h"
+#include "daemon.h"
+#include <assert.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <direct.h>
+#include <dirent.h>
+#include <locale.h>
+
+#define MAX_NUM_DRIVES 26
+#define FT70SEC 11644473600LL         /* seconds between 1601-01-01 and
+                                         1970-01-01 */
+#define FT80SEC 315529200             /* seconds between 1970-01-01 and
+                                         1980-01-01 */
+
+#define wsizeof(x) (sizeof(x)/sizeof(wchar_t))
+
+typedef struct _fdname {
+    int fd;
+    char *name;
+    struct _fdname *next;
+} fdname;
+
+static fdname *fdnames = NULL;
+
+static char *get_fdname(int fd)
+{
+    fdname *fn;
+
+    for (fn = fdnames; fn; fn = fn->next) {
+       if (fn->fd == fd) {
+           return fn->name;
+           break;
+       }
+    }
+
+    assert(0);
+    return NULL;
+}
+
+static int add_fdname(int fd, const char *name)
+{
+    fdname *fn;
+
+    fn = malloc(sizeof(fdname));
+    if (!fn) {
+       logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
+       return -1;
+    }
+
+    fn->fd = fd;
+    fn->name = strdup(name);
+    fn->next = fdnames;
+    fdnames = fn;
+
+    return fd;
+}
+
+static void remove_fdname(int fd)
+{
+    fdname *fn, **prevnext = &fdnames;
+
+    for (fn = fdnames; fn; fn = fn->next) {
+       if (fn->fd == fd) {
+           *prevnext = fn->next;
+           free(fn->name);
+           free(fn);
+           break;
+       }
+       prevnext = &fn->next;
+    }
+}
+
+/* 
+ * The following UTF-8 validation is borrowed from
+ * ftp://ftp.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c.
+ */
+
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ * 
+ * Disclaimer
+ * 
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ * 
+ * Limitations on Rights to Redistribute This Code
+ * 
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
+};
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ *  length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns 0.  The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static int isLegalUTF8(const unsigned char *source, int length)
+{
+    unsigned char a;
+    const unsigned char *srcptr = source + length;
+
+    switch (length) {
+       default:
+           return 0;
+           /* Everything else falls through when "1"... */
+       case 4:
+           if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
+               return 0;
+       case 3:
+           if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
+               return 0;
+       case 2:
+           if ((a = (*--srcptr)) > 0xBF)
+               return 0;
+
+           switch (*source) {
+                   /* no fall-through in this inner switch */
+               case 0xE0:
+                   if (a < 0xA0)
+                       return 0;
+                   break;
+               case 0xED:
+                   if (a > 0x9F)
+                       return 0;
+                   break;
+               case 0xF0:
+                   if (a < 0x90)
+                       return 0;
+                   break;
+               case 0xF4:
+                   if (a > 0x8F)
+                       return 0;
+                   break;
+               default:
+                   if (a < 0x80)
+                       return 0;
+           }
+
+       case 1:
+           if (*source >= 0x80 && *source < 0xC2)
+               return 0;
+    }
+    if (*source > 0xF4)
+       return 0;
+    return 1;
+}
+
+/* End of code borrowed from ConvertUTF.c */
+
+int isLegalUTF8String(const unsigned char *source)
+{
+    const unsigned char *seq, *sourceend;
+    int seqlen;
+
+    sourceend = source + strlen(source);
+    seq = source;
+
+    while (seq < sourceend) {
+       seqlen = trailingBytesForUTF8[*seq] + 1;
+       if (!isLegalUTF8(seq, seqlen))
+           return 0;
+       seq += seqlen;
+    }
+
+    return 1;
+}
+
+/* Translate an internal representation of a path (like /c/home) to
+   a Windows path (like c:\home) */
+static wchar_t *intpath2winpath(const char *intpath)
+{
+    wchar_t *winpath;
+    int winpath_len;
+    wchar_t *slash;
+    const char *lastrootslash;
+    wchar_t *lastslash;
+    size_t intlen;
+
+    /* Verify that input is valid UTF-8. We cannot use MB_ERR_INVALID_CHARS
+       to MultiByteToWideChar, since it's only available in late versions of
+       Windows. */
+    if (!isLegalUTF8String(intpath)) {
+       logmsg(LOG_CRIT, "intpath2winpath: Illegal UTF-8 string:%s", intpath);
+       return NULL;
+    }
+
+    /* Skip over multiple root slashes for paths like ///home/john */
+    lastrootslash = intpath;
+    while (*lastrootslash == '/')
+       lastrootslash++;
+    if (lastrootslash != intpath)
+       lastrootslash--;
+
+    intlen = strlen(lastrootslash);
+    /* One extra for /c -> c:\ */
+    winpath_len = sizeof(wchar_t) * (intlen + 2);
+    winpath = malloc(winpath_len);
+    if (!winpath) {
+       logmsg(LOG_CRIT, "intpath2winpath: Unable to allocate memory");
+       return NULL;
+    }
+
+    if (!MultiByteToWideChar
+       (CP_UTF8, 0, lastrootslash, -1, winpath, winpath_len)) {
+       logmsg(LOG_CRIT, "intpath2winpath: MultiByteToWideChar failed");
+       return NULL;
+    }
+
+    /* If path ends with /.., chop of the last component. Eventually, we
+       might want to eliminate all occurances of .. */
+    lastslash = wcsrchr(winpath, '/');
+    if (!wcscmp(lastslash, L"/..")) {
+       *lastslash = '\0';
+       lastslash = wcsrchr(winpath, '/');
+       *lastslash = '\0';
+    }
+
+    /* Translate /x -> x:/ and /x/something -> x:/something */
+    if ((winpath[0] == '/') && winpath[1]) {
+       switch (winpath[2]) {
+           case '\0':
+               winpath[2] = '/';
+               winpath[3] = '\0';
+               /* fall through */
+
+           case '/':
+               winpath[0] = winpath[1];
+               winpath[1] = ':';
+               break;
+
+           default:
+               break;
+       }
+    }
+
+    while ((slash = wcschr(winpath, '/')) != NULL) {
+       *slash = '\\';
+    }
+
+    return winpath;
+}
+
+int win_seteuid(U(uid_t euid))
+{
+    return 0;
+}
+
+int win_setegid(U(gid_t egid))
+{
+    return 0;
+}
+
+int win_truncate(const char *path, off_t length)
+{
+    int fd, ret, saved_errno;
+
+    fd = win_open(path, O_WRONLY);
+    if (fd < 0)
+       return -1;
+    ret = chsize(fd, length);
+    saved_errno = errno;
+    win_close(fd);
+    errno = saved_errno;
+
+    return ret;
+}
+
+int win_chown(U(const char *path), U(uid_t owner), U(gid_t group))
+{
+    errno = EINVAL;
+    return -1;
+}
+
+int win_fchown(U(int fd), U(uid_t owner), U(gid_t group))
+{
+    errno = EINVAL;
+    return -1;
+}
+
+int win_fchmod(int fildes, mode_t mode)
+{
+    wchar_t *winpath;
+    int ret;
+
+    winpath = intpath2winpath(get_fdname(fildes));
+    if (!winpath) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    ret = _wchmod(winpath, mode);
+    free(winpath);
+    return ret;
+}
+
+int inet_aton(const char *cp, struct in_addr *addr)
+{
+    addr->s_addr = inet_addr(cp);
+    return (addr->s_addr == INADDR_NONE) ? 0 : 1;
+}
+
+/* 
+   If you need a good laugh, take a look at the "Suggested Interix
+   replacement" at:
+   http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnucmg/html/UCMGch10.asp
+*/
+ssize_t pread(int fd, void *buf, size_t count, off_t offset)
+{
+    ssize_t size;
+    off_t ret;
+
+    if ((ret = lseek(fd, offset, SEEK_SET)) < 0)
+       return -1;
+    size = read(fd, buf, count);
+    return size;
+}
+
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset)
+{
+    ssize_t size;
+    off_t ret;
+    HANDLE h;
+    FILETIME ft;
+    SYSTEMTIME st;
+    ULARGE_INTEGER fti;
+
+    if ((ret = lseek(fd, offset, SEEK_SET)) < 0)
+       return -1;
+    size = write(fd, buf, count);
+
+    /* Since we are using the CreationTime attribute as "ctime", we need to
+       update it. From RFC1813: "Writing to the file changes the ctime in
+       addition to the mtime." */
+    h = (HANDLE) _get_osfhandle(fd);
+    GetSystemTime(&st);
+    SystemTimeToFileTime(&st, &ft);
+    /* Ceil up to nearest even second */
+    fti.LowPart = ft.dwLowDateTime;
+    fti.HighPart = ft.dwHighDateTime;
+    fti.QuadPart = ((fti.QuadPart + 20000000 - 1) / 20000000) * 20000000;
+    ft.dwLowDateTime = fti.LowPart;
+    ft.dwHighDateTime = fti.HighPart;
+    if (!SetFileTime(h, &ft, NULL, NULL)) {
+       fprintf(stderr,
+               "warning: pwrite: SetFileTime failed with error %ld\n",
+               GetLastError());
+    }
+
+    return size;
+}
+
+void syslog(U(int priority), U(const char *format), ...)
+{
+    assert(0);
+}
+
+int win_init()
+{
+    WORD winsock_ver;
+    WSADATA wsadata;
+
+    /* Set up locale, so that string compares works correctly */
+    setlocale(LC_ALL, "");
+
+    /* Verify that -s is used */
+    if (!opt_singleuser) {
+       fprintf(stderr, "Single-user mode is required on this platform.\n");
+       exit(1);
+    }
+
+    /* Verify that -d is used */
+    if (opt_detach) {
+       fprintf(stderr,
+               "Foreground (debug) mode is required on this platform.\n");
+       exit(1);
+    }
+
+    /* init winsock */
+    winsock_ver = MAKEWORD(1, 1);
+    if (WSAStartup(winsock_ver, &wsadata)) {
+       fprintf(stderr, "Unable to initialise WinSock\n");
+       exit(1);
+    }
+    if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
+       fprintf(stderr, "WinSock version is incompatible with 1.1\n");
+       WSACleanup();
+       exit(1);
+    }
+
+    /* disable error popups, for example from drives not ready */
+    SetErrorMode(SEM_FAILCRITICALERRORS);
+
+    return 0;
+}
+
+void win_shutdown()
+{
+    WSACleanup();
+}
+
+/* Wrapper for Windows stat function, which provides
+   st_dev and st_ino. These are calculated as follows:
+
+   st_dev is set to the drive number (0=A 1=B ...). Our virtual root
+   "/" gets a st_dev of 0xff. 
+
+   st_ino is hashed from the full file path. Each half produces a 32
+   bit hash. These are concatenated to a 64 bit value. The risk that
+   st_ino is the same for two files on the system is, if I'm not
+   mistaken, b=pigeon(2**32, f)**2. For f=1000, b=1e-08. By using a 64
+   bit hash function this risk can be lowered. Possible future
+   enhancement.
+
+   pigeon() can be calculated in Python with:
+   
+   def pigeon(m, n):
+       res = 1.0
+       for i in range(m - n + 1, m):
+           res = res * i / m
+       return 1 - res
+*/
+int win_stat(const char *file_name, backend_statstruct * buf)
+{
+    wchar_t *winpath;
+    int ret;
+    wchar_t pathbuf[4096];
+    int retval;
+    size_t namelen;
+    wchar_t *splitpoint;
+    char savedchar;
+    struct _stat win_statbuf;
+
+    /* Special case: Our top-level virtual root, containing each drive
+       represented as a directory. Compare with "My Computer" etc. This
+       virtual root has a hardcoded hash value of 1, to simplify debugging
+       etc. */
+    if (!strcmp(file_name, "/")) {
+       buf->st_mode = S_IFDIR | S_IRUSR | S_IWUSR;
+       buf->st_nlink = MAX_NUM_DRIVES + 3;     /* 3 extra for: . .. / */
+       buf->st_uid = 1;
+       buf->st_gid = 1;
+       buf->st_rdev = 0;
+       buf->st_size = 4096;
+       buf->st_atime = 0;
+       buf->st_mtime = 0;
+       buf->st_ctime = 0;
+       buf->st_dev = 0xff;
+       buf->st_ino = 1;
+       return 0;
+    }
+
+    winpath = intpath2winpath(file_name);
+    if (!winpath) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    ret = _wstat(winpath, &win_statbuf);
+    if (ret < 0) {
+       free(winpath);
+       return ret;
+    }
+
+    /* Copy values to our struct */
+    buf->st_mode = win_statbuf.st_mode;
+    buf->st_nlink = win_statbuf.st_nlink;
+    buf->st_uid = win_statbuf.st_uid;
+    buf->st_gid = win_statbuf.st_gid;
+    buf->st_rdev = win_statbuf.st_rdev;
+    buf->st_size = win_statbuf.st_size;
+    buf->st_atime = win_statbuf.st_atime;
+    buf->st_mtime = win_statbuf.st_mtime;
+    buf->st_ctime = win_statbuf.st_ctime;
+    buf->st_blocks = win_statbuf.st_size / 512;
+
+    retval = GetFullPathNameW(winpath, wsizeof(pathbuf), pathbuf, NULL);
+    if (!retval) {
+       errno = ENOENT;
+       return -1;
+    }
+
+    /* Set st_dev to the drive number */
+    buf->st_dev = tolower(pathbuf[0]) - 'a';
+
+    /* GetLongPathName fails if called with only x:\, and drive x is not
+       ready. So, only call it for other paths. */
+    if (pathbuf[0] && wcscmp(pathbuf + 1, L":\\")) {
+       retval = GetLongPathNameW(pathbuf, pathbuf, wsizeof(pathbuf));
+       if (!retval || (unsigned) retval > wsizeof(pathbuf)) {
+           /* Strangely enough, GetLongPathName returns
+              ERROR_SHARING_VIOLATION for locked files, such as hiberfil.sys 
+            */
+           if (GetLastError() != ERROR_SHARING_VIOLATION) {
+               errno = ENAMETOOLONG;
+               return -1;
+           }
+       }
+    }
+
+    /* Hash st_ino, by splitting in two halves */
+    namelen = wcslen(pathbuf);
+    splitpoint = &pathbuf[namelen / 2];
+    savedchar = *splitpoint;
+    *splitpoint = '\0';
+    buf->st_ino = wfnv1a_32(pathbuf, 0);
+    assert(sizeof(buf->st_ino) == 8);
+    buf->st_ino = buf->st_ino << 32;
+    *splitpoint = savedchar;
+    buf->st_ino |= wfnv1a_32(splitpoint, 0);
+
+#if 0
+    fprintf(stderr,
+           "win_stat: file=%s, ret=%d, st_dev=0x%x, st_ino=0x%I64x\n",
+           file_name, ret, buf->st_dev, buf->st_ino);
+#endif
+    free(winpath);
+    return ret;
+}
+
+int win_open(const char *pathname, int flags, ...)
+{
+    va_list args;
+    mode_t mode;
+    int fd;
+    wchar_t *winpath;
+
+    va_start(args, flags);
+    mode = va_arg(args, int);
+
+    va_end(args);
+
+    winpath = intpath2winpath(pathname);
+    if (!winpath) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    fd = _wopen(winpath, flags | O_BINARY, mode);
+    free(winpath);
+    if (fd < 0) {
+       return fd;
+    }
+
+    return add_fdname(fd, pathname);
+
+}
+
+int win_close(int fd)
+{
+    remove_fdname(fd);
+    return close(fd);
+}
+
+int win_fstat(int fd, backend_statstruct * buf)
+{
+    return win_stat(get_fdname(fd), buf);
+}
+
+/*
+  opendir implementation which emulates a virtual root with the drive
+  letters presented as directories. 
+*/
+UNFS3_WIN_DIR *win_opendir(const char *name)
+{
+    wchar_t *winpath;
+    UNFS3_WIN_DIR *ret;
+
+    ret = malloc(sizeof(UNFS3_WIN_DIR));
+    if (!ret) {
+       logmsg(LOG_CRIT, "win_opendir: Unable to allocate memory");
+       return NULL;
+    }
+
+    if (!strcmp("/", name)) {
+       /* Emulate root */
+       ret->stream = NULL;
+       ret->currentdrive = 0;
+       ret->logdrives = GetLogicalDrives();
+    } else {
+       winpath = intpath2winpath(name);
+       if (!winpath) {
+           free(ret);
+           errno = EINVAL;
+           return NULL;
+       }
+
+       ret->stream = _wopendir(winpath);
+       free(winpath);
+       if (ret->stream == NULL) {
+           free(ret);
+           ret = NULL;
+       }
+    }
+
+    return ret;
+}
+
+struct dirent *win_readdir(UNFS3_WIN_DIR * dir)
+{
+    if (dir->stream == NULL) {
+       /* Emulate root */
+       for (; dir->currentdrive < MAX_NUM_DRIVES; dir->currentdrive++) {
+           if (dir->logdrives & 1 << dir->currentdrive)
+               break;
+       }
+
+       if (dir->currentdrive < MAX_NUM_DRIVES) {
+           dir->de.d_name[0] = 'a' + dir->currentdrive;
+           dir->de.d_name[1] = '\0';
+           dir->currentdrive++;
+           return &dir->de;
+       } else {
+           return NULL;
+       }
+    } else {
+       struct _wdirent *de;
+
+       de = _wreaddir(dir->stream);
+       if (!de) {
+           return NULL;
+       }
+
+       if (!WideCharToMultiByte
+           (CP_UTF8, 0, de->d_name, -1, dir->de.d_name,
+            sizeof(dir->de.d_name), NULL, NULL)) {
+           logmsg(LOG_CRIT, "win_readdir: WideCharToMultiByte failed");
+           return NULL;
+       }
+       return &dir->de;
+    }
+}
+
+int win_closedir(UNFS3_WIN_DIR * dir)
+{
+    if (dir->stream == NULL) {
+       free(dir);
+       return 0;
+    } else {
+       return _wclosedir(dir->stream);
+    }
+}
+
+void openlog(U(const char *ident), U(int option), U(int facility))
+{
+
+}
+
+char *win_realpath(const char *path, char *resolved_path)
+{
+    return normpath(path, resolved_path);
+}
+
+int win_readlink(U(const char *path), U(char *buf), U(size_t bufsiz))
+{
+    errno = ENOSYS;
+    return -1;
+}
+
+int win_mkdir(const char *pathname, U(mode_t mode))
+{
+    wchar_t *winpath;
+    int ret;
+
+    if (!strcmp("/", pathname)) {
+       /* Emulate root */
+       errno = EROFS;
+       return -1;
+    }
+
+    winpath = intpath2winpath(pathname);
+    if (!winpath) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    /* FIXME: Use mode */
+    ret = _wmkdir(winpath);
+    free(winpath);
+    return ret;
+}
+
+int win_symlink(U(const char *oldpath), U(const char *newpath))
+{
+    errno = ENOSYS;
+    return -1;
+}
+
+int win_mknod(U(const char *pathname), U(mode_t mode), U(dev_t dev))
+{
+    errno = ENOSYS;
+    return -1;
+}
+
+int win_mkfifo(U(const char *pathname), U(mode_t mode))
+{
+    errno = ENOSYS;
+    return -1;
+}
+
+int win_link(U(const char *oldpath), U(const char *newpath))
+{
+    errno = ENOSYS;
+    return -1;
+}
+
+int win_statvfs(const char *path, backend_statvfsstruct * buf)
+{
+    wchar_t *winpath;
+    DWORD SectorsPerCluster;
+    DWORD BytesPerSector;
+    DWORD NumberOfFreeClusters;
+    DWORD TotalNumberOfClusters;
+    ULARGE_INTEGER FreeBytesAvailable;
+    ULARGE_INTEGER TotalNumberOfBytes;
+    ULARGE_INTEGER TotalNumberOfFreeBytes;
+
+    if (!strcmp("/", path)) {
+       /* Emulate root */
+       buf->f_bsize = 1024;
+       buf->f_blocks = 1024;
+       buf->f_bfree = 0;
+       buf->f_bavail = 0;
+       buf->f_files = 1024;
+       buf->f_ffree = 0;
+       return 0;
+    }
+
+    winpath = intpath2winpath(path);
+    if (!winpath) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    winpath[3] = '\0';                /* Cut off after x:\ */
+
+    if (!GetDiskFreeSpaceW
+       (winpath, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters,
+        &TotalNumberOfClusters)) {
+       errno = EIO;
+       return -1;
+    }
+
+    if (!GetDiskFreeSpaceExW
+       (winpath, &FreeBytesAvailable, &TotalNumberOfBytes,
+        &TotalNumberOfFreeBytes)) {
+       errno = EIO;
+       return -1;
+    }
+
+    buf->f_bsize = BytesPerSector;
+    buf->f_blocks = TotalNumberOfBytes.QuadPart / BytesPerSector;
+    buf->f_bfree = TotalNumberOfFreeBytes.QuadPart / BytesPerSector;
+    buf->f_bavail = FreeBytesAvailable.QuadPart / BytesPerSector;
+    buf->f_files = buf->f_blocks / SectorsPerCluster;
+    buf->f_ffree = buf->f_bfree / SectorsPerCluster;
+    free(winpath);
+    return 0;
+}
+
+int win_remove(const char *pathname)
+{
+    wchar_t *winpath;
+    int ret;
+
+    if (!strcmp("/", pathname)) {
+       /* Emulate root */
+       errno = EROFS;
+       return -1;
+    }
+
+    winpath = intpath2winpath(pathname);
+    if (!winpath) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    ret = _wremove(winpath);
+    free(winpath);
+    return ret;
+}
+
+int win_chmod(const char *path, mode_t mode)
+{
+    wchar_t *winpath;
+    int ret;
+
+    if (!strcmp("/", path)) {
+       /* Emulate root */
+       errno = EROFS;
+       return -1;
+    }
+
+    winpath = intpath2winpath(path);
+    if (!winpath) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    ret = _wchmod(winpath, mode);
+    free(winpath);
+    return ret;
+}
+
+/* 
+   If creation is false, the LastAccessTime will be set according to
+   times->actime. Otherwise, CreationTime will be set. LastWriteTime
+   is always set according to times->modtime.
+*/
+static int win_utime_creation(const char *path, const struct utimbuf *times,
+                             int creation)
+{
+    wchar_t *winpath;
+    int ret = 0;
+    HANDLE h;
+    ULARGE_INTEGER fti;
+    FILETIME xtime, mtime;
+
+    if (!strcmp("/", path)) {
+       /* Emulate root */
+       errno = EROFS;
+       return -1;
+    }
+
+    winpath = intpath2winpath(path);
+    if (!winpath) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    /* Unfortunately, we cannot use utime(), since it doesn't support
+       directories. */
+    fti.QuadPart = UInt32x32To64(times->actime + FT70SEC, 10000000);
+    xtime.dwHighDateTime = fti.HighPart;
+    xtime.dwLowDateTime = fti.LowPart;
+    fti.QuadPart = UInt32x32To64(times->modtime + FT70SEC, 10000000);
+    mtime.dwHighDateTime = fti.HighPart;
+    mtime.dwLowDateTime = fti.LowPart;
+
+    h = CreateFileW(winpath, FILE_WRITE_ATTRIBUTES,
+                   FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+                   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+    if (!SetFileTime
+       (h, creation ? &xtime : NULL, creation ? NULL : &xtime, &mtime)) {
+       errno = EACCES;
+       ret = -1;
+    }
+
+    CloseHandle(h);
+    free(winpath);
+    return ret;
+}
+
+int win_utime(const char *path, const struct utimbuf *times)
+{
+    return win_utime_creation(path, times, FALSE);
+}
+
+int win_rmdir(const char *path)
+{
+    wchar_t *winpath;
+    int ret;
+
+    if (!strcmp("/", path)) {
+       /* Emulate root */
+       errno = EROFS;
+       return -1;
+    }
+
+    winpath = intpath2winpath(path);
+    if (!winpath) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    ret = _wrmdir(winpath);
+    free(winpath);
+    return ret;
+}
+
+int win_rename(const char *oldpath, const char *newpath)
+{
+    wchar_t *oldwinpath, *newwinpath;
+    int ret;
+
+    if (!strcmp("/", oldpath) && !strcmp("/", newpath)) {
+       /* Emulate root */
+       errno = EROFS;
+       return -1;
+    }
+
+    oldwinpath = intpath2winpath(oldpath);
+    if (!oldwinpath) {
+       errno = EINVAL;
+       return -1;
+    }
+    newwinpath = intpath2winpath(newpath);
+    if (!newwinpath) {
+       free(oldwinpath);
+       errno = EINVAL;
+       return -1;
+    }
+
+    ret = _wrename(oldwinpath, newwinpath);
+    free(oldwinpath);
+    free(newwinpath);
+    return ret;
+}
+
+int win_gen_nonce(char *nonce)
+{
+    HCRYPTPROV hCryptProv;
+
+    if (!CryptAcquireContext
+       (&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+       logmsg(LOG_ERR, "CryptAcquireContext failed with error 0x%lx",
+              GetLastError());
+       return -1;
+    }
+
+    if (!CryptGenRandom(hCryptProv, 32, nonce)) {
+       logmsg(LOG_ERR, "CryptGenRandom failed with error 0x%lx",
+              GetLastError());
+       return -1;
+    }
+
+    if (!CryptReleaseContext(hCryptProv, 0)) {
+       logmsg(LOG_ERR, "CryptReleaseContext failed with error 0x%lx",
+              GetLastError());
+       return -1;
+    }
+
+    return 0;
+}
+
+/* Just like strncasecmp, but compare two UTF8 strings. Limited to 4096 chars. */
+int win_utf8ncasecmp(const char *s1, const char *s2, size_t n)
+{
+    wchar_t ws1[4096], ws2[4096];
+    int converted;
+
+    /* Make sure input is valid UTF-8 */
+    if (!isLegalUTF8String(s1)) {
+       logmsg(LOG_CRIT, "win_utf8ncasecmp: Illegal UTF-8 string:%s", s1);
+       return -1;
+    }
+    if (!isLegalUTF8String(s2)) {
+       logmsg(LOG_CRIT, "win_utf8ncasecmp: Illegal UTF-8 string:%s", s2);
+       return -1;
+    }
+
+    /* Convert both strings to wide chars */
+    converted = MultiByteToWideChar(CP_UTF8, 0, s1, n, ws1, wsizeof(ws1));
+    if (!converted) {
+       logmsg(LOG_CRIT, "win_utf8ncasecmp: MultiByteToWideChar failed");
+       return -1;
+    }
+    ws1[converted] = '\0';
+    converted = MultiByteToWideChar(CP_UTF8, 0, s2, n, ws2, wsizeof(ws2));
+    if (!converted) {
+       logmsg(LOG_CRIT, "win_utf8ncasecmp: MultiByteToWideChar failed");
+       return 1;
+    }
+    ws2[converted] = '\0';
+
+    /* compare */
+    return _wcsicmp(ws1, ws2);
+}
+
+static void win_verf_to_ubuf(struct utimbuf *ubuf, createverf3 verf)
+{
+    ubuf->actime = verf[0] | verf[1] << 8 | verf[2] << 16 | verf[3] << 24;
+    ubuf->modtime = verf[4] | verf[5] << 8 | verf[6] << 16 | verf[7] << 24;
+
+    /* FAT can only store dates in the interval 1980-01-01 to 2107-12-31.
+       However, since the utime interface uses Epoch time, we are further
+       limited to 1980-01-01 to 2038-01-19, assuming 32 bit signed time_t.
+       math.log(2**31-1 - FT80SEC, 2) = 30.7, which means that we can only
+       use 30 bits. */
+    ubuf->actime &= 0x3fffffff;
+    ubuf->actime += FT80SEC;
+    ubuf->modtime &= 0x3fffffff;
+    ubuf->modtime += FT80SEC;
+    /* While FAT CreationTime has a resolution of 10 ms, WriteTime only has a 
+       resolution of 2 seconds. */
+    ubuf->modtime &= ~1;
+}
+
+int win_store_create_verifier(char *obj, createverf3 verf)
+{
+    struct utimbuf ubuf;
+
+    win_verf_to_ubuf(&ubuf, verf);
+    return win_utime_creation(obj, &ubuf, TRUE);
+}
+
+int win_check_create_verifier(backend_statstruct * buf, createverf3 verf)
+{
+    struct utimbuf ubuf;
+
+    win_verf_to_ubuf(&ubuf, verf);
+    return (buf->st_ctime == ubuf.actime && buf->st_mtime == ubuf.modtime);
+}
+
+#endif                                /* WIN32 */
diff --git a/unfs3/winsupport.h b/unfs3/winsupport.h
new file mode 100644 (file)
index 0000000..6aa295b
--- /dev/null
@@ -0,0 +1,131 @@
+
+/*
+ * unfs3 Windows compatibility
+ * Copyright 2006 Peter Ã…strand <astrand@cendio.se> for Cendio AB
+ * see file LICENSE for license details
+ */
+
+#ifdef WIN32
+#ifndef UNFS3_WINSUPPORT_H
+#define UNFS3_WINSUPPORT_H
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <utime.h>
+#include "nfs.h"
+
+#define LOG_EMERG       0       /* system is unusable */
+#define LOG_ALERT       1       /* action must be taken immediately */
+#define LOG_CRIT        2       /* critical conditions */
+#define LOG_ERR         3       /* error conditions */
+#define LOG_WARNING     4       /* warning conditions */
+#define LOG_NOTICE      5       /* normal but significant condition */
+#define LOG_INFO        6       /* informational */
+#define LOG_DEBUG       7       /* debug-level messages */
+#define LOG_CONS        0
+#define LOG_PID         0
+#define LOG_DAEMON      0
+#define closelog()      do { } while (0)
+
+#define O_NONBLOCK      0
+#define ELOOP           ENAMETOOLONG
+
+#define S_IRWXG 0
+#define S_IXGRP S_IRGRP
+#define S_IRGRP 0
+#define S_IWGRP 0
+
+#define S_IRWXO 0
+#define S_IXOTH S_IROTH 
+#define S_IROTH 0
+#define S_IWOTH 0
+
+#define S_IFLNK 0
+#define S_IFSOCK 0
+
+typedef int socklen_t;
+typedef uint32 uid_t;
+typedef uint32 gid_t;
+
+typedef struct _backend_statstruct
+{
+        uint32  st_dev;  
+        uint64  st_ino;  
+        _mode_t st_mode;
+        short   st_nlink;
+        uint32  st_uid;
+        uint32  st_gid;
+        _dev_t  st_rdev;
+        _off_t  st_size;
+        short   st_blksize;
+        _off_t  st_blocks;
+        time_t  st_atime;
+        time_t  st_mtime;
+        time_t  st_ctime;
+} backend_statstruct;
+
+typedef struct _backend_passwdstruct
+{
+    uid_t   pw_uid;
+    gid_t   pw_gid;
+} backend_passwdstruct;
+
+/* Only includes fields actually used by unfs3 */
+typedef struct _backend_statvfsstruct
+{
+        unsigned long  f_bsize;    /* file system block size */
+        uint64         f_blocks;   /* size of fs in f_frsize units */
+        uint64         f_bfree;    /* # free blocks */
+        uint64         f_bavail;   /* # free blocks for non-root */
+        uint64         f_files;    /* # inodes */
+        uint64         f_ffree;    /* # free inodes */
+} backend_statvfsstruct;
+
+typedef struct _UNFS3_WIN_DIR
+{
+    _WDIR *stream; /* Windows DIR stream. NULL means root emulation */
+    uint32 currentdrive; /* Next drive to check/return */
+    struct dirent de;
+    DWORD logdrives;
+} UNFS3_WIN_DIR;
+
+int inet_aton(const char *cp, struct in_addr *addr);
+ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
+void syslog(int priority, const char *format, ...);
+
+int win_seteuid(uid_t euid);
+int win_setegid(gid_t egid);
+int win_truncate(const char *path, off_t length);
+int win_chown(const char *path, uid_t owner, gid_t group);
+int win_fchown(int fd, uid_t owner, gid_t group);
+int win_fchmod(int fildes, mode_t mode);
+int win_stat(const char *file_name, backend_statstruct *buf);
+int win_fstat(int fd, backend_statstruct *buf);
+int win_open(const char *pathname, int flags, ...);
+int win_close(int fd);
+UNFS3_WIN_DIR *win_opendir(const char *name);
+struct dirent *win_readdir(UNFS3_WIN_DIR *dir);
+int win_closedir(UNFS3_WIN_DIR *dir);
+int win_init();
+void openlog(const char *ident, int option, int facility);
+char *win_realpath(const char *path, char *resolved_path);
+int win_readlink(const char *path, char *buf, size_t bufsiz);
+int win_mkdir(const char *pathname, mode_t mode);
+int win_symlink(const char *oldpath, const char *newpath);
+int win_mknod(const char *pathname, mode_t mode, dev_t dev);
+int win_mkfifo(const char *pathname, mode_t mode);
+int win_link(const char *oldpath, const char *newpath);
+int win_statvfs(const char *path, backend_statvfsstruct *buf);
+int win_remove(const char *pathname);
+int win_chmod(const char *path, mode_t mode);
+int win_utime(const char *path, const struct utimbuf *times);
+int win_rmdir(const char *path);
+int win_rename(const char *oldpath, const char *newpath);
+int win_gen_nonce(char *nonce);
+int win_utf8ncasecmp(const char *s1, const char *s2, size_t n);
+int win_store_create_verifier(char *obj, createverf3 verf);
+int win_check_create_verifier(backend_statstruct * buf, createverf3 verf);
+
+#endif /* UNFS3_WINSUPPORT_H */
+#endif /* WIN32 */
diff --git a/unfs3/xdr.c b/unfs3/xdr.c
new file mode 100644 (file)
index 0000000..21e5ea7
--- /dev/null
@@ -0,0 +1,1656 @@
+
+/*
+ * UNFS3 XDR routines
+ * Generated by rpcgen
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#ifndef WIN32
+#include <netinet/in.h>
+#endif                                /* WIN32 */
+
+#if HAVE_XDR_U_INT64_T == 1
+#define xdr_uint64_t xdr_u_int64_t
+#undef HAVE_XDR_UINT64_T
+#define HAVE_XDR_UINT64_T 1
+#endif
+
+#include "mount.h"
+#include "nfs.h"
+#include "xdr.h"
+
+bool_t xdr_fhandle3(XDR * xdrs, fhandle3 * objp)
+{
+    if (!xdr_bytes
+       (xdrs, (char **) &objp->fhandle3_val, (u_int *) & objp->fhandle3_len,
+        FHSIZE3))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_mountstat3(XDR * xdrs, mountstat3 * objp)
+{
+    if (!xdr_enum(xdrs, (enum_t *) objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_mountres3_ok(XDR * xdrs, mountres3_ok * objp)
+{
+    if (!xdr_fhandle3(xdrs, &objp->fhandle))
+       return FALSE;
+    if (!xdr_array
+       (xdrs, (char **) (void *) &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)
+{
+    if (!xdr_mountstat3(xdrs, &objp->fhs_status))
+       return FALSE;
+    switch (objp->fhs_status) {
+       case 0:
+           if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
+               return FALSE;
+           break;
+       default:
+           break;
+    }
+    return TRUE;
+}
+
+bool_t xdr_dirpath(XDR * xdrs, dirpath * objp)
+{
+    if (!xdr_string(xdrs, objp, MNTPATHLEN))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_name(XDR * xdrs, name * objp)
+{
+    if (!xdr_string(xdrs, objp, MNTNAMLEN))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_mountlist(XDR * xdrs, mountlist * objp)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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_filename(XDR * xdrs, filename * objp)
+{
+    if (!xdr_string(xdrs, objp, NFS_MAXNAMLEN))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_nfspath(XDR * xdrs, nfspath * objp)
+{
+    if (!xdr_string(xdrs, objp, NFS_MAXPATHLEN))
+       return FALSE;
+    return TRUE;
+}
+
+#if HAVE_XDR_UINT64 == 0
+#if HAVE_XDR_UINT64_T == 1
+bool_t xdr_uint64(XDR * xdrs, uint64 * objp)
+{
+    if (!xdr_uint64_t(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+#else
+bool_t xdr_uint64(XDR * xdrs, uint64 * objp)
+{
+    char buf[8];
+
+    if (xdrs->x_op == XDR_ENCODE) {
+       buf[0] = (*objp >> 56) & 0xFF;
+       buf[1] = (*objp >> 48) & 0xFF;
+       buf[2] = (*objp >> 40) & 0xFF;
+       buf[3] = (*objp >> 32) & 0xFF;
+       buf[4] = (*objp >> 24) & 0xFF;
+       buf[5] = (*objp >> 16) & 0xFF;
+       buf[6] = (*objp >> 8) & 0xFF;
+       buf[7] = *objp & 0xFF;
+       if (!xdr_opaque(xdrs, buf, 8))
+           return FALSE;
+       return TRUE;
+    } else if (xdrs->x_op == XDR_DECODE) {
+       uint32 *top = (void *) &buf[0];
+       uint32 *bottom = (void *) &buf[4];
+
+       if (!xdr_opaque(xdrs, buf, 8))
+           return FALSE;
+       *objp = (uint64) (ntohl(*top)) << 32 | ntohl(*bottom);
+       return TRUE;
+    }
+
+    if (!xdr_opaque(xdrs, buf, 8))
+       return FALSE;
+    return TRUE;
+}
+#endif
+#endif
+
+#if HAVE_XDR_UINT32 == 0 && HAVE_XDR_U_LONG == 1
+bool_t xdr_uint32(XDR * xdrs, uint32 * objp)
+{
+    if (!xdr_u_long(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+#endif
+
+#if HAVE_XDR_INT32 == 0 && HAVE_XDR_LONG == 1
+bool_t xdr_int32(XDR * xdrs, int32 * objp)
+{
+    if (!xdr_long(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+#endif
+
+bool_t xdr_filename3(XDR * xdrs, filename3 * objp)
+{
+    if (!xdr_string(xdrs, objp, ~0))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_nfspath3(XDR * xdrs, nfspath3 * objp)
+{
+    if (!xdr_string(xdrs, objp, ~0))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_fileid3(XDR * xdrs, fileid3 * objp)
+{
+    if (!xdr_uint64(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_cookie3(XDR * xdrs, cookie3 * objp)
+{
+    if (!xdr_uint64(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_cookieverf3(XDR * xdrs, cookieverf3 objp)
+{
+    if (!xdr_opaque(xdrs, objp, NFS3_COOKIEVERFSIZE))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_createverf3(XDR * xdrs, createverf3 objp)
+{
+    if (!xdr_opaque(xdrs, objp, NFS3_CREATEVERFSIZE))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_writeverf3(XDR * xdrs, writeverf3 objp)
+{
+    if (!xdr_opaque(xdrs, objp, NFS3_WRITEVERFSIZE))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_uid3(XDR * xdrs, uid3 * objp)
+{
+    if (!xdr_uint32(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_gid3(XDR * xdrs, gid3 * objp)
+{
+    if (!xdr_uint32(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_size3(XDR * xdrs, size3 * objp)
+{
+    if (!xdr_uint64(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_offset3(XDR * xdrs, offset3 * objp)
+{
+    if (!xdr_uint64(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_mode3(XDR * xdrs, mode3 * objp)
+{
+    if (!xdr_uint32(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_count3(XDR * xdrs, count3 * objp)
+{
+    if (!xdr_uint32(xdrs, objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_nfsstat3(XDR * xdrs, nfsstat3 * objp)
+{
+    if (!xdr_enum(xdrs, (enum_t *) objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_ftype3(XDR * xdrs, ftype3 * objp)
+{
+    if (!xdr_enum(xdrs, (enum_t *) objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_specdata3(XDR * xdrs, specdata3 * objp)
+{
+    if (!xdr_uint32(xdrs, &objp->specdata1))
+       return FALSE;
+    if (!xdr_uint32(xdrs, &objp->specdata2))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_nfs_fh3(XDR * xdrs, nfs_fh3 * objp)
+{
+    if (!xdr_bytes
+       (xdrs, (char **) &objp->data.data_val,
+        (u_int *) & objp->data.data_len, NFS3_FHSIZE))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_nfstime3(XDR * xdrs, nfstime3 * objp)
+{
+    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)
+{
+    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)
+{
+    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_wcc_attr(XDR * xdrs, wcc_attr * objp)
+{
+    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)
+{
+    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)
+{
+    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_post_op_fh3(XDR * xdrs, post_op_fh3 * objp)
+{
+    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_time_how(XDR * xdrs, time_how * objp)
+{
+    if (!xdr_enum(xdrs, (enum_t *) objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_set_mode3(XDR * xdrs, set_mode3 * objp)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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_diropargs3(XDR * xdrs, diropargs3 * objp)
+{
+    if (!xdr_nfs_fh3(xdrs, &objp->dir))
+       return FALSE;
+    if (!xdr_filename3(xdrs, &objp->name))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_GETATTR3args(XDR * xdrs, GETATTR3args * objp)
+{
+    if (!xdr_nfs_fh3(xdrs, &objp->object))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_GETATTR3resok(XDR * xdrs, GETATTR3resok * objp)
+{
+    if (!xdr_fattr3(xdrs, &objp->obj_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_GETATTR3res(XDR * xdrs, GETATTR3res * objp)
+{
+    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_sattrguard3(XDR * xdrs, sattrguard3 * objp)
+{
+    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)
+{
+    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)
+{
+    if (!xdr_wcc_data(xdrs, &objp->obj_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_SETATTR3resfail(XDR * xdrs, SETATTR3resfail * objp)
+{
+    if (!xdr_wcc_data(xdrs, &objp->obj_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_SETATTR3res(XDR * xdrs, SETATTR3res * objp)
+{
+    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;
+}
+
+bool_t xdr_LOOKUP3args(XDR * xdrs, LOOKUP3args * objp)
+{
+    if (!xdr_diropargs3(xdrs, &objp->what))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_LOOKUP3resok(XDR * xdrs, LOOKUP3resok * objp)
+{
+    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)
+{
+    if (!xdr_post_op_attr(xdrs, &objp->dir_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_LOOKUP3res(XDR * xdrs, LOOKUP3res * objp)
+{
+    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_ACCESS3args(XDR * xdrs, ACCESS3args * objp)
+{
+    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)
+{
+    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)
+{
+    if (!xdr_post_op_attr(xdrs, &objp->obj_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_ACCESS3res(XDR * xdrs, ACCESS3res * objp)
+{
+    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_READLINK3args(XDR * xdrs, READLINK3args * objp)
+{
+    if (!xdr_nfs_fh3(xdrs, &objp->symlink))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_READLINK3resok(XDR * xdrs, READLINK3resok * objp)
+{
+    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)
+{
+    if (!xdr_post_op_attr(xdrs, &objp->symlink_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_READLINK3res(XDR * xdrs, READLINK3res * objp)
+{
+    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_READ3args(XDR * xdrs, READ3args * objp)
+{
+    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)
+{
+    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)
+{
+    if (!xdr_post_op_attr(xdrs, &objp->file_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_READ3res(XDR * xdrs, READ3res * objp)
+{
+    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_stable_how(XDR * xdrs, stable_how * objp)
+{
+    if (!xdr_enum(xdrs, (enum_t *) objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_WRITE3args(XDR * xdrs, WRITE3args * objp)
+{
+    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_WRITE3resok(XDR * xdrs, WRITE3resok * objp)
+{
+    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)
+{
+    if (!xdr_wcc_data(xdrs, &objp->file_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_WRITE3res(XDR * xdrs, WRITE3res * objp)
+{
+    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_createmode3(XDR * xdrs, createmode3 * objp)
+{
+    if (!xdr_enum(xdrs, (enum_t *) objp))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_createhow3(XDR * xdrs, createhow3 * objp)
+{
+    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)
+{
+    if (!xdr_diropargs3(xdrs, &objp->where))
+       return FALSE;
+    if (!xdr_createhow3(xdrs, &objp->how))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_CREATE3resok(XDR * xdrs, CREATE3resok * objp)
+{
+    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)
+{
+    if (!xdr_wcc_data(xdrs, &objp->dir_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_CREATE3res(XDR * xdrs, CREATE3res * objp)
+{
+    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_MKDIR3args(XDR * xdrs, MKDIR3args * objp)
+{
+    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)
+{
+    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)
+{
+    if (!xdr_wcc_data(xdrs, &objp->dir_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_MKDIR3res(XDR * xdrs, MKDIR3res * objp)
+{
+    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_symlinkdata3(XDR * xdrs, symlinkdata3 * objp)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    if (!xdr_wcc_data(xdrs, &objp->dir_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_SYMLINK3res(XDR * xdrs, SYMLINK3res * objp)
+{
+    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_devicedata3(XDR * xdrs, devicedata3 * objp)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    if (!xdr_wcc_data(xdrs, &objp->dir_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_MKNOD3res(XDR * xdrs, MKNOD3res * objp)
+{
+    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_REMOVE3args(XDR * xdrs, REMOVE3args * objp)
+{
+    if (!xdr_diropargs3(xdrs, &objp->object))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_REMOVE3resok(XDR * xdrs, REMOVE3resok * objp)
+{
+    if (!xdr_wcc_data(xdrs, &objp->dir_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_REMOVE3resfail(XDR * xdrs, REMOVE3resfail * objp)
+{
+    if (!xdr_wcc_data(xdrs, &objp->dir_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_REMOVE3res(XDR * xdrs, REMOVE3res * objp)
+{
+    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_RMDIR3args(XDR * xdrs, RMDIR3args * objp)
+{
+    if (!xdr_diropargs3(xdrs, &objp->object))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_RMDIR3resok(XDR * xdrs, RMDIR3resok * objp)
+{
+    if (!xdr_wcc_data(xdrs, &objp->dir_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_RMDIR3resfail(XDR * xdrs, RMDIR3resfail * objp)
+{
+    if (!xdr_wcc_data(xdrs, &objp->dir_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_RMDIR3res(XDR * xdrs, RMDIR3res * objp)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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_LINK3args(XDR * xdrs, LINK3args * objp)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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_READDIR3args(XDR * xdrs, READDIR3args * objp)
+{
+    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)
+{
+    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 **) (void *) &objp->nextentry, sizeof(entry3),
+        (xdrproc_t) xdr_entry3))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_dirlist3(XDR * xdrs, dirlist3 * objp)
+{
+    if (!xdr_pointer
+       (xdrs, (char **) (void *) &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)
+{
+    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)
+{
+    if (!xdr_post_op_attr(xdrs, &objp->dir_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_READDIR3res(XDR * xdrs, READDIR3res * objp)
+{
+    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_READDIRPLUS3args(XDR * xdrs, READDIRPLUS3args * objp)
+{
+    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)
+{
+    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 **) (void *) &objp->nextentry, sizeof(entryplus3),
+        (xdrproc_t) xdr_entryplus3))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_dirlistplus3(XDR * xdrs, dirlistplus3 * objp)
+{
+    if (!xdr_pointer
+       (xdrs, (char **) (void *) &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)
+{
+    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)
+{
+    if (!xdr_post_op_attr(xdrs, &objp->dir_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_READDIRPLUS3res(XDR * xdrs, READDIRPLUS3res * objp)
+{
+    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_FSSTAT3args(XDR * xdrs, FSSTAT3args * objp)
+{
+    if (!xdr_nfs_fh3(xdrs, &objp->fsroot))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_FSSTAT3resok(XDR * xdrs, FSSTAT3resok * objp)
+{
+    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)
+{
+    if (!xdr_post_op_attr(xdrs, &objp->obj_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_FSSTAT3res(XDR * xdrs, FSSTAT3res * objp)
+{
+    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_FSINFO3args(XDR * xdrs, FSINFO3args * objp)
+{
+    if (!xdr_nfs_fh3(xdrs, &objp->fsroot))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_FSINFO3resok(XDR * xdrs, FSINFO3resok * objp)
+{
+    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)
+{
+    if (!xdr_post_op_attr(xdrs, &objp->obj_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_FSINFO3res(XDR * xdrs, FSINFO3res * objp)
+{
+    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_PATHCONF3args(XDR * xdrs, PATHCONF3args * objp)
+{
+    if (!xdr_nfs_fh3(xdrs, &objp->object))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_PATHCONF3resok(XDR * xdrs, PATHCONF3resok * objp)
+{
+    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)
+{
+    if (!xdr_post_op_attr(xdrs, &objp->obj_attributes))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_PATHCONF3res(XDR * xdrs, PATHCONF3res * objp)
+{
+    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_COMMIT3args(XDR * xdrs, COMMIT3args * objp)
+{
+    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)
+{
+    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)
+{
+    if (!xdr_wcc_data(xdrs, &objp->file_wcc))
+       return FALSE;
+    return TRUE;
+}
+
+bool_t xdr_COMMIT3res(XDR * xdrs, COMMIT3res * objp)
+{
+    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;
+}
diff --git a/unfs3/xdr.h b/unfs3/xdr.h
new file mode 100644 (file)
index 0000000..3296c59
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * UNFS3 XDR routine prototypes
+ * Generated by rpcgen
+ */
+
+#ifndef UNFS3_XDR_H
+#define UNFS3_XDR_H
+
+/* MOUNT protocol */
+
+extern bool_t xdr_fhandle3 (XDR *, fhandle3*);
+extern bool_t xdr_mountstat3 (XDR *, mountstat3*);
+extern bool_t xdr_mountres3_ok (XDR *, mountres3_ok*);
+extern bool_t xdr_mountres3 (XDR *, mountres3*);
+extern bool_t xdr_dirpath (XDR *, dirpath*);
+extern bool_t xdr_name (XDR *, name*);
+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*);
+
+/* NFS protocol */
+
+extern bool_t xdr_filename (XDR *, filename*);
+extern bool_t xdr_nfspath (XDR *, nfspath*);
+#if HAVE_XDR_UINT64 == 0
+extern bool_t xdr_uint64 (XDR *, uint64*);
+#endif
+#if HAVE_XDR_UINT32 == 0
+extern bool_t xdr_uint32 (XDR *, uint32*);
+#endif
+#if HAVE_XDR_INT32 == 0
+extern bool_t xdr_int32 (XDR *, int32*);
+#endif
+extern bool_t xdr_filename3 (XDR *, filename3*);
+extern bool_t xdr_nfspath3 (XDR *, nfspath3*);
+extern bool_t xdr_fileid3 (XDR *, fileid3*);
+extern bool_t xdr_cookie3 (XDR *, cookie3*);
+extern bool_t xdr_cookieverf3 (XDR *, cookieverf3);
+extern bool_t xdr_createverf3 (XDR *, createverf3);
+extern bool_t xdr_writeverf3 (XDR *, writeverf3);
+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_offset3 (XDR *, offset3*);
+extern bool_t xdr_mode3 (XDR *, mode3*);
+extern bool_t xdr_count3 (XDR *, count3*);
+extern bool_t xdr_nfsstat3 (XDR *, nfsstat3*);
+extern bool_t xdr_ftype3 (XDR *, ftype3*);
+extern bool_t xdr_specdata3 (XDR *, specdata3*);
+extern bool_t xdr_nfs_fh3 (XDR *, nfs_fh3*);
+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_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_post_op_fh3 (XDR *, post_op_fh3*);
+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_diropargs3 (XDR *, diropargs3*);
+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_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*);
+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_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_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_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_stable_how (XDR *, stable_how*);
+extern bool_t xdr_WRITE3args (XDR *, WRITE3args*);
+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_createmode3 (XDR *, createmode3*);
+extern bool_t xdr_createhow3 (XDR *, createhow3*);
+extern bool_t xdr_CREATE3args (XDR *, CREATE3args*);
+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_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_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_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_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_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_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_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_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_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_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_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_COMMIT3args (XDR *, COMMIT3args*);
+extern bool_t xdr_COMMIT3resok (XDR *, COMMIT3resok*);
+extern bool_t xdr_COMMIT3resfail (XDR *, COMMIT3resfail*);
+extern bool_t xdr_COMMIT3res (XDR *, COMMIT3res*);
+
+#endif