Index: rtemsbsd/include/librtemsNfs.h
===================================================================
--- rtemsbsd/include/librtemsNfs.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/include/librtemsNfs.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,234 @@
+/**
+ * @file
+ *
+ * @brief Public Interface to the NFS Client Library for RTEMS
+ *
+ * @ingroup rtems-nfsclient
+ */
+
+/*
+ * Author: Till Straumann <strauman@slac.stanford.edu> 2002-2003
+ *
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
+ * 	   Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The NFS-2 client implementation for RTEMS was produced by
+ *     the Stanford Linear Accelerator Center, Stanford University,
+ * 	   under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software.  Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#ifndef LIB_RTEMS_NFS_CLIENT_H
+#define LIB_RTEMS_NFS_CLIENT_H
+
+/**
+ * @defgroup rtems-nfsclient NFS Client Library
+ *
+ * @ingroup nfsclient
+ * @{
+ */
+
+#ifdef	HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/libio_.h>
+#include <rtems/seterr.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** RPCIO driver interface.
+ * If you need RPCIO for other purposes than NFS
+ * you may want to include <rpcio.h>
+#include "rpcio.h"
+ */
+
+/** Priority of daemon; may be setup prior to calling rpcUdpInit();
+ * otherwise the network task priority from the rtems_bsdnet_config
+ * is used...
+ */
+extern rtems_task_priority rpciodPriority;
+
+#ifdef RTEMS_SMP
+/** CPU affinity of daemon; may be setup prior to calling rpcUdpInit();
+ * otherwise the network task CPU affinity from the rtems_bsdnet_config
+ * is used...
+ */
+extern const cpu_set_t *rpciodCpuset;
+extern size_t rpciodCpusetSize;
+#endif
+
+/**
+ * @brief Sets the XIDs of the RPC transaction hash table.
+ *
+ * The active RPC transactions are stored in a hash table.  Each table entry
+ * contains the XID of its corresponding transaction.  The XID consists of two
+ * parts.  The lower part is determined by the hash table index.  The upper
+ * part is incremented in each send operation.
+ *
+ * This function sets the upper part of the XID in all hash table entries.
+ * This can be used to ensure that the XIDs are not reused in a short interval
+ * for example during a boot process or after resets.
+ *
+ * @param[in] xid The upper part is used to set the upper XID part of the hash
+ * table entries.
+ */
+void
+rpcSetXIDs(uint32_t xid);
+
+/** Initialize the driver.
+ *
+ * Note, called in nfsfs initialise when mount is called.
+ *
+ * @retval 0 on success, -1 on failure
+ */
+int
+rpcUdpInit(void);
+
+/**
+ * @brief RPC cleanup and stop.
+ *
+ * @retval 0 on success, nonzero if still in use
+ */
+int
+rpcUdpCleanup(void);
+
+/** NFS driver interface */
+
+/**
+ * @brief Initialize the NFS driver.
+ *
+ * The RPCIO driver must have been initialized prior to calling this.
+ *
+ * Note, called in nfsfs initialise when mount is called with defaults.
+ *
+ * ARGS:	depth of the small and big
+ * 			transaction pools, i.e. how
+ * 			many transactions (buffers)
+ * 			should always be kept around.
+ *
+ * 			(If more transactions are needed,
+ * 			they are created and destroyed
+ * 			on the fly).
+ *
+ * 			Supply zero values to have the
+ * 			driver chose reasonable defaults.
+ *
+ * @retval 0 Successful operation.
+ * @retval -1 An error occurred.  The errno is set to indicate the error.
+ */
+int
+nfsInit(int smallPoolDepth, int bigPoolDepth);
+
+/**
+ * @brief Driver cleanup code.
+ *
+ * @retval 0 on success, nonzero if still in use
+ */
+int
+nfsCleanup(void);
+
+/**
+ * @brief Dump a list of the currently mounted NFS to a file.
+ *
+ * Dump a list of the currently mounted NFS to a file
+ * (stdout is used in case f==NULL)
+ */
+int
+nfsMountsShow(FILE *f);
+
+/**
+ * @brief Filesystem mount table mount handler.
+ *
+ * Filesystem mount table mount handler. Do not call, use the mount call.
+ */
+int
+rtems_nfs_initialize(rtems_filesystem_mount_table_entry_t *mt_entry,
+                     const void                           *data);
+
+/**
+ * @brief A utility routine to find the path leading to a
+ * rtems_filesystem_location_info_t node.
+ *
+ * This should really be present in libcsupport...
+ *
+ * @param[in] 'loc' and a buffer 'buf' (length 'len') to hold the path.
+ *
+ * @param[out] path copied into 'buf'
+ *
+ * @retval 0 on success, RTEMS error code on error.
+ */
+rtems_status_code
+rtems_filesystem_resolve_location(char *buf, int len, rtems_filesystem_location_info_t *loc);
+
+/**
+ * @brief Set the timeout (initial default: 10s) for NFS and mount calls.
+ *
+ * Set the timeout (initial default: 10s) for NFS and mount calls.
+ *
+ * @retval 0 on success, nonzero if the requested timeout is less than
+ * a clock tick or if the system clock rate cannot be determined.
+ */
+
+int
+nfsSetTimeout(uint32_t timeout_ms);
+
+/** Read current timeout (in milliseconds) */
+uint32_t
+nfsGetTimeout(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
+#endif
Index: rtemsbsd/nfsclient/LICENSE
===================================================================
--- rtemsbsd/nfsclient/LICENSE	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/LICENSE	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,44 @@
+/* 
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
+ * 	   Stanford Linear Accelerator Center, Stanford University.
+ * 
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The NFS-2 client implementation for RTEMS was produced by
+ *     the Stanford Linear Accelerator Center, Stanford University,
+ * 	   under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ * 
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ * 
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ * 
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software.  Hence, anyone may
+ * freely use it for any purpose without restriction.  
+ * 
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ * 
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */ 
Index: rtemsbsd/nfsclient/README
===================================================================
--- rtemsbsd/nfsclient/README	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/README	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,548 @@
+RTEMS-NFS
+=========
+
+A NFS-V2 client implementation for the RTEMS real-time
+executive.
+
+Author: Till Straumann <strauman@slac.stanford.edu>, 2002
+
+Copyright 2002, Stanford University and
+                Till Straumann <strauman@slac.stanford.edu>
+
+Stanford Notice
+***************
+
+Acknowledgement of sponsorship
+* * * * * * * * * * * * * * * *
+This software was produced by the Stanford Linear Accelerator Center,
+Stanford University, under Contract DE-AC03-76SFO0515 with the Department
+of Energy.
+
+
+Contents
+--------
+I   Overview
+  1) Performance
+  2) Reference Platform / Test Environment
+
+II  Usage
+  1) Initialization
+  2) Mounting Remote Server Filesystems
+  3) Unmounting
+  4) Unloading
+  5) Dumping Information / Statistics
+
+III Implementation Details
+  1) RPCIOD
+  2) NFS
+  3) RTEMS Resources Used By NFS/RPCIOD
+  4) Caveats & Bugs
+
+IV  Licensing & Disclaimers
+
+I  Overview
+-----------
+
+This package implements a simple non-caching NFS
+client for RTEMS. Most of the system calls are
+supported with the exception of 'mount', i.e. it
+is not possible to mount another FS on top of NFS
+(mostly because of the difficulty that arises when
+mount points are deleted on the server). It
+shouldn't be hard to do, though.
+
+Note: this client supports NFS vers. 2 / MOUNT vers. 1;
+      NFS Version 3 or higher are NOT supported.
+
+The package consists of two modules: RPCIOD and NFS
+itself.
+
+ - RPCIOD is a UDP/RPC multiplexor daemon. It takes
+   RPC requests from multiple local client threads,
+   funnels them through a single socket to multiple
+   servers and dispatches the replies back to the
+   (blocked) requestor threads.
+   RPCIOD does packet retransmission and handles
+   timeouts etc.
+   Note however, that it does NOT do any XDR
+   marshalling - it is up to the requestor threads
+   to do the XDR encoding/decoding. RPCIOD _is_ RPC
+   specific, though, because its message dispatching
+   is based on the RPC transaction ID.
+
+ - The NFS package maps RTEMS filesystem calls
+   to proper RPCs, it does the XDR work and
+   hands marshalled RPC requests to RPCIOD.
+   All of the calls are synchronous, i.e. they
+   block until they get a reply.
+
+1) Performance
+- - - - - - - -
+Performance sucks (due to the lack of
+readahead/delayed write and caching). On a fast
+(100Mb/s) ethernet, it takes about 20s to copy a
+10MB file from NFS to NFS.  I found, however, that
+vxWorks' NFS client doesn't seem to be any
+faster...
+
+Since there is no buffer cache with read-ahead
+implemented, all NFS reads are synchronous RPC
+calls. Every read operation involves sending a
+request and waiting for the reply. As long as the
+overhead (sending request + processing it on the
+server) is significant compared to the time it
+takes to transferring the actual data, increasing
+the amount of data per request results in better
+throughput. The UDP packet size limit imposes a
+limit of 8k per RPC call, hence reading from NFS
+in chunks of 8k is better than chunks of 1k [but
+chunks >8k are not possible, i.e., simply not
+honoured: read(a_nfs_fd, buf, 20000) returns
+8192]. This is similar to the old linux days
+(mount with rsize=8k).  You can let stdio take
+care of the buffering or use 8k buffers with
+explicit read(2) operations. Note that stdio
+honours the file-system's st_blksize field
+if newlib is compiled with HAVE_BLKSIZE defined.
+In this case, stdio uses 8k buffers for files
+on NFS transparently. The blocksize NFS 
+reports can be tuned with a global variable
+setting (see nfs.c for details).
+
+Further increase of throughput can be achieved
+with read-ahead (issuing RPC calls in parallel
+[send out request for block n+1 while you are
+waiting for data of block n to arrive]).  Since
+this is not handled by the file system itself, you
+would have to code this yourself e.g., using
+parallel threads to read from a single file from
+interleaved offsets.
+
+Another obvious improvement can be achieved if
+processing the data takes a significant amount of
+time. Then, having a pipeline of threads for
+reading data and processing them makes sense
+[thread b processes chunk n while thread a blocks
+in read(chunk n+1)].
+
+Some performance figures:
+Software: src/nfsTest.c:nfsReadTest() [data not
+          processed in any way].
+Hardware: MVME6100
+Network:  100baseT-FD
+Server:   Linux-2.6/RHEL4-smp [dell precision 420]
+File:     10MB
+
+Results:
+Single threaded ('normal') NFS read, 1k buffers: 3.46s (2.89MB/s)
+Single threaded ('normal') NFS read, 8k buffers: 1.31s (7.63MB/s)
+Multi  threaded; 2 readers, 8k buffers/xfers:    1.12s (8.9 MB/s)  
+Multi  threaded; 3 readers, 8k buffers/xfers:    1.04s (9.6 MB/s)
+
+2) Reference Platform
+- - - - - - - - - - -
+RTEMS-NFS was developed and tested on
+
+ o RTEMS-ss20020301 (local patches applied)
+ o PowerPC G3, G4 on Synergy SVGM series board
+   (custom 'SVGM' BSP, to be released soon)
+ o PowerPC 604 on MVME23xx
+   (powerpc/shared/motorola-powerpc BSP)
+ o Test Environment:
+    - RTEMS executable running CEXP
+    - rpciod/nfs dynamically loaded from TFTPfs
+    - EPICS application dynamically loaded from NFS;
+      the executing IOC accesses all of its files
+      on NFS.
+
+II Usage
+---------
+
+After linking into the system and proper initialization
+(rtems-NFS supports 'magic' module initialization when
+loaded into a running system with the CEXP loader),
+you are ready for mounting NFSes from a server
+(I avoid the term NFS filesystem because NFS already
+stands for 'Network File System').
+
+You should also read the
+
+  - "RTEMS Resources Used By NFS/RPCIOD"
+  - "CAVEATS & BUGS"
+
+below.
+
+1) Initialization
+- - - - - - - - - 
+NFS consists of two modules who must be initialized:
+
+ a) the RPCIO daemon package; by calling
+
+      rpcUdpInit();
+
+    note that this step must be performed prior to
+    initializing NFS:
+
+ b) NFS is initialized by calling
+
+      nfsInit( smallPoolDepth, bigPoolDepth );
+
+    if you supply 0 (zero) values for the pool
+    depths, the compile-time default configuration
+    is used which should work fine.
+
+NOTE: when using CEXP to load these modules into a
+running system, initialization will be performed
+automagically.
+
+2) Mounting Remote Server Filesystems
+- - - - - - - - - - - - - - - - - - -
+
+There are two interfaces for mounting an NFS:
+
+ - The (non-POSIX) RTEMS 'mount()' call:
+
+     mount( &mount_table_entry_pointer,
+            &filesystem_operations_table_pointer,
+            options,
+            device,
+            mount_point )
+
+    Note that you must specify a 'mount_table_entry_pointer'
+    (use a dummy) - RTEMS' mount() doesn't grok a NULL for
+    the first argument.
+
+     o for the 'filesystem_operations_table_pointer', supply
+
+         &nfs_fs_ops
+   
+     o options are constants (see RTEMS headers) for specifying
+       read-only / read-write mounts.
+
+     o the 'device' string specifies the remote filesystem
+       who is to be mounted. NFS expects a string conforming
+       to the following format (EBNF syntax):
+
+         [ <uid> '.' <gid> '@' ] <hostip> ':' <path>
+
+       The first optional part of the string allows you
+       to specify the credentials to be used for all
+       subsequent transactions with this server. If the
+       string is omitted, the EUID/EGID of the executing
+       thread (i.e. the thread performing the 'mount' - 
+       NFS will still 'remember' these values and use them
+       for all future communication with this server).
+       
+       The <hostip> part denotes the server IP address
+       in standard 'dot' notation. It is followed by
+       a colon and the (absolute) path on the server.
+       Note that no extra characters or whitespace must
+       be present in the string. Example 'device' strings
+       are:
+
+         "300.99@192.168.44.3:/remote/rtems/root"
+
+         "192.168.44.3:/remote/rtems/root"
+
+    o the 'mount_point' string identifies the local
+      directory (most probably on IMFS) where the NFS
+      is to be mounted. Note that the mount point must
+      already exist with proper permissions.
+
+ - Alternate 'mount' interface. NFS offers a more
+   convenient wrapper taking three string arguments:
+
+	nfsMount(uidgid_at_host, server_path, mount_point)
+
+   This interface does DNS lookup (see reentrancy note
+   below) and creates the mount point if necessary.
+   
+   o the first argument specifies the server and
+     optionally the uid/gid to be used for authentication.
+     The semantics are exactly as described above:
+
+       [ <uid> '.' <gid> '@' ] <host>
+     
+     The <host> part may be either a host _name_ or
+     an IP address in 'dot' notation. In the former
+     case, nfsMount() uses 'gethostbyname()' to do
+     a DNS lookup.
+
+     IMPORTANT NOTE: gethostbyname() is NOT reentrant/
+     thread-safe and 'nfsMount()' (if not provided with an
+     IP/dot address string) is hence subject to race conditions.
+ 
+   o the 'server_path' and 'mount_point' arguments
+     are described above.
+     NOTE: If the mount point does not exist yet,
+           nfsMount() tries to create it.
+
+   o if nfsMount() is called with a NULL 'uidgid_at_host'
+     argument, it lists all currently mounted NFS
+
+3) Unmounting
+- - - - - - -
+An NFS can be unmounted using RTEMS 'unmount()'
+call (yep, it is unmount() - not umount()):
+
+  unmount(mount_point)
+
+Note that you _must_ supply the mount point (string
+argument). It is _not_ possible to specify the
+'mountee' when unmounting. NFS implements no
+convenience wrapper for this (yet), essentially because
+(although this sounds unbelievable) it is non-trivial
+to lookup the path leading to an RTEMS filesystem
+directory node.
+
+4) Unloading
+- - - - - - -
+After unmounting all NFS from the system, the NFS
+and RPCIOD modules may be stopped and unloaded.
+Just call 'nfsCleanup()' and 'rpcUdpCleanup()'
+in this order. You should evaluate the return value
+of these routines which is non-zero if either
+of them refuses to yield (e.g. because there are
+still mounted filesystems).
+Again, when unloading is done by CEXP this is
+transparently handled.
+
+5) Dumping Information / Statistics
+- - - - - - - - - - - - - - - - - -
+
+Rudimentary RPCIOD statistics are printed
+to a file (stdout when NULL) by
+
+  int rpcUdpStats(FILE *f)
+
+A list of all currently mounted NFS can be
+printed to a file (stdout if NULL) using
+
+  int nfsMountsShow(FILE *f)
+
+For convenience, this routine is also called
+by nfsMount() when supplying NULL arguments.
+
+III Implementation Details
+--------------------------
+
+1) RPCIOD
+- - - - -
+
+RPCIOD was created to
+
+a) avoid non-reentrant librpc calls.
+b) support 'asynchronous' operation over a single
+   socket.
+
+RPCIOD is a daemon thread handling 'transaction objects'
+(XACTs) through an UDP socket.  XACTs are marshalled RPC
+calls/replies associated with RPC servers and requestor
+threads.
+
+requestor thread:                 network:
+
+       XACT                        packet  
+        |                            |
+        V                            V
+  | message queue |              ( socket )
+        |                            |  ^
+        ---------->          <-----  |  |
+                     RPCIOD             |
+                   /       --------------
+           timeout/         (re) transmission
+                         
+
+A requestor thread drops a transaction into 
+the message queue and goes to sleep.  The XACT is
+picked up by rpciod who is listening for events from
+three sources:
+
+  o the request queue
+  o packet arrival at the socket
+  o timeouts
+
+RPCIOD sends the XACT to its destination server and
+enqueues the pending XACT into an ordered list of
+outstanding transactions.
+
+When a packet arrives, RPCIOD (based on the RPC transaction
+ID) looks up the matching XACT and wakes up the requestor
+who can then XDR-decode the RPC results found in the XACT
+object's buffer.
+
+When a timeout expires, RPCIOD examines the outstanding
+XACT that is responsible for the timeout. If its lifetime
+has not expired yet, RPCIOD resends the request. Otherwise,
+the XACT's error status is set and the requestor is woken up.
+
+RPCIOD dynamically adjusts the retransmission intervals
+based on the average round-trip time measured (on a per-server
+basis).
+
+Having the requestors event driven (rather than blocking
+e.g. on a semaphore) is geared to having many different
+requestors (one synchronization object per requestor would
+be needed otherwise).
+
+Requestors who want to do asynchronous IO need a different
+interface which will be added in the future.
+
+1.a) Reentrancy
+- - - - - - - - 
+RPCIOD does no non-reentrant librpc calls.
+
+1.b) Efficiency
+- - - - - - - - 
+We shouldn't bother about efficiency until pipelining (read-ahead/
+delayed write) and caching are implemented. The round-trip delay
+associated with every single RPC transaction clearly is a big
+performance killer.
+
+Nevertheless, I could not withstand the temptation to eliminate
+the extra copy step involved with socket IO:
+
+A user data object has to be XDR encoded into a buffer. The 
+buffer given to the socket where it is copied into MBUFs.
+(The network chip driver might even do more copying).
+
+Likewise, on reception 'recvfrom' copies MBUFS into a user
+buffer which is XDR decoded into the final user data object.
+
+Eliminating the copying into (possibly multiple) MBUFS by
+'sendto()' is actually a piece of cake. RPCIOD uses the
+'sosend()' routine [properly wrapped] supplying a single
+MBUF header who directly points to the marshalled buffer
+:-)
+
+Getting rid of the extra copy on reception was (only a little)
+harder: I derived a 'XDR-mbuf' stream from SUN's xdr_mem which
+allows for XDR-decoding out of a MBUF chain who is obtained by
+soreceive().
+
+2) NFS
+- - - -
+The actual NFS implementation is straightforward and essentially
+'passive' (no threads created). Any RTEMS task executing a
+filesystem call dispatched to NFS (such as 'opendir()', 'lseek()'
+or 'unlink()') ends up XDR encoding arguments, dropping a
+XACT into RPCIOD's message queue and going to sleep.
+When woken up by RPCIOD, the XACT is decoded (using the XDR-mbuf
+stream mentioned above) and the properly cooked-up results are
+returned.
+
+3) RTEMS Resources Used By NFS/RPCIOD
+- - - - - - - - - - - - - - - - - - -
+
+The RPCIOD/NFS package uses the following resources. Some
+parameters are compile-time configurable - consult the
+source files for details.
+
+RPCIOD:
+ o 1 task 
+ o 1 message queue
+ o 1 socket/filedescriptor
+ o 2 semaphores (a third one is temporarily created during
+   rpcUdpCleanup()).
+ o 1 RTEMS EVENT (by default RTEMS_EVENT_30).
+   IMPORTANT: this event is used by _every_ thread executing
+              NFS system calls and hence is RESERVED.
+ o 3 events only used by RPCIOD itself, i.e. these must not
+   be sent to RPCIOD by no other thread (except for the intended
+   use, of course). The events involved are 1,2,3.
+ o preemption disabled sections:      NONE
+ o sections with interrupts disabled: NONE
+ o NO 'timers' are used (timer code would run in IRQ context)
+ o memory usage: n.a
+
+NFS:
+ o 2 message queues
+ o 2 semaphores
+ o 1 semaphore per mounted NFS
+ o 1 slot in driver entry table (for major number)
+ o preemption disabled sections:      NONE
+ o sections with interrupts disabled: NONE
+ o 1 task + 1 semaphore temporarily created when
+   listing mounted filesystems (rtems_filesystem_resolve_location())
+
+4) CAVEATS & BUGS
+- - - - - - - - -
+Unfortunately, some bugs crawl around in the filesystem generics.
+(Some of them might already be fixed in versions later than
+rtems-ss-20020301).
+I recommend to use the patch distributed with RTEMS-NFS.
+
+ o RTEMS uses/used (Joel said it has been fixed already) a 'short'
+   ino_t which is not enough for NFS.
+   The driver detects this problem and enables a workaround. In rare
+   situations (mainly involving 'getcwd()' improper inode comparison
+   may result (due to the restricted size, stat() returns st_ino modulo
+   2^16). In most cases, however, st_dev is compared along with st_ino
+   which will give correct results (different files may yield identical
+   st_ino but they will have different st_dev). However, there is 
+   code (in getcwd(), for example) who assumes that files residing
+   in one directory must be hosted by the same device and hence omits
+   the st_dev comparison. In such a case, the workaround will fail.
+ 
+   NOTE: changing the size (sys/types.h) of ino_t from 'short' to 'long'
+         is strongly recommended. It is NOT included in the patch, however
+         as this is a major change requiring ALL of your sources to
+         be recompiled.
+
+   THE ino_t SIZE IS FIXED IN GCC-3.2/NEWLIB-1.10.0-2 DISTRIBUTED BY
+   OAR.
+
+ o You may work around most filesystem bugs by observing the following
+   rules:
+
+    * never use chroot() (fixed by the patch)
+    * never use getpwent(), getgrent() & friends - they are NOT THREAD
+      safe (fixed by the patch)
+    * NEVER use rtems_libio_share_private_env() - not even with the
+      patch applied. Just DONT - it is broken by design.
+    * All threads who have their own userenv (who have called
+      rtems_libio_set_private_env()) SHOULD 'chdir("/")' before
+      terminating. Otherwise, (i.e. if their cwd is on NFS), it will
+      be impossible to unmount the NFS involved.
+
+ o The patch slightly changes the semantics of 'getpwent()' and
+   'getgrent()' & friends (to what is IMHO correct anyways - the patch is
+   also needed to fix another problem, however): with the patch applied,
+   the passwd and group files are always accessed from the 'current' user
+   environment, i.e. a thread who has changed its 'root' or 'uid' might
+   not be able to access these files anymore.
+      
+ o NOTE: RTEMS 'mount()' / 'unmount()' are NOT THREAD SAFE.
+
+ o The NFS protocol has no 'append' or 'seek_end' primitive. The client
+   must query the current file size (this client uses cached info) and
+   change the local file pointer accordingly (in 'O_APPEND' mode).
+   Obviously, this involves a race condition and hence multiple clients
+   writing the same file may lead to corruption.
+
+IV Licensing & Disclaimers
+--------------------------
+
+NFS is distributed under the SLAC License - consult the
+separate 'LICENSE' file.
+
+Government disclaimer of liability
+- - - - - - - - - - - - - - - - -
+Neither the United States nor the United States Department of Energy,
+nor any of their employees, makes any warranty, express or implied,
+or assumes any legal liability or responsibility for the accuracy,
+completeness, or usefulness of any data, apparatus, product, or process
+disclosed, or represents that its use would not infringe privately
+owned rights.
+
+Stanford disclaimer of liability
+- - - - - - - - - - - - - - - - -
+Stanford University makes no representations or warranties, express or
+implied, nor assumes any liability for the use of this software.
+
+Maintenance of notice
+- - - - - - - - - - -
+In the interest of clarity regarding the origin and status of this
+software, Stanford University requests that any recipient of it maintain
+this notice affixed to any distribution by the recipient that contains a
+copy or derivative of this software.
Index: rtemsbsd/nfsclient/mount_prot.h
===================================================================
--- rtemsbsd/nfsclient/mount_prot.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/mount_prot.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,152 @@
+/**
+ * @file
+ *
+ * @brief Nfsclient Mount Prot
+ *
+ * @ingroup rtems-nfsclient
+ */
+
+#ifndef _MOUNT_PROT_H_RPCGEN
+#define _MOUNT_PROT_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+/**
+ *  @defgroup libfs_nfsclient_mount_prot Mount Prot
+ *
+ *  @ingroup libfs
+ */
+/**@{*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+#define FHSIZE 32
+
+typedef char fhandle[FHSIZE];
+
+struct fhstatus {
+	u_int fhs_status;
+	union {
+		fhandle fhs_fhandle;
+	} fhstatus_u;
+};
+typedef struct fhstatus fhstatus;
+
+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 MOUNTVERS 1
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define MOUNTPROC_NULL 0
+extern  void * mountproc_null_1(void *, CLIENT *);
+extern  void * mountproc_null_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_MNT 1
+extern  fhstatus * mountproc_mnt_1(dirpath *, CLIENT *);
+extern  fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_DUMP 2
+extern  mountlist * mountproc_dump_1(void *, CLIENT *);
+extern  mountlist * mountproc_dump_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_UMNT 3
+extern  void * mountproc_umnt_1(dirpath *, CLIENT *);
+extern  void * mountproc_umnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_UMNTALL 4
+extern  void * mountproc_umntall_1(void *, CLIENT *);
+extern  void * mountproc_umntall_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORT 5
+extern  exports * mountproc_export_1(void *, CLIENT *);
+extern  exports * mountproc_export_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORTALL 6
+extern  exports * mountproc_exportall_1(void *, CLIENT *);
+extern  exports * mountproc_exportall_1_svc(void *, struct svc_req *);
+extern int mountprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define MOUNTPROC_NULL 0
+extern  void * mountproc_null_1();
+extern  void * mountproc_null_1_svc();
+#define MOUNTPROC_MNT 1
+extern  fhstatus * mountproc_mnt_1();
+extern  fhstatus * mountproc_mnt_1_svc();
+#define MOUNTPROC_DUMP 2
+extern  mountlist * mountproc_dump_1();
+extern  mountlist * mountproc_dump_1_svc();
+#define MOUNTPROC_UMNT 3
+extern  void * mountproc_umnt_1();
+extern  void * mountproc_umnt_1_svc();
+#define MOUNTPROC_UMNTALL 4
+extern  void * mountproc_umntall_1();
+extern  void * mountproc_umntall_1_svc();
+#define MOUNTPROC_EXPORT 5
+extern  exports * mountproc_export_1();
+extern  exports * mountproc_export_1_svc();
+#define MOUNTPROC_EXPORTALL 6
+extern  exports * mountproc_exportall_1();
+extern  exports * mountproc_exportall_1_svc();
+extern int mountprog_1_freeresult ();
+#endif /* K&R C */
+
+/* the xdr functions */
+
+#if defined(__STDC__) || defined(__cplusplus)
+extern  bool_t xdr_fhandle (XDR *, fhandle);
+extern  bool_t xdr_fhstatus (XDR *, fhstatus*);
+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*);
+
+#else /* K&R C */
+extern bool_t xdr_fhandle ();
+extern bool_t xdr_fhstatus ();
+extern bool_t xdr_dirpath ();
+extern bool_t xdr_name ();
+extern bool_t xdr_mountlist ();
+extern bool_t xdr_mountbody ();
+extern bool_t xdr_groups ();
+extern bool_t xdr_groupnode ();
+extern bool_t xdr_exports ();
+extern bool_t xdr_exportnode ();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+/**@}*/
+#endif /* !_MOUNT_PROT_H_RPCGEN */
Index: rtemsbsd/nfsclient/mount_prot.x
===================================================================
--- rtemsbsd/nfsclient/mount_prot.x	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/mount_prot.x	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,161 @@
+/* @(#)mount.x	2.1 88/08/01 4.0 RPCSRC */
+/* @(#)mount.x 1.2 87/09/18 Copyr 1987 Sun Micro */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ * 
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ * 
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ * 
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ * 
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ * 
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * Protocol description for the mount program
+ */
+
+
+const MNTPATHLEN = 1024;	/* maximum bytes in a pathname argument */
+const MNTNAMLEN = 255;		/* maximum bytes in a name argument */
+const FHSIZE = 32;		/* size in bytes of a file handle */
+
+/*
+ * The fhandle is the file handle that the server passes to the client.
+ * All file operations are done using the file handles to refer to a file
+ * or a directory. The file handle can contain whatever information the
+ * server needs to distinguish an individual file.
+ */
+typedef opaque fhandle[FHSIZE];	
+
+/*
+ * If a status of zero is returned, the call completed successfully, and 
+ * a file handle for the directory follows. A non-zero status indicates
+ * some sort of error. The status corresponds with UNIX error numbers.
+ */
+union fhstatus switch (unsigned fhs_status) {
+case 0:
+	fhandle fhs_fhandle;
+default:
+	void;
+};
+
+/*
+ * The type dirpath is the pathname of a directory
+ */
+typedef string dirpath<MNTPATHLEN>;
+
+/*
+ * The type name is used for arbitrary names (hostnames, groupnames)
+ */
+typedef string name<MNTNAMLEN>;
+
+/*
+ * A list of who has what mounted
+ */
+typedef struct mountbody *mountlist;
+struct mountbody {
+	name ml_hostname;
+	dirpath ml_directory;
+	mountlist ml_next;
+};
+
+/*
+ * A list of netgroups
+ */
+typedef struct groupnode *groups;
+struct groupnode {
+	name gr_name;
+	groups gr_next;
+};
+
+/*
+ * A list of what is exported and to whom
+ */
+typedef struct exportnode *exports;
+struct exportnode {
+	dirpath ex_dir;
+	groups ex_groups;
+	exports ex_next;
+};
+
+program MOUNTPROG {
+	/*
+	 * Version one of the mount protocol communicates with version two
+	 * of the NFS protocol. The only connecting point is the fhandle 
+	 * structure, which is the same for both protocols.
+	 */
+	version MOUNTVERS {
+		/*
+		 * Does no work. It is made available in all RPC services
+		 * to allow server reponse testing and timing
+		 */
+		void
+		MOUNTPROC_NULL(void) = 0;
+
+		/*	
+		 * If fhs_status is 0, then fhs_fhandle contains the
+	 	 * file handle for the directory. This file handle may
+		 * be used in the NFS protocol. This procedure also adds
+		 * a new entry to the mount list for this client mounting
+		 * the directory.
+		 * Unix authentication required.
+		 */
+		fhstatus 
+		MOUNTPROC_MNT(dirpath) = 1;
+
+		/*
+		 * Returns the list of remotely mounted filesystems. The 
+		 * mountlist contains one entry for each hostname and 
+		 * directory pair.
+		 */
+		mountlist
+		MOUNTPROC_DUMP(void) = 2;
+
+		/*
+		 * Removes the mount list entry for the directory
+		 * Unix authentication required.
+		 */
+		void
+		MOUNTPROC_UMNT(dirpath) = 3;
+
+		/*
+		 * Removes all of the mount list entries for this client
+		 * Unix authentication required.
+		 */
+		void
+		MOUNTPROC_UMNTALL(void) = 4;
+
+		/*
+		 * Returns a list of all the exported filesystems, and which
+		 * machines are allowed to import it.
+		 */
+		exports
+		MOUNTPROC_EXPORT(void)  = 5;
+
+		/*
+		 * Identical to MOUNTPROC_EXPORT above
+		 */
+		exports
+		MOUNTPROC_EXPORTALL(void) = 6;
+	} = 1;
+} = 100005;
Index: rtemsbsd/nfsclient/mount_prot_xdr.c
===================================================================
--- rtemsbsd/nfsclient/mount_prot_xdr.c	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/mount_prot_xdr.c	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,111 @@
+/**
+ * @file
+ *
+ * @brief Mount Prot XDR
+ * @ingroup libfs_nfsclient_mount_prot Mount Prot
+ */
+
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "mount_prot.h"
+
+bool_t
+xdr_fhandle (XDR *xdrs, fhandle objp)
+{
+	 if (!xdr_opaque (xdrs, objp, FHSIZE))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_fhstatus (XDR *xdrs, fhstatus *objp)
+{
+	 if (!xdr_u_int (xdrs, &objp->fhs_status))
+		 return FALSE;
+	switch (objp->fhs_status) {
+	case 0:
+		 if (!xdr_fhandle (xdrs, objp->fhstatus_u.fhs_fhandle))
+			 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;
+}
Index: rtemsbsd/nfsclient/nfs.c
===================================================================
--- rtemsbsd/nfsclient/nfs.c	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/nfs.c	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,3240 @@
+/**
+ * @file
+ *
+ * @brief NFS Client Implementation for RTEMS
+ * @ingroup libfs
+ *
+ * Hooks Into the RTEMS NFS Filesystem
+ */
+
+/*
+ * Author: Till Straumann <strauman@slac.stanford.edu>, 2002
+ *
+ * Hacked on by others.
+ *
+ * Modifications to support reference counting in the file system are
+ * Copyright (c) 2012 embedded brains GmbH.
+ *
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
+ * 	   Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The NFS-2 client implementation for RTEMS was produced by
+ *     the Stanford Linear Accelerator Center, Stanford University,
+ * 	   under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software.  Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#ifdef	HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/libio_.h>
+#include <rtems/seterr.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <nfs_prot.h>
+#include <mount_prot.h>
+
+#include "rpcio.h"
+#include "librtemsNfs.h"
+
+/* Configurable parameters */
+
+/* Estimated average length of a filename (including terminating 0).
+ * This was calculated by doing
+ *
+ * 	find <some root> -print -exec basename '{}' \; > feil
+ * 	wc feil
+ *
+ * AVG_NAMLEN = (num_chars + num_lines)/num_lines
+ */
+#define CONFIG_AVG_NAMLEN				10
+
+#define CONFIG_NFS_SMALL_XACT_SIZE		800			/* size of RPC arguments for non-write ops */
+/* lifetime of NFS attributes in a NfsNode;
+ * the time is in seconds and the lifetime is
+ * infinite if the symbol is #undef
+ */
+#define CONFIG_ATTR_LIFETIME			10/*secs*/
+
+/*
+ * The 'st_blksize' (stat(2)) value this nfs
+ * client should report. If set to zero then the server's fattr data
+ * is passed throught which is not necessary optimal.
+ * Newlib's stdio uses 'st_blksize' (if built with HAVE_BLKSIZE defined)
+ * to size the default buffer.
+ * Due to the overhead of NFS it is probably better to use the maximum
+ * size of an NFS read request (8k) rather than the optimal block
+ * size on the server.
+ * This value can be overridden at run-time by setting the global
+ * variable 'nfsStBlksize'.
+ * Thanks to Steven Johnson <sjohnson@sakuraindustries.com> for helping
+ * working on this issue.
+ */
+#define DEFAULT_NFS_ST_BLKSIZE			NFS_MAXDATA
+
+/* dont change this without changing the maximal write size */
+#define CONFIG_NFS_BIG_XACT_SIZE		UDPMSGSIZE	/* dont change this */
+
+/* The real values for these are specified further down */
+#define NFSCALL_TIMEOUT					(&_nfscalltimeout)
+#define MNTCALL_TIMEOUT					(&_nfscalltimeout)
+static struct timeval _nfscalltimeout = { 10, 0 };	/* {secs, us } */
+
+/* More or less fixed constants; in particular, NFS3 is not supported */
+#define DELIM							'/'
+#define HOSTDELIM						':'
+#define UPDIR							".."
+#define UIDSEP							'@'
+#define NFS_VERSION_2					NFS_VERSION
+
+/* we use a dynamically assigned major number */
+#define NFS_MAJOR						(nfsGlob.nfs_major)
+
+
+/* NOTE: RTEMS (ss-20020301) uses a 'short st_ino' type :-( but the
+ * NFS fileid is 32 bit. [Later versions of RTEMS have fixed this;
+ * nfsInit() issues a warning if you run a version with 'short st_ino'.]
+ *
+ * As a workarount, we merge the upper 16bits of the fileid into the
+ * minor device no. Hence, it is still possible to uniquely identify
+ * a file by looking at its device number (major = nfs, minor = part
+ * of the fileid + our 'nfs-id' identifier).
+ *
+ * This has an impact on performance, as e.g. getcwd() stats() all
+ * directory entries when it believes it has crossed a mount point
+ * (a.st_dev != b.st_dev).
+ *
+ * OTOH, it also might cause node comparison failure! E.g. 'getcwd()'
+ * assumes that two nodes residing in the same directory must be located
+ * on the same device and hence compares 'st_ino' only.
+ * If two files in the same directory have the same inode number
+ * modulo 2^16, they will be considered equal (although their device
+ * number doesn't match - getcwd doesn't look at it).
+ *
+ * Other software might or might not be affected.
+ *
+ * The only clean solution to this problem is bumping up the size of
+ * 'ino_t' at least to 'long'.
+ * Note that this requires _all_ software (libraries etc.) to be
+ * recompiled.
+ */
+
+#define	NFS_MAKE_DEV_T_INO_HACK(node) \
+		rtems_filesystem_make_dev_t( NFS_MAJOR, \
+			(((rtems_device_minor_number)((node)->nfs->id))<<16) | (((rtems_device_minor_number)SERP_ATTR((node)).fileid) >> 16) )
+
+/* use our 'nfs id' and the server's fsid for the minor device number
+ * this should be fairly unique
+ */
+#define	NFS_MAKE_DEV_T(node) \
+		rtems_filesystem_make_dev_t( NFS_MAJOR, \
+			(((rtems_device_minor_number)((node)->nfs->id))<<16) | (SERP_ATTR((node)).fsid & (((rtems_device_minor_number)1<<16)-1)) )
+
+#define  DIRENT_HEADER_SIZE ( sizeof(struct dirent) - \
+			sizeof( ((struct dirent *)0)->d_name ) )
+
+
+/* debugging flags */
+#define DEBUG_COUNT_NODES	(1<<0)
+#define DEBUG_TRACK_NODES	(1<<1)
+#define DEBUG_EVALPATH		(1<<2)
+#define DEBUG_READDIR		(1<<3)
+#define DEBUG_SYSCALLS		(1<<4)
+
+/* #define DEBUG	( DEBUG_SYSCALLS | DEBUG_COUNT_NODES ) */
+
+#ifdef DEBUG
+#define STATIC
+#else
+#define STATIC static
+#endif
+
+#define MUTEX_ATTRIBUTES    (RTEMS_LOCAL           |   \
+                            RTEMS_PRIORITY         |   \
+                            RTEMS_INHERIT_PRIORITY |   \
+                            RTEMS_BINARY_SEMAPHORE)
+
+#define LOCK(s)		do {                               \
+						rtems_semaphore_obtain((s),    \
+									RTEMS_WAIT,        \
+									RTEMS_NO_TIMEOUT); \
+					} while (0)
+
+#define UNLOCK(s)	do { rtems_semaphore_release((s)); \
+					} while (0)
+
+RTEMS_INTERRUPT_LOCK_DEFINE(static, nfs_global_lock, "NFS")
+
+#define NFS_GLOBAL_ACQUIRE(lock_context) \
+    rtems_interrupt_lock_acquire(&nfs_global_lock, lock_context)
+
+#define NFS_GLOBAL_RELEASE(lock_context) \
+    rtems_interrupt_lock_release(&nfs_global_lock, lock_context)
+
+static inline char *
+nfs_dupname(const char *name, size_t namelen)
+{
+	char *dupname = malloc(namelen + 1);
+
+	if (dupname != NULL) {
+		memcpy(dupname, name, namelen);
+		dupname [namelen] = '\0';
+	} else {
+		errno = ENOMEM;
+	}
+
+	return dupname;
+}
+
+/*****************************************
+	Types with Associated XDR Routines
+ *****************************************/
+
+/* a string buffer with a maximal length.
+ * If the buffer pointer is NULL, it is updated
+ * with an appropriately allocated area.
+ */
+typedef struct strbuf {
+	char	*buf;
+	u_int	max;
+} strbuf;
+
+/* Read 'readlink' results into a 'strbuf'.
+ * This is convenient as it avoids
+ * one extra step of copying / lenght
+ * checking.
+ */
+typedef struct readlinkres_strbuf {
+	nfsstat	status;
+	strbuf	strbuf;
+} readlinkres_strbuf;
+
+static bool_t
+xdr_readlinkres_strbuf(XDR *xdrs, readlinkres_strbuf *objp)
+{
+	if ( !xdr_nfsstat(xdrs, &objp->status) )
+		return FALSE;
+
+	if ( NFS_OK == objp->status ) {
+		if ( !xdr_string(xdrs, &objp->strbuf.buf, objp->strbuf.max) )
+			return FALSE;
+	}
+	return TRUE;
+}
+
+
+/* DirInfoRec is used instead of dirresargs
+ * to convert recursion into iteration. The
+ * 'rpcgen'erated xdr_dirresargs ends up
+ * doing nested calls when unpacking the
+ * 'next' pointers.
+ */
+
+typedef struct DirInfoRec_ {
+	readdirargs	readdirargs;
+	/* clone of the 'readdirres' fields;
+	 * the cookie is put into the readdirargs above
+	 */
+	nfsstat		status;
+	char		*buf, *ptr;
+	int			len;
+	bool_t		eofreached;
+} DirInfoRec, *DirInfo;
+
+/* this deals with one entry / record */
+static bool_t
+xdr_dir_info_entry(XDR *xdrs, DirInfo di)
+{
+union	{
+	char			nambuf[NFS_MAXNAMLEN+1];
+	nfscookie		cookie;
+}				dummy;
+struct dirent	*pde = (struct dirent *)di->ptr;
+u_int			fileid;
+char			*name;
+register int	nlen = 0,len,naligned = 0;
+nfscookie		*pcookie;
+
+	len = di->len;
+
+	if ( !xdr_u_int(xdrs, &fileid) )
+		return FALSE;
+
+	/* we must pass the address of a char* */
+	name = (len > NFS_MAXNAMLEN) ? pde->d_name : dummy.nambuf;
+
+	if ( !xdr_filename(xdrs, &name) ) {
+		return FALSE;
+	}
+
+	if (len >= 0) {
+		nlen      = strlen(name);
+		naligned  = nlen + 1 /* string delimiter */ + 3 /* alignment */;
+		naligned &= ~3;
+		len      -= naligned;
+	}
+
+	/* if the cookie goes into the DirInfo, we hope this doesn't fail
+	 * - the caller ends up with an invalid readdirargs cookie otherwise...
+	 */
+	pcookie = (len >= 0) ? &di->readdirargs.cookie : &dummy.cookie;
+	if ( !xdr_nfscookie(xdrs, pcookie) ) {
+		return FALSE;
+	}
+
+	di->len = len;
+	/* adjust the buffer pointer */
+	if (len >= 0) {
+		pde->d_ino    = fileid;
+		pde->d_namlen = nlen;
+		pde->d_off	  = di->ptr - di->buf;
+		if (name == dummy.nambuf) {
+			memcpy(pde->d_name, dummy.nambuf, nlen + 1);
+		}
+		pde->d_reclen = DIRENT_HEADER_SIZE + naligned;
+		di->ptr      += pde->d_reclen;
+	}
+
+	return TRUE;
+}
+
+/* this routine loops over all entries */
+static bool_t
+xdr_dir_info(XDR *xdrs, DirInfo di)
+{
+DirInfo	dip;
+
+	if ( !xdr_nfsstat(xdrs, &di->status) )
+		return FALSE;
+
+	if ( NFS_OK != di->status )
+		return TRUE;
+
+	dip = di;
+
+	while (dip) {
+		/* reserve space for the dirent 'header' - we assume it's word aligned! */
+#ifdef DEBUG
+		assert( DIRENT_HEADER_SIZE % 4 == 0 );
+#endif
+		dip->len -= DIRENT_HEADER_SIZE;
+
+		/* we pass a 0 size - size is unused since
+		 * we always pass a non-NULL pointer
+		 */
+		if ( !xdr_pointer(xdrs, (void*)&dip, 0 /* size */, (xdrproc_t)xdr_dir_info_entry) )
+			return FALSE;
+	}
+
+	if ( ! xdr_bool(xdrs, &di->eofreached) )
+		return FALSE;
+
+	/* if everything fits into the XDR buffer but not the user's buffer,
+	 * they must resume reading from where xdr_dir_info_entry() started
+	 * skipping and 'eofreached' needs to be adjusted
+	 */
+	if ( di->len < 0 && di->eofreached )
+		di->eofreached = FALSE;
+
+	return TRUE;
+}
+
+
+/* a type better suited for node operations
+ * than diropres.
+ * fattr and fhs are swapped so parts of this
+ * structure may be used as a diroparg which
+ * is practical when looking up paths.
+ */
+
+/* Macro for accessing serporid fields
+ */
+#define SERP_ARGS(node) ((node)->serporid.serporid_u.serporid.arg_u)
+#define SERP_ATTR(node) ((node)->serporid.serporid_u.serporid.attributes)
+#define SERP_FILE(node) ((node)->serporid.serporid_u.serporid.file)
+
+
+typedef struct serporidok {
+	fattr					attributes;
+	nfs_fh					file;
+	union	{
+		struct {
+			filename	name;
+		}					diroparg;
+		struct {
+			sattr		attributes;
+		}					sattrarg;
+		struct {
+			uint32_t	offset;
+			uint32_t	count;
+			uint32_t	totalcount;
+		}					readarg;
+		struct {
+			uint32_t	beginoffset;
+			uint32_t	offset;
+			uint32_t	totalcount;
+			struct {
+				uint32_t data_len;
+				char* data_val;
+			}			data;
+		}					writearg;
+		struct {
+			filename	name;
+			sattr		attributes;
+		}					createarg;
+		struct {
+			filename	name;
+			diropargs	to;
+		}					renamearg;
+		struct {
+			diropargs	to;
+		}					linkarg;
+		struct {
+			filename	name;
+			nfspath		to;
+			sattr		attributes;
+		}					symlinkarg;
+		struct {
+			nfscookie	cookie;
+			uint32_t	count;
+		}					readdirarg;
+	}							arg_u;
+} serporidok;
+
+typedef struct serporid {
+	nfsstat			status;
+	union	{
+		serporidok	serporid;
+	}				serporid_u;
+} serporid;
+
+/* an XDR routine to encode/decode the inverted diropres
+ * into an nfsnodestat;
+ *
+ * NOTE: this routine only acts on
+ *   - 'serporid.status'
+ *   - 'serporid.file'
+ *   - 'serporid.attributes'
+ * and leaves the 'arg_u' alone.
+ *
+ * The idea is that a 'diropres' is read into 'serporid'
+ * which can then be used as an argument to subsequent
+ * NFS-RPCs (after filling in the node's arg_u).
+ */
+static bool_t
+xdr_serporidok(XDR *xdrs, serporidok *objp)
+{
+     if (!xdr_nfs_fh (xdrs, &objp->file))
+         return FALSE;
+     if (!xdr_fattr (xdrs, &objp->attributes))
+         return FALSE;
+    return TRUE;
+}
+
+static bool_t
+xdr_serporid(XDR *xdrs, serporid *objp)
+{
+     if (!xdr_nfsstat (xdrs, &objp->status))
+         return FALSE;
+    switch (objp->status) {
+    case NFS_OK:
+         if (!xdr_serporidok(xdrs, &objp->serporid_u.serporid))
+             return FALSE;
+        break;
+    default:
+        break;
+    }
+    return TRUE;
+}
+
+/*****************************************
+	Data Structures and Types
+ *****************************************/
+
+/* 'time()' hack with less overhead; */
+
+/* assume reading a long word is atomic */
+#define READ_LONG_IS_ATOMIC
+
+typedef uint32_t	TimeStamp;
+
+static inline TimeStamp
+nowSeconds(void)
+{
+  rtems_interval rval;
+  rtems_clock_get_seconds_since_epoch( &rval );
+  return rval;
+}
+
+
+/* Per mounted FS structure */
+typedef struct NfsRec_ {
+		/* the NFS server we're talking to.
+		 */
+	RpcUdpServer						 server;
+		/* statistics; how many NfsNodes are
+		 * currently alive.
+		 */
+	volatile int						 nodesInUse;
+#if DEBUG & DEBUG_COUNT_NODES
+		/* statistics; how many 'NfsNode.str'
+		 * strings are currently allocated.
+		 */
+	volatile int						 stringsInUse;
+#endif
+		/* A small number who uniquely
+		 * identifies a mounted NFS within
+		 * this driver (i.e. this NfsRec).
+		 * Each time a NFS is mounted, the
+		 * global ID counter is incremented
+		 * and its value is assigned to the
+		 * newly created NfsRec.
+		 */
+	u_short								 id;
+		/* Our RTEMS filesystem mt_entry
+		 */
+	rtems_filesystem_mount_table_entry_t *mt_entry;
+		/* Next NfsRec on a linked list who
+		 * is anchored at nfsGlob
+		 */
+	struct NfsRec_						 *next;
+		/* Who we pretend we are
+		 */
+	u_long								 uid,gid;
+} NfsRec, *Nfs;
+
+typedef struct NfsNodeRec_ {
+		/* This holds this node's attributes
+		 * (stats) and its nfs filehandle.
+		 * It also contains room for nfs rpc
+		 * arguments.
+		 */
+	serporid	serporid;
+		/* The arguments we used when doing
+		 * the 'lookup' call for this node.
+		 * We need this information (especially
+		 * the directory FH) for performing
+		 * certain operations on this
+		 * node (in particular: for unlinking
+		 * it from a parent directory)
+		 */
+	diropargs		args;
+		/* FS this node belongs to
+		 */
+	Nfs				nfs;
+		/* A buffer for the string the
+		 * args.name points to.
+		 * We need this because args.name might
+		 * temporarily point to strings on the
+		 * stack. Duplicates are allocated from
+		 * the heap and attached to 'str' so
+		 * they can be released as appropriate.
+		 */
+	char		   *str;
+		/* A timestamp for the stats
+		 */
+	TimeStamp		age;
+} NfsNodeRec, *NfsNode;
+
+/*****************************************
+	Forward Declarations
+ *****************************************/
+
+static ssize_t nfs_readlink_with_node(
+	NfsNode node,
+	char *buf,
+	size_t len
+);
+
+static int updateAttr(NfsNode node, int force);
+
+/* Mask bits when setting attributes.
+ * Only the 'arg' fields with their
+ * corresponding bit set in the mask
+ * will be used. The others are left
+ * unchanged.
+ * The 'TOUCH' bits instruct nfs_sattr()
+ * to update the respective time
+ * fields to the current time
+ */
+#define	SATTR_MODE		(1<<0)
+#define	SATTR_UID		(1<<1)
+#define	SATTR_GID		(1<<2)
+#define	SATTR_SIZE		(1<<3)
+#define	SATTR_ATIME		(1<<4)
+#define	SATTR_TOUCHA	(1<<5)
+#define	SATTR_MTIME		(1<<6)
+#define	SATTR_TOUCHM	(1<<7)
+#define SATTR_TOUCH		(SATTR_TOUCHM | SATTR_TOUCHA)
+
+static int
+nfs_sattr(NfsNode node, sattr *arg, u_long mask);
+
+extern const struct _rtems_filesystem_operations_table nfs_fs_ops;
+static const struct _rtems_filesystem_file_handlers_r nfs_file_file_handlers;
+static const struct _rtems_filesystem_file_handlers_r nfs_dir_file_handlers;
+static const struct _rtems_filesystem_file_handlers_r nfs_link_file_handlers;
+static		   rtems_driver_address_table		 drvNfs;
+
+int
+nfsMountsShow(FILE*);
+
+rtems_status_code
+rtems_filesystem_resolve_location(char *buf, int len, rtems_filesystem_location_info_t *loc);
+
+/*****************************************
+	Global Variables
+ *****************************************/
+
+/* These are (except for MAXNAMLEN/MAXPATHLEN) copied from IMFS */
+
+static const rtems_filesystem_limits_and_options_t
+nfs_limits_and_options = {
+   5, 				/* link_max */
+   6, 				/* max_canon */
+   7, 				/* max_input */
+   NFS_MAXNAMLEN,	/* name_max */
+   NFS_MAXPATHLEN,	/* path_max */
+   2,				/* pipe_buf */
+   1,				/* posix_async_io */
+   2,				/* posix_chown_restrictions */
+   3,				/* posix_no_trunc */
+   4,				/* posix_prio_io */
+   5,				/* posix_sync_io */
+   6				/* posix_vdisable */
+};
+
+/* size of an encoded 'entry' object */
+static int dirres_entry_size;
+
+/* Global stuff and statistics */
+static struct nfsstats {
+		/* A lock for protecting the
+		 * linked ist of mounted NFS
+		 * and the num_mounted_fs field
+		 */
+	rtems_id					llock;
+		/* A lock for protecting misc
+		 * stuff  within the driver.
+		 * The lock must only be held
+		 * for short periods of time.
+		 */
+	rtems_id					lock;
+		/* Our major number as assigned
+		 * by RTEMS
+		 */
+	rtems_device_major_number	nfs_major;
+		/* The number of currently
+		 * mounted NFS
+		 */
+	int							num_mounted_fs;
+		/* A list of the currently
+		 * mounted NFS
+		 */
+	struct NfsRec_				*mounted_fs;
+		/* A counter for allocating
+		 * unique IDs to each mounted
+		 * NFS.
+		 * Assume we are not going to
+		 * do more than 16k mounts
+		 * during the system lifetime
+		 */
+	u_short						fs_ids;
+
+	/* Two pools of RPC transactions;
+	 * One with small send buffers
+	 * the other with a big one.
+	 * The actual size of the small
+	 * buffer is configurable (see top).
+	 *
+	 * Note: The RX buffers are always
+	 * big
+	 */
+	RpcUdpXactPool smallPool;
+	RpcUdpXactPool bigPool;
+} nfsGlob = {0, 0,  0xffffffff, 0, 0, 0, NULL, NULL};
+
+/*
+ * Global variable to tune the 'st_blksize' (stat(2)) value this nfs
+ * client should report.
+ * size on the server.
+ */
+#ifndef DEFAULT_NFS_ST_BLKSIZE
+#define DEFAULT_NFS_ST_BLKSIZE	NFS_MAXDATA
+#endif
+int nfsStBlksize = DEFAULT_NFS_ST_BLKSIZE;
+
+
+/*****************************************
+	Implementation
+ *****************************************/
+
+static int nfsEvaluateStatus(nfsstat nfsStatus)
+{
+	static const uint8_t nfsStatusToErrno [71] = {
+		[NFS_OK] = 0,
+		[NFSERR_PERM] = EPERM,
+		[NFSERR_NOENT] = ENOENT,
+		[3] = EIO,
+		[4] = EIO,
+		[NFSERR_IO] = EIO,
+		[NFSERR_NXIO] = ENXIO,
+		[7] = EIO,
+		[8] = EIO,
+		[9] = EIO,
+		[10] = EIO,
+		[11] = EIO,
+		[12] = EIO,
+		[NFSERR_ACCES] = EACCES,
+		[14] = EIO,
+		[15] = EIO,
+		[16] = EIO,
+		[NFSERR_EXIST] = EEXIST,
+		[18] = EIO,
+		[NFSERR_NODEV] = ENODEV,
+		[NFSERR_NOTDIR] = ENOTDIR,
+		[NFSERR_ISDIR] = EISDIR,
+		[22] = EIO,
+		[24] = EIO,
+		[25] = EIO,
+		[26] = EIO,
+		[27] = EIO,
+		[NFSERR_FBIG] = EFBIG,
+		[NFSERR_NOSPC] = ENOSPC,
+		[29] = EIO,
+		[NFSERR_ROFS] = EROFS,
+		[31] = EIO,
+		[32] = EIO,
+		[34] = EIO,
+		[35] = EIO,
+		[36] = EIO,
+		[37] = EIO,
+		[38] = EIO,
+		[39] = EIO,
+		[40] = EIO,
+		[41] = EIO,
+		[42] = EIO,
+		[44] = EIO,
+		[45] = EIO,
+		[46] = EIO,
+		[47] = EIO,
+		[48] = EIO,
+		[49] = EIO,
+		[50] = EIO,
+		[51] = EIO,
+		[52] = EIO,
+		[54] = EIO,
+		[55] = EIO,
+		[56] = EIO,
+		[57] = EIO,
+		[58] = EIO,
+		[59] = EIO,
+		[60] = EIO,
+		[61] = EIO,
+		[62] = EIO,
+		[NFSERR_NAMETOOLONG] = ENAMETOOLONG,
+		[64] = EIO,
+		[65] = EIO,
+		[NFSERR_NOTEMPTY] = ENOTEMPTY,
+		[67] = EIO,
+		[68] = EIO,
+		[NFSERR_DQUOT] = EDQUOT,
+		[NFSERR_STALE] = ESTALE
+	};
+
+	size_t idx = (size_t) nfsStatus;
+	int eno = EIO;
+	int rv = 0;
+
+	if (idx < sizeof(nfsStatusToErrno) / sizeof(nfsStatusToErrno [0])) {
+		eno = nfsStatusToErrno [idx];
+	}
+
+	if (eno != 0) {
+		errno = eno;
+		rv = -1;
+	}
+
+	return rv;
+}
+
+/* Create a Nfs object. This is
+ * per-mounted NFS information.
+ *
+ * ARGS:	The Nfs server handle.
+ *
+ * RETURNS:	Nfs on success,
+ * 			NULL on failure with
+ * 			errno set
+ *
+ * NOTE:	The submitted server
+ * 			object is 'owned' by
+ * 			this Nfs and will be
+ * 			destroyed by nfsDestroy()
+ */
+static Nfs
+nfsCreate(RpcUdpServer server)
+{
+Nfs rval = calloc(1,sizeof(*rval));
+
+	if (rval) {
+		rval->server     = server;
+		LOCK(nfsGlob.llock);
+			rval->next 		   = nfsGlob.mounted_fs;
+			nfsGlob.mounted_fs = rval;
+		UNLOCK(nfsGlob.llock);
+	} else {
+		errno = ENOMEM;
+	}
+		return rval;
+}
+
+/* Destroy an Nfs object and
+ * its associated server
+ */
+static void
+nfsDestroy(Nfs nfs)
+{
+register Nfs prev;
+	if (!nfs)
+		return;
+
+	LOCK(nfsGlob.llock);
+		if (nfs == nfsGlob.mounted_fs)
+			nfsGlob.mounted_fs = nfs->next;
+		else {
+			for (prev = nfsGlob.mounted_fs;
+				 prev && prev->next != nfs;
+				 prev = prev->next)
+					/* nothing else to do */;
+			assert( prev );
+			prev->next = nfs->next;
+		}
+	UNLOCK(nfsGlob.llock);
+
+	nfs->next = 0; /* paranoia */
+	rpcUdpServerDestroy(nfs->server);
+	free(nfs);
+}
+
+/*
+ * Create a Node. The node will
+ * be associated with a particular
+ * mounted NFS identified by 'nfs'
+ * Optionally, a NFS file handle
+ * may be copied into this node.
+ *
+ * ARGS:	nfs of the NFS this node
+ * 			belongs to.
+ * 			NFS file handle identifying
+ * 			this node.
+ * RETURNS:	node on success,
+ * 			NULL on failure with errno
+ * 			set.
+ *
+ * NOTE:	The caller of this routine
+ * 			is responsible for copying
+ * 			a NFS file handle if she
+ * 			choses to pass a NULL fh.
+ *
+ * 			The driver code assumes the
+ * 			a node always has a valid
+ * 			NFS filehandle and file
+ * 			attributes (unless the latter
+ * 			are aged).
+ */
+static NfsNode
+nfsNodeCreate(Nfs nfs, fhandle *fh)
+{
+NfsNode	rval = malloc(sizeof(*rval));
+rtems_interrupt_lock_context lock_context;
+
+#if DEBUG & DEBUG_TRACK_NODES
+	fprintf(stderr,"NFS: creating a node\n");
+#endif
+
+	if (rval) {
+		if (fh)
+			memcpy( &SERP_FILE(rval), fh, sizeof(*fh) );
+		NFS_GLOBAL_ACQUIRE(&lock_context);
+			nfs->nodesInUse++;
+		NFS_GLOBAL_RELEASE(&lock_context);
+		rval->nfs       = nfs;
+		rval->str		= 0;
+	} else {
+		errno = ENOMEM;
+	}
+
+	return rval;
+}
+
+/* destroy a node */
+static void
+nfsNodeDestroy(NfsNode node)
+{
+rtems_interrupt_lock_context lock_context;
+
+#if DEBUG & DEBUG_TRACK_NODES
+	fprintf(stderr,"NFS: destroying a node\n");
+#endif
+#if 0
+	if (!node)
+		return;
+	/* this probably does nothing... */
+  	xdr_free(xdr_serporid, &node->serporid);
+#endif
+
+	NFS_GLOBAL_ACQUIRE(&lock_context);
+		node->nfs->nodesInUse--;
+#if DEBUG & DEBUG_COUNT_NODES
+		if (node->str)
+			node->nfs->stringsInUse--;
+#endif
+	NFS_GLOBAL_RELEASE(&lock_context);
+
+	if (node->str)
+		free(node->str);
+
+	free(node);
+}
+
+/* Clone a given node (AKA copy constructor),
+ * i.e. create an exact copy.
+ *
+ * ARGS:	node to clone
+ * RETURNS:	new node on success
+ * 			NULL on failure with errno set.
+ *
+ * NOTE:	a string attached to 'str'
+ * 			is cloned as well. Outdated
+ * 			attributes (of the new copy
+ * 			only) will be refreshed
+ * 			(if unsuccessful, this could
+ * 			be a reason for failure to
+ * 			clone a node).
+ */
+static NfsNode
+nfsNodeClone(NfsNode node)
+{
+NfsNode rval = nfsNodeCreate(node->nfs, 0);
+
+	if (rval) {
+		*rval = *node;
+
+		/* must clone the string also */
+		if (node->str) {
+			rval->args.name = rval->str = strdup(node->str);
+			if (!rval->str) {
+				errno = ENOMEM;
+				nfsNodeDestroy(rval);
+				return 0;
+			}
+#if DEBUG & DEBUG_COUNT_NODES
+			{ rtems_interrupt_lock_context lock_context;
+			NFS_GLOBAL_ACQUIRE(&lock_context);
+				node->nfs->stringsInUse++;
+			NFS_GLOBAL_RELEASE(&lock_context);
+			}
+#endif
+		}
+
+		/* possibly update the stats */
+		if (updateAttr(rval, 0 /* only if necessary */)) {
+			nfsNodeDestroy(rval);
+			return 0;
+		}
+	}
+	return rval;
+}
+
+/* Initialize the driver.
+ *
+ * ARGS:	depth of the small and big
+ * 			transaction pools, i.e. how
+ * 			many transactions (buffers)
+ * 			should always be kept around.
+ *
+ * 			(If more transactions are needed,
+ * 			they are created and destroyed
+ * 			on the fly).
+ */
+int
+nfsInit(int smallPoolDepth, int bigPoolDepth)
+{
+static int initialised = 0;
+entry	dummy;
+rtems_status_code status;
+
+	if (initialised)
+		return 0;
+
+	initialised = 1;
+
+	fprintf(stderr,
+          "RTEMS-NFS $Release$, "                       \
+          "Till Straumann, Stanford/SLAC/SSRL 2002, " \
+          "See LICENSE file for licensing info.\n");
+
+	/* Get a major number */
+
+	if (RTEMS_SUCCESSFUL != rtems_io_register_driver(0, &drvNfs, &nfsGlob.nfs_major)) {
+		fprintf(stderr,"Registering NFS driver failed - %s\n", strerror(errno));
+		errno = ENOMEM;
+		return -1;
+	}
+
+	if (0==smallPoolDepth)
+		smallPoolDepth = 20;
+	if (0==bigPoolDepth)
+		bigPoolDepth   = 10;
+
+	/* it's crucial to zero out the 'next' pointer
+	 * because it terminates the xdr_entry recursion
+	 *
+	 * we also must make the filename some non-zero
+	 * char pointer!
+	 */
+
+	memset(&dummy, 0, sizeof(dummy));
+
+	dummy.nextentry   = 0;
+	dummy.name        = "somename"; /* guess average length of a filename */
+	dirres_entry_size = xdr_sizeof((xdrproc_t)xdr_entry, &dummy);
+
+	nfsGlob.smallPool = rpcUdpXactPoolCreate(
+		NFS_PROGRAM,
+		NFS_VERSION_2,
+		CONFIG_NFS_SMALL_XACT_SIZE,
+		smallPoolDepth);
+	if (nfsGlob.smallPool == NULL) {
+		goto cleanup;
+	}
+
+	nfsGlob.bigPool = rpcUdpXactPoolCreate(
+		NFS_PROGRAM,
+		NFS_VERSION_2,
+		CONFIG_NFS_BIG_XACT_SIZE,
+		bigPoolDepth);
+	if (nfsGlob.bigPool == NULL) {
+		goto cleanup;
+	}
+
+	status = rtems_semaphore_create(
+		rtems_build_name('N','F','S','l'),
+		1,
+		MUTEX_ATTRIBUTES,
+		0,
+		&nfsGlob.llock);
+	if (status != RTEMS_SUCCESSFUL) {
+		goto cleanup;
+	}
+
+	status = rtems_semaphore_create(
+		rtems_build_name('N','F','S','m'),
+		1,
+		MUTEX_ATTRIBUTES,
+		0,
+		&nfsGlob.lock);
+	if (status != RTEMS_SUCCESSFUL) {
+		goto cleanup;
+	}
+
+	if (sizeof(ino_t) < sizeof(u_int)) {
+		fprintf(stderr,
+			"WARNING: Using 'short st_ino' hits performance and may fail to access/find correct files\n");
+		fprintf(stderr,
+			"you should fix newlib's sys/stat.h - for now I'll enable a hack...\n");
+
+	}
+
+	return 0;
+
+cleanup:
+
+	nfsCleanup();
+	initialised = 0;
+
+	return -1;
+}
+
+/* Driver cleanup code
+ */
+int
+nfsCleanup(void)
+{
+int			refuse;
+
+	if (nfsGlob.llock != 0) {
+		LOCK(nfsGlob.llock);
+		if ( (refuse = nfsGlob.num_mounted_fs) ) {
+			fprintf(stderr,"Refuse to unload NFS; %i filesystems still mounted.\n",
+							refuse);
+			nfsMountsShow(stderr);
+			/* yes, printing is slow - but since you try to unload the driver,
+			 * you assume nobody is using NFS, so what if they have to wait?
+			 */
+			UNLOCK(nfsGlob.llock);
+			return -1;
+		}
+	}
+
+	if (nfsGlob.lock != 0) {
+		rtems_semaphore_delete(nfsGlob.lock);
+		nfsGlob.lock = 0;
+	}
+
+	if (nfsGlob.smallPool != NULL) {
+		rpcUdpXactPoolDestroy(nfsGlob.smallPool);
+		nfsGlob.smallPool = NULL;
+	}
+
+	if (nfsGlob.bigPool != NULL) {
+		rpcUdpXactPoolDestroy(nfsGlob.bigPool);
+		nfsGlob.bigPool = NULL;
+	}
+
+	if (nfsGlob.nfs_major != 0xffffffff) {
+		rtems_io_unregister_driver(nfsGlob.nfs_major);
+		nfsGlob.nfs_major = 0xffffffff;
+	}
+
+	if (nfsGlob.llock != 0) {
+		rtems_semaphore_delete(nfsGlob.llock);
+		nfsGlob.llock = 0;
+	}
+
+	return 0;
+}
+
+/* NFS RPC wrapper.
+ *
+ * ARGS:	srvr	the NFS server we want to call
+ * 			proc	the NFSPROC_xx we want to invoke
+ * 			xargs   xdr routine to wrap the arguments
+ * 			pargs   pointer to the argument object
+ * 			xres	xdr routine to unwrap the results
+ * 			pres	pointer to the result object
+ *
+ * RETURNS:	0 on success, -1 on error with errno set.
+ *
+ * NOTE:	the caller assumes that errno is set to
+ *			a nonzero value if this routine returns
+ *			an error (nonzero return value).
+ *
+ *			This routine prints RPC error messages to
+ *			stderr.
+ */
+STATIC int
+nfscall(
+	RpcUdpServer	srvr,
+	int				proc,
+	xdrproc_t		xargs,
+	void *			pargs,
+	xdrproc_t		xres,
+	void *			pres)
+{
+RpcUdpXact		xact;
+enum clnt_stat	stat;
+RpcUdpXactPool	pool;
+int				rval = -1;
+
+
+	switch (proc) {
+		case NFSPROC_SYMLINK:
+		case NFSPROC_WRITE:
+					pool = nfsGlob.bigPool;		break;
+		default:	pool = nfsGlob.smallPool;	break;
+	}
+
+	xact = rpcUdpXactPoolGet(pool, XactGetCreate);
+
+	if ( !xact ) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	if ( RPC_SUCCESS != (stat=rpcUdpSend(
+								xact,
+								srvr,
+								NFSCALL_TIMEOUT,
+								proc,
+								xres,
+								pres,
+								xargs,
+								pargs,
+								0)) ||
+	     RPC_SUCCESS != (stat=rpcUdpRcv(xact)) ) {
+
+		fprintf(stderr,
+				"NFS (proc %i) - %s\n",
+				proc,
+				clnt_sperrno(stat));
+
+		switch (stat) {
+			/* TODO: this is probably not complete and/or fully accurate */
+			case RPC_CANTENCODEARGS : errno = EINVAL;	break;
+			case RPC_AUTHERROR  	: errno = EPERM;	break;
+
+			case RPC_CANTSEND		:
+			case RPC_CANTRECV		: /* hope they have errno set */
+			case RPC_SYSTEMERROR	: break;
+
+			default             	: errno = EIO;		break;
+		}
+	} else {
+		rval = 0;
+	}
+
+	/* release the transaction back into the pool */
+	rpcUdpXactPoolPut(xact);
+
+	if (rval && !errno)
+		errno = EIO;
+
+	return rval;
+}
+
+/* Check the 'age' of a node's stats
+ * and read the attributes from the server
+ * if necessary.
+ *
+ * ARGS:	node	node to update
+ * 			force	enforce updating ignoring
+ * 					the timestamp/age
+ *
+ * RETURNS:	0 on success,
+ * 			-1 on failure with errno set
+ */
+
+static int
+updateAttr(NfsNode node, int force)
+{
+	int rv = 0;
+
+	if (force
+#ifdef CONFIG_ATTR_LIFETIME
+		|| (nowSeconds() - node->age > CONFIG_ATTR_LIFETIME)
+#endif
+	) {
+		rv = nfscall(
+			node->nfs->server,
+			NFSPROC_GETATTR,
+			(xdrproc_t) xdr_nfs_fh, &SERP_FILE(node),
+			(xdrproc_t) xdr_attrstat, &node->serporid
+		);
+
+		if (rv == 0) {
+			rv = nfsEvaluateStatus(node->serporid.status);
+
+			if (rv == 0) {
+				node->age = nowSeconds();
+			}
+		}
+	}
+
+	return rv;
+}
+
+/*
+ * IP address helper.
+ *
+ * initialize a sockaddr_in from a
+ * [<uid>'.'<gid>'@']<host>':'<path>" string and let
+ * pPath point to the <path> part; retrieve the optional
+ * uid/gids
+ *
+ * ARGS:	see description above
+ *
+ * RETURNS:	0 on success,
+ * 			-1 on failure with errno set
+ */
+static int
+buildIpAddr(u_long *puid, u_long *pgid,
+			char **pHost, struct sockaddr_in *psa,
+			char **pPath)
+{
+struct hostent *h;
+char	host[64];
+char	*chpt = *pPath;
+char	*path;
+int		len;
+
+	if ( !chpt ) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	/* look for the optional uid/gid */
+	if ( (chpt = strchr(chpt, UIDSEP)) ) {
+		if ( 2 != sscanf(*pPath,"%li.%li",puid,pgid) ) {
+			errno = EINVAL;
+			return -1;
+		}
+		chpt++;
+	} else {
+		*puid = geteuid();
+		*pgid = getegid();
+		chpt  = *pPath;
+	}
+	if ( pHost )
+		*pHost = chpt;
+
+	/* split the device name which is in the form
+	 *
+	 * <host> ':' <path>
+	 *
+	 * into its components using a local buffer
+	 */
+
+	if ( !(path = strchr(chpt, HOSTDELIM)) ||
+	      (len  = path - chpt) >= sizeof(host) - 1 ) {
+		errno = EINVAL;
+		return -1;
+	}
+	/* point to path beyond ':' */
+	path++;
+
+	strncpy(host, chpt, len);
+	host[len]=0;
+
+  /* BEGIN OF NON-THREAD SAFE REGION */
+
+	h = gethostbyname(host);
+
+	if ( !h ) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	memcpy(&psa->sin_addr, h->h_addr, sizeof (struct in_addr));
+
+  /* END OF NON-THREAD SAFE REGION */
+
+	psa->sin_family = AF_INET;
+	psa->sin_port   = 0;
+	*pPath          = path;
+	return 0;
+}
+
+/* wrapper similar to nfscall.
+ * However, since it is not used
+ * very often, the simpler and less
+ * efficient rpcUdpCallRp API is used.
+ *
+ * ARGS:	see 'nfscall()' above
+ *
+ * RETURNS:	RPC status
+ */
+static enum clnt_stat
+mntcall(
+	struct sockaddr_in	*psrvr,
+	int					proc,
+	xdrproc_t			xargs,
+	void *				pargs,
+	xdrproc_t			xres,
+	void *				pres,
+	u_long				uid,
+	u_long				gid)
+{
+#ifdef MOUNT_V1_PORT
+int					retry;
+#endif
+enum clnt_stat		stat = RPC_FAILED;
+
+#ifdef MOUNT_V1_PORT
+	/* if the portmapper fails, retry a fixed port */
+	for (retry = 1, psrvr->sin_port = 0, stat = RPC_FAILED;
+		 retry >= 0 && stat;
+		 stat && (psrvr->sin_port = htons(MOUNT_V1_PORT)), retry-- )
+#endif
+		stat  = rpcUdpCallRp(
+						psrvr,
+						MOUNTPROG,
+						MOUNTVERS,
+						proc,
+						xargs,
+						pargs,
+						xres,
+						pres,
+						uid,
+						gid,
+						MNTCALL_TIMEOUT
+				);
+	return stat;
+}
+
+/*****************************************
+	RTEMS File System Operations for NFS
+ *****************************************/
+
+static bool nfs_is_directory(
+	rtems_filesystem_eval_path_context_t *ctx,
+	void *arg
+)
+{
+	bool is_dir = false;
+	rtems_filesystem_location_info_t *currentloc =
+		rtems_filesystem_eval_path_get_currentloc(ctx);
+	NfsNode node = currentloc->node_access;
+	int force_update = 0;
+
+	if (updateAttr(node, force_update) == 0) {
+		is_dir = SERP_ATTR(node).type == NFDIR;
+	}
+
+	return is_dir;
+}
+
+static int nfs_search_in_directory(
+	Nfs nfs,
+	const NfsNode dir,
+	char *part,
+	NfsNode entry
+)
+{
+	int rv;
+
+	entry->nfs = nfs;
+
+	/* lookup one element */
+	SERP_ATTR(entry) = SERP_ATTR(dir);
+	SERP_FILE(entry) = SERP_FILE(dir);
+	SERP_ARGS(entry).diroparg.name = part;
+
+	/* remember args / directory fh */
+	memcpy(&entry->args, &SERP_FILE(dir), sizeof(dir->args));
+
+#if DEBUG & DEBUG_EVALPATH
+	fprintf(stderr,"Looking up '%s'\n",part);
+#endif
+
+	rv = nfscall(
+		nfs->server,
+		NFSPROC_LOOKUP,
+		(xdrproc_t) xdr_diropargs, &SERP_FILE(entry),
+		(xdrproc_t) xdr_serporid,  &entry->serporid
+	);
+
+	if (rv == 0 && entry->serporid.status == NFS_OK) {
+		int force_update = 1;
+
+		rv = updateAttr(entry, force_update);
+	} else {
+		rv = -1;
+	}
+
+	return rv;
+}
+
+static void nfs_eval_follow_link(
+	rtems_filesystem_eval_path_context_t *ctx,
+	NfsNode link
+)
+{
+	const size_t len = NFS_MAXPATHLEN + 1;
+	char *buf = malloc(len);
+
+	if (buf != NULL) {
+		ssize_t rv = nfs_readlink_with_node(link, buf, len);
+
+		if (rv >= 0) {
+			rtems_filesystem_eval_path_recursive(ctx, buf, (size_t) rv);
+		} else {
+			rtems_filesystem_eval_path_error(ctx, 0);
+		}
+
+		free(buf);
+	} else {
+		rtems_filesystem_eval_path_error(ctx, ENOMEM);
+	}
+}
+
+static void nfs_eval_set_handlers(
+	rtems_filesystem_eval_path_context_t *ctx,
+	ftype type
+)
+{
+	rtems_filesystem_location_info_t *currentloc =
+		rtems_filesystem_eval_path_get_currentloc(ctx);
+
+	switch (type) {
+		case NFDIR:
+			currentloc->handlers = &nfs_dir_file_handlers;
+			break;
+		case NFREG:
+			currentloc->handlers = &nfs_file_file_handlers;
+			break;
+		case NFLNK:
+			currentloc->handlers = &nfs_link_file_handlers;
+			break;
+		default:
+			currentloc->handlers = &rtems_filesystem_handlers_default;
+			break;
+	}
+}
+
+static int nfs_move_node(NfsNode dst, const NfsNode src, const char *part)
+{
+	int rv = 0;
+
+	if (dst->str != NULL) {
+#if DEBUG & DEBUG_COUNT_NODES
+		rtems_interrupt_lock_context lock_context;
+		NFS_GLOBAL_ACQUIRE(&lock_context);
+			dst->nfs->stringsInUse--;
+		NFS_GLOBAL_RELEASE(&lock_context);
+#endif
+		free(dst->str);
+	}
+
+	*dst = *src;
+
+	dst->str = dst->args.name = strdup(part);
+	if (dst->str != NULL) {
+#if DEBUG & DEBUG_COUNT_NODES
+		rtems_interrupt_lock_context lock_context;
+		NFS_GLOBAL_ACQUIRE(&lock_context);
+			dst->nfs->stringsInUse++;
+		NFS_GLOBAL_RELEASE(&lock_context);
+#endif
+	} else {
+		rv = -1;
+	}
+
+	return rv;
+}
+
+static rtems_filesystem_eval_path_generic_status nfs_eval_part(
+	rtems_filesystem_eval_path_context_t *ctx,
+	char *part
+)
+{
+	rtems_filesystem_eval_path_generic_status status =
+		RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
+	rtems_filesystem_location_info_t *currentloc =
+		rtems_filesystem_eval_path_get_currentloc(ctx);
+	Nfs nfs = currentloc->mt_entry->fs_info;
+	NfsNode dir = currentloc->node_access;
+	NfsNodeRec entry;
+	int rv = nfs_search_in_directory(nfs, dir, part, &entry);
+
+	if (rv == 0) {
+		bool terminal = !rtems_filesystem_eval_path_has_path(ctx);
+		int eval_flags = rtems_filesystem_eval_path_get_flags(ctx);
+		bool follow_sym_link = (eval_flags & RTEMS_FS_FOLLOW_SYM_LINK) != 0;
+		ftype type = SERP_ATTR(&entry).type;
+
+		rtems_filesystem_eval_path_clear_token(ctx);
+
+		if (type == NFLNK && (follow_sym_link || !terminal)) {
+			nfs_eval_follow_link(ctx, &entry);
+		} else {
+			rv = nfs_move_node(dir, &entry, part);
+			if (rv == 0) {
+				nfs_eval_set_handlers(ctx, type);
+				if (!terminal) {
+					status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
+				}
+			} else {
+				rtems_filesystem_eval_path_error(ctx, ENOMEM);
+			}
+		}
+	} else {
+		status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY;
+	}
+
+	return status;
+}
+
+static rtems_filesystem_eval_path_generic_status nfs_eval_token(
+	rtems_filesystem_eval_path_context_t *ctx,
+	void *arg,
+	const char *token,
+	size_t tokenlen
+)
+{
+	rtems_filesystem_eval_path_generic_status status =
+		RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
+
+	if (rtems_filesystem_is_current_directory(token, tokenlen)) {
+		rtems_filesystem_eval_path_clear_token(ctx);
+		if (rtems_filesystem_eval_path_has_path(ctx)) {
+			status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
+		}
+	} else {
+		char *part = nfs_dupname(token, tokenlen);
+
+		if (part != NULL) {
+			status = nfs_eval_part(ctx, part);
+			free(part);
+		} else {
+			rtems_filesystem_eval_path_error(ctx, ENOMEM);
+		}
+	}
+
+	return status;
+}
+
+static const rtems_filesystem_eval_path_generic_config nfs_eval_config = {
+	.is_directory = nfs_is_directory,
+	.eval_token = nfs_eval_token
+};
+
+static void nfs_eval_path(rtems_filesystem_eval_path_context_t *ctx)
+{
+	rtems_filesystem_eval_path_generic(ctx, NULL, &nfs_eval_config);
+}
+
+/* create a hard link */
+
+static int nfs_link(
+	const rtems_filesystem_location_info_t *parentloc,
+	const rtems_filesystem_location_info_t *targetloc,
+	const char *name,
+	size_t namelen
+)
+{
+int rv = 0;
+NfsNode pNode = parentloc->node_access;
+nfsstat status;
+NfsNode tNode = targetloc->node_access;
+char *dupname;
+
+	dupname = nfs_dupname(name, namelen);
+	if (dupname == NULL)
+		return -1;
+
+#if DEBUG & DEBUG_SYSCALLS
+	fprintf(stderr,"Creating link '%s'\n",dupname);
+#endif
+
+	memcpy(&SERP_ARGS(tNode).linkarg.to.dir,
+		   &SERP_FILE(pNode),
+		   sizeof(SERP_FILE(pNode)));
+
+	SERP_ARGS(tNode).linkarg.to.name = dupname;
+
+	rv = nfscall(
+		tNode->nfs->server,
+		NFSPROC_LINK,
+		(xdrproc_t)xdr_linkargs, &SERP_FILE(tNode),
+		(xdrproc_t)xdr_nfsstat, &status
+	);
+
+	if (rv == 0) {
+		rv = nfsEvaluateStatus(status);
+#if DEBUG & DEBUG_SYSCALLS
+		if (rv != 0) {
+			perror("nfs_link");
+		}
+#endif
+	}
+
+	free(dupname);
+
+	return rv;
+
+}
+
+static int nfs_do_unlink(
+	const rtems_filesystem_location_info_t *parentloc,
+	const rtems_filesystem_location_info_t *loc,
+	int								  proc
+)
+{
+int rv = 0;
+nfsstat			status;
+NfsNode			node  = loc->node_access;
+Nfs				nfs   = node->nfs;
+#if DEBUG & DEBUG_SYSCALLS
+char			*name = NFSPROC_REMOVE == proc ?
+							"nfs_unlink" : "nfs_rmdir";
+#endif
+
+	/* The FS generics have determined that pathloc is _not_
+	 * a directory. Hence we may assume that the parent
+	 * is in our NFS.
+	 */
+
+#if DEBUG & DEBUG_SYSCALLS
+	assert( node->args.name == node->str && node->str );
+
+	fprintf(stderr,"%s '%s'\n", name, node->args.name);
+#endif
+
+	rv = nfscall(
+		nfs->server,
+		proc,
+		(xdrproc_t)xdr_diropargs, &node->args,
+		(xdrproc_t)xdr_nfsstat, &status
+	);
+
+	if (rv == 0) {
+		rv = nfsEvaluateStatus(status);
+#if DEBUG & DEBUG_SYSCALLS
+		if (rv != 0) {
+			perror(name);
+		}
+#endif
+	}
+
+	return rv;
+}
+
+static int nfs_chown(
+	const rtems_filesystem_location_info_t  *pathloc,       /* IN */
+	uid_t                                    owner,         /* IN */
+	gid_t                                    group          /* IN */
+)
+{
+sattr	arg;
+
+	arg.uid = owner;
+	arg.gid = group;
+
+	return nfs_sattr(pathloc->node_access, &arg, SATTR_UID | SATTR_GID);
+
+}
+
+static int nfs_clonenode(rtems_filesystem_location_info_t *loc)
+{
+	NfsNode	node = loc->node_access;
+
+	LOCK(nfsGlob.lock);
+	node = nfsNodeClone(node);
+	UNLOCK(nfsGlob.lock);
+
+	loc->node_access = node;
+
+	return node != NULL ? 0 : -1;
+}
+
+/* Cleanup the FS private info attached to pathloc->node_access */
+static void nfs_freenode(
+	const rtems_filesystem_location_info_t      *pathloc       /* IN */
+)
+{
+#if DEBUG & DEBUG_COUNT_NODES
+Nfs	nfs    = ((NfsNode)pathloc->node_access)->nfs;
+
+	/* print counts at entry where they are > 0 so 'nfs' is safe from being destroyed
+	 * and there's no race condition
+	 */
+	fprintf(stderr,
+			"entering freenode, in use count is %i nodes, %i strings\n",
+			nfs->nodesInUse,
+			nfs->stringsInUse);
+#endif
+
+	nfsNodeDestroy(pathloc->node_access);
+}
+
+/* NOTE/TODO: mounting on top of NFS is not currently supported
+ *
+ * Challenge: stateless protocol. It would be possible to
+ * delete mount points on the server. We would need some sort
+ * of a 'garbage collector' looking for dead/unreachable
+ * mount points and unmounting them.
+ * Also, the path evaluation routine would have to check
+ * for crossing mount points. Crossing over from one NFS
+ * into another NFS could probably handled iteratively
+ * rather than by recursion.
+ */
+
+int rtems_nfs_initialize(
+  rtems_filesystem_mount_table_entry_t *mt_entry,
+  const void                           *data
+)
+{
+char				*host;
+struct sockaddr_in	saddr;
+enum clnt_stat		stat;
+fhstatus			fhstat;
+u_long				uid,gid;
+#ifdef NFS_V2_PORT
+int					retry;
+#endif
+Nfs					nfs       = 0;
+NfsNode				rootNode  = 0;
+RpcUdpServer		nfsServer = 0;
+int					e         = -1;
+char				*path     = mt_entry->dev;
+
+  if (rpcUdpInit () < 0) {
+    fprintf (stderr, "error: initialising RPC\n");
+    return -1;
+  }
+
+	if (nfsInit(0, 0) != 0) {
+		fprintf (stderr, "error: initialising NFS\n");
+		return -1;
+	};
+
+#if 0
+	printf("Trying to mount %s on %s\n",path,mntpoint);
+#endif
+
+	if ( buildIpAddr(&uid, &gid, &host, &saddr, &path) )
+		return -1;
+
+#ifdef NFS_V2_PORT
+	/* if the portmapper fails, retry a fixed port */
+	for (retry = 1, saddr.sin_port = 0, stat = RPC_FAILED;
+		 retry >= 0 && stat;
+		 stat && (saddr.sin_port = htons(NFS_V2_PORT)), retry-- )
+#endif
+		stat = rpcUdpServerCreate(
+					&saddr,
+					NFS_PROGRAM,
+					NFS_VERSION_2,
+					uid,
+					gid,
+					&nfsServer
+					);
+
+	if ( RPC_SUCCESS != stat ) {
+		fprintf(stderr,
+				"Unable to contact NFS server - invalid port? (%s)\n",
+				clnt_sperrno(stat));
+		e = EPROTONOSUPPORT;
+		goto cleanup;
+	}
+
+
+	/* first, try to ping the NFS server by
+	 * calling the NULL proc.
+	 */
+	if ( nfscall(nfsServer,
+					 NFSPROC_NULL,
+					 (xdrproc_t)xdr_void, 0,
+					 (xdrproc_t)xdr_void, 0) ) {
+
+		fputs("NFS Ping ",stderr);
+		fwrite(host, 1, path-host-1, stderr);
+		fprintf(stderr," failed: %s\n", strerror(errno));
+
+		e = errno ? errno : EIO;
+		goto cleanup;
+	}
+
+	/* that seemed to work - we now try the
+	 * actual mount
+	 */
+
+	/* reuse server address but let the mntcall()
+	 * search for the mountd's port
+	 */
+	saddr.sin_port = 0;
+
+	stat = mntcall( &saddr,
+					MOUNTPROC_MNT,
+					(xdrproc_t)xdr_dirpath,
+					&path,
+					(xdrproc_t)xdr_fhstatus,
+					&fhstat,
+				 	uid,
+				 	gid );
+
+	if (stat) {
+		fprintf(stderr,"MOUNT -- %s\n",clnt_sperrno(stat));
+		if ( e<=0 )
+			e = EIO;
+		goto cleanup;
+	} else if (NFS_OK != (e=fhstat.fhs_status)) {
+		fprintf(stderr,"MOUNT: %s\n",strerror(e));
+		goto cleanup;
+	}
+
+	nfs = nfsCreate(nfsServer);
+	assert( nfs );
+	nfsServer = 0;
+
+	nfs->uid  = uid;
+	nfs->gid  = gid;
+
+	/* that seemed to work - we now create the root node
+	 * and we also must obtain the root node attributes
+	 */
+	rootNode = nfsNodeCreate(nfs, &fhstat.fhstatus_u.fhs_fhandle);
+	assert( rootNode );
+
+	if ( updateAttr(rootNode, 1 /* force */) ) {
+		e = errno;
+		goto cleanup;
+	}
+
+	/* looks good so far */
+
+	mt_entry->mt_fs_root->location.node_access = rootNode;
+
+	rootNode = 0;
+
+	mt_entry->ops = &nfs_fs_ops;
+	mt_entry->mt_fs_root->location.handlers	 = &nfs_dir_file_handlers;
+	mt_entry->pathconf_limits_and_options = &nfs_limits_and_options;
+
+	LOCK(nfsGlob.llock);
+		nfsGlob.num_mounted_fs++;
+		/* allocate a new ID for this FS */
+		nfs->id = nfsGlob.fs_ids++;
+	UNLOCK(nfsGlob.llock);
+
+	mt_entry->fs_info				 = nfs;
+	nfs->mt_entry					 = mt_entry;
+	nfs = 0;
+
+	e = 0;
+
+cleanup:
+	if (nfs)
+		nfsDestroy(nfs);
+	if (nfsServer)
+		rpcUdpServerDestroy(nfsServer);
+	if (rootNode)
+		nfsNodeDestroy(rootNode);
+	if (e)
+		rtems_set_errno_and_return_minus_one(e);
+	else
+		return 0;
+}
+
+/* This op is called when they try to unmount THIS fs */
+STATIC void nfs_fsunmount_me(
+	rtems_filesystem_mount_table_entry_t *mt_entry    /* in */
+)
+{
+enum clnt_stat		stat;
+struct sockaddr_in	saddr;
+char			*path = mt_entry->dev;
+int			nodesInUse;
+u_long			uid,gid;
+int			status;
+
+LOCK(nfsGlob.llock);
+	nodesInUse = ((Nfs)mt_entry->fs_info)->nodesInUse;
+
+	if (nodesInUse > 1 /* one ref to the root node used by us */) {
+		UNLOCK(nfsGlob.llock);
+		fprintf(stderr,
+				"Refuse to unmount; there are still %i nodes in use (1 used by us)\n",
+				nodesInUse);
+                rtems_fatal_error_occurred(0xdeadbeef);
+                return;
+	}
+
+	status = buildIpAddr(&uid, &gid, 0, &saddr, &path);
+	assert( !status );
+
+	stat = mntcall( &saddr,
+					MOUNTPROC_UMNT,
+					(xdrproc_t)xdr_dirpath, &path,
+					(xdrproc_t)xdr_void,	 0,
+				    uid,
+				    gid
+				  );
+
+	if (stat) {
+		UNLOCK(nfsGlob.llock);
+		fprintf(stderr,"NFS UMOUNT -- %s\n", clnt_sperrno(stat));
+		return;
+	}
+
+	nfsNodeDestroy(mt_entry->mt_fs_root->location.node_access);
+	mt_entry->mt_fs_root->location.node_access = 0;
+
+	nfsDestroy(mt_entry->fs_info);
+	mt_entry->fs_info = 0;
+
+	nfsGlob.num_mounted_fs--;
+UNLOCK(nfsGlob.llock);
+}
+
+static int nfs_mknod(
+	const rtems_filesystem_location_info_t *parentloc,
+	const char *name,
+	size_t namelen,
+	mode_t mode,
+	dev_t dev
+)
+{
+
+int					rv = 0;
+struct timeval				now;
+diropres				res;
+NfsNode					node = parentloc->node_access;
+Nfs					nfs  = node->nfs;
+mode_t					type = S_IFMT & mode;
+char					*dupname;
+
+	if (type != S_IFDIR && type != S_IFREG)
+		rtems_set_errno_and_return_minus_one(ENOTSUP);
+
+	dupname = nfs_dupname(name, namelen);
+	if (dupname == NULL)
+		return -1;
+
+#if DEBUG & DEBUG_SYSCALLS
+	fprintf(stderr,"nfs_mknod: creating %s\n", dupname);
+#endif
+
+        rtems_clock_get_tod_timeval(&now);
+
+	SERP_ARGS(node).createarg.name       		= dupname;
+	SERP_ARGS(node).createarg.attributes.mode	= mode;
+	SERP_ARGS(node).createarg.attributes.uid	= nfs->uid;
+	SERP_ARGS(node).createarg.attributes.gid	= nfs->gid;
+	SERP_ARGS(node).createarg.attributes.size	= 0;
+	SERP_ARGS(node).createarg.attributes.atime.seconds	= now.tv_sec;
+	SERP_ARGS(node).createarg.attributes.atime.useconds	= now.tv_usec;
+	SERP_ARGS(node).createarg.attributes.mtime.seconds	= now.tv_sec;
+	SERP_ARGS(node).createarg.attributes.mtime.useconds	= now.tv_usec;
+
+	rv = nfscall(
+		nfs->server,
+		(type == S_IFDIR) ? NFSPROC_MKDIR : NFSPROC_CREATE,
+		(xdrproc_t)xdr_createargs, &SERP_FILE(node),
+		(xdrproc_t)xdr_diropres, &res
+	);
+
+	if (rv == 0) {
+		rv = nfsEvaluateStatus(res.status);
+#if DEBUG & DEBUG_SYSCALLS
+		if (rv != 0) {
+			perror("nfs_mknod");
+		}
+#endif
+	}
+
+	free(dupname);
+
+	return rv;
+}
+
+static int nfs_rmnod(
+	const rtems_filesystem_location_info_t *parentloc,
+	const rtems_filesystem_location_info_t *loc
+)
+{
+	int rv = 0;
+	NfsNode	node  = loc->node_access;
+	int force_update = 0;
+
+	if (updateAttr(node, force_update) == 0) {
+		int proc = SERP_ATTR(node).type == NFDIR
+			? NFSPROC_RMDIR
+				: NFSPROC_REMOVE;
+
+		rv = nfs_do_unlink(parentloc, loc, proc);
+	} else {
+		rv = -1;
+	}
+
+	return rv;
+}
+
+static int nfs_utime(
+	const rtems_filesystem_location_info_t  *pathloc, /* IN */
+	time_t                                   actime,  /* IN */
+	time_t                                   modtime  /* IN */
+)
+{
+sattr	arg;
+
+	/* TODO: add rtems EPOCH - UNIX EPOCH seconds */
+	arg.atime.seconds  = actime;
+	arg.atime.useconds = 0;
+	arg.mtime.seconds  = modtime;
+	arg.mtime.useconds = 0;
+
+	return nfs_sattr(pathloc->node_access, &arg, SATTR_ATIME | SATTR_MTIME);
+}
+
+static int nfs_symlink(
+	const rtems_filesystem_location_info_t *parentloc,
+	const char *name,
+	size_t namelen,
+	const char *target
+)
+{
+int					rv = 0;
+struct timeval				now;
+nfsstat					status;
+NfsNode					node = parentloc->node_access;
+Nfs					nfs  = node->nfs;
+char					*dupname;
+
+	dupname = nfs_dupname(name, namelen);
+	if (dupname == NULL)
+		return -1;
+
+#if DEBUG & DEBUG_SYSCALLS
+	fprintf(stderr,"nfs_symlink: creating %s -> %s\n", dupname, target);
+#endif
+
+	rtems_clock_get_tod_timeval(&now);
+
+	SERP_ARGS(node).symlinkarg.name       		= dupname;
+	SERP_ARGS(node).symlinkarg.to				= (nfspath) target;
+
+	SERP_ARGS(node).symlinkarg.attributes.mode	= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
+	SERP_ARGS(node).symlinkarg.attributes.uid	= nfs->uid;
+	SERP_ARGS(node).symlinkarg.attributes.gid	= nfs->gid;
+	SERP_ARGS(node).symlinkarg.attributes.size	= 0;
+	SERP_ARGS(node).symlinkarg.attributes.atime.seconds  = now.tv_sec;
+	SERP_ARGS(node).symlinkarg.attributes.atime.useconds = now.tv_usec;
+	SERP_ARGS(node).symlinkarg.attributes.mtime.seconds  = now.tv_sec;
+	SERP_ARGS(node).symlinkarg.attributes.mtime.useconds = now.tv_usec;
+
+	rv = nfscall(
+		nfs->server,
+		NFSPROC_SYMLINK,
+		(xdrproc_t)xdr_symlinkargs, &SERP_FILE(node),
+		(xdrproc_t)xdr_nfsstat, &status
+	);
+
+	if (rv == 0) {
+		rv = nfsEvaluateStatus(status);
+#if DEBUG & DEBUG_SYSCALLS
+		perror("nfs_symlink");
+#endif
+	}
+
+	free(dupname);
+
+	return rv;
+}
+
+static ssize_t nfs_readlink_with_node(
+	NfsNode node,
+	char *buf,
+	size_t len
+)
+{
+	ssize_t rv;
+	Nfs nfs = node->nfs;
+	readlinkres_strbuf rr;
+
+	rr.strbuf.buf = buf;
+	rr.strbuf.max = len - 1;
+
+	rv = nfscall(
+		nfs->server,
+		NFSPROC_READLINK,
+		(xdrproc_t)xdr_nfs_fh, &SERP_FILE(node),
+		(xdrproc_t)xdr_readlinkres_strbuf, &rr
+	);
+
+	if (rv == 0) {
+		rv = nfsEvaluateStatus(rr.status);
+
+		if (rv == 0) {
+			rv = (ssize_t) strlen(rr.strbuf.buf);
+		} else {
+#if DEBUG & DEBUG_SYSCALLS
+			perror("nfs_readlink_with_node");
+#endif
+		}
+	}
+
+	return rv;
+}
+
+static ssize_t nfs_readlink(
+	const rtems_filesystem_location_info_t *loc,
+	char *buf,
+	size_t len
+)
+{
+	NfsNode	node = loc->node_access;
+
+	return nfs_readlink_with_node(node, buf, len);
+}
+
+static int nfs_rename(
+	const rtems_filesystem_location_info_t *oldparentloc,
+	const rtems_filesystem_location_info_t *oldloc,
+	const rtems_filesystem_location_info_t *newparentloc,
+	const char *name,
+	size_t namelen
+)
+{
+	int rv = 0;
+	char *dupname = nfs_dupname(name, namelen);
+
+	if (dupname != NULL) {
+		NfsNode oldParentNode = oldparentloc->node_access;
+		NfsNode oldNode = oldloc->node_access;
+		NfsNode newParentNode = newparentloc->node_access;
+		Nfs nfs = oldParentNode->nfs;
+		const nfs_fh *toDirSrc = &SERP_FILE(newParentNode);
+		nfs_fh *toDirDst = &SERP_ARGS(oldParentNode).renamearg.to.dir;
+		nfsstat	status;
+
+		SERP_ARGS(oldParentNode).renamearg.name = oldNode->str;
+		SERP_ARGS(oldParentNode).renamearg.to.name = dupname;
+		memcpy(toDirDst, toDirSrc, sizeof(*toDirDst));
+
+		rv = nfscall(
+			nfs->server,
+			NFSPROC_RENAME,
+			(xdrproc_t) xdr_renameargs,
+			&SERP_FILE(oldParentNode),
+			(xdrproc_t) xdr_nfsstat,
+			&status
+		);
+
+		if (rv == 0) {
+			rv = nfsEvaluateStatus(status);
+		}
+
+		free(dupname);
+	} else {
+		rv = -1;
+	}
+
+	return rv;
+}
+
+static void nfs_lock(const rtems_filesystem_mount_table_entry_t *mt_entry)
+{
+}
+
+static void nfs_unlock(const rtems_filesystem_mount_table_entry_t *mt_entry)
+{
+}
+
+static bool nfs_are_nodes_equal(
+	const rtems_filesystem_location_info_t *a,
+	const rtems_filesystem_location_info_t *b
+)
+{
+	bool equal = false;
+	NfsNode na = a->node_access;
+
+	if (updateAttr(na, 0) == 0) {
+		NfsNode nb = b->node_access;
+
+		if (updateAttr(nb, 0) == 0) {
+			equal = SERP_ATTR(na).fileid == SERP_ATTR(nb).fileid
+				&& SERP_ATTR(na).fsid == SERP_ATTR(nb).fsid;
+		}
+	}
+
+	return equal;
+}
+
+static int nfs_fchmod(
+	const rtems_filesystem_location_info_t *loc,
+	mode_t mode
+)
+{
+sattr	arg;
+
+	arg.mode = mode;
+	return nfs_sattr(loc->node_access, &arg, SATTR_MODE);
+
+}
+
+const struct _rtems_filesystem_operations_table nfs_fs_ops = {
+	.lock_h         = nfs_lock,
+	.unlock_h       = nfs_unlock,
+	.eval_path_h    = nfs_eval_path,
+	.link_h         = nfs_link,
+	.are_nodes_equal_h = nfs_are_nodes_equal,
+	.mknod_h        = nfs_mknod,
+	.rmnod_h        = nfs_rmnod,
+	.fchmod_h       = nfs_fchmod,
+	.chown_h        = nfs_chown,
+	.clonenod_h     = nfs_clonenode,
+	.freenod_h      = nfs_freenode,
+	.mount_h        = rtems_filesystem_default_mount,
+	.unmount_h      = rtems_filesystem_default_unmount,
+	.fsunmount_me_h = nfs_fsunmount_me,
+	.utime_h        = nfs_utime,
+	.symlink_h      = nfs_symlink,
+	.readlink_h     = nfs_readlink,
+	.rename_h       = nfs_rename,
+	.statvfs_h      = rtems_filesystem_default_statvfs
+};
+
+/*****************************************
+	File Handlers
+
+	NOTE: the FS generics expect a FS'
+	      evalpath_h() to switch the
+		  pathloc->handlers according
+		  to the pathloc/node's file
+		  type.
+		  We currently have 'file' and
+		  'directory' handlers and very
+		  few 'symlink' handlers.
+
+		  The handlers for each type are
+		  implemented or #defined ZERO
+		  in a 'nfs_file_xxx',
+		  'nfs_dir_xxx', 'nfs_link_xxx'
+		  sequence below this point.
+
+		  In some cases, a common handler,
+		  can be used for all file types.
+		  It is then simply called
+		  'nfs_xxx'.
+ *****************************************/
+
+/* stateless NFS protocol makes this trivial */
+static int nfs_file_open(
+	rtems_libio_t *iop,
+	const char    *pathname,
+	int           oflag,
+	mode_t        mode
+)
+{
+	return 0;
+}
+
+/* reading directories is not stateless; we must
+ * remember the last 'read' position, i.e.
+ * the server 'cookie'. We do manage this information
+ * attached to the pathinfo.node_access_2.
+ */
+static int nfs_dir_open(
+	rtems_libio_t *iop,
+	const char    *pathname,
+	int           oflag,
+	mode_t        mode
+)
+{
+NfsNode		node = iop->pathinfo.node_access;
+DirInfo		di;
+
+	/* create a readdirargs object and copy the file handle;
+	 * attach to the pathinfo.node_access_2
+	 */
+
+	di = (DirInfo) malloc(sizeof(*di));
+	iop->pathinfo.node_access_2 = di;
+
+	if ( !di  ) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	memcpy( &di->readdirargs.dir,
+			&SERP_FILE(node),
+			sizeof(di->readdirargs.dir) );
+
+	/* rewind cookie */
+	memset( &di->readdirargs.cookie,
+	        0,
+	        sizeof(di->readdirargs.cookie) );
+
+	di->eofreached = FALSE;
+
+	return 0;
+}
+
+static int nfs_file_close(
+	rtems_libio_t *iop
+)
+{
+	return 0;
+}
+
+static int nfs_dir_close(
+	rtems_libio_t *iop
+)
+{
+	free(iop->pathinfo.node_access_2);
+	iop->pathinfo.node_access_2 = 0;
+	return 0;
+}
+
+static ssize_t nfs_file_read_chunk(
+	NfsNode node,
+	uint32_t offset,
+	void *buffer,
+	size_t count
+)
+{
+ssize_t rv;
+readres	rr;
+Nfs		nfs  = node->nfs;
+
+	SERP_ARGS(node).readarg.offset		= offset;
+	SERP_ARGS(node).readarg.count	  	= count;
+	SERP_ARGS(node).readarg.totalcount	= UINT32_C(0xdeadbeef);
+
+	rr.readres_u.reply.data.data_val	= buffer;
+
+	rv = nfscall(
+		nfs->server,
+		NFSPROC_READ,
+		(xdrproc_t)xdr_readargs, &SERP_FILE(node),
+		(xdrproc_t)xdr_readres, &rr
+	);
+
+	if (rv == 0) {
+		rv = nfsEvaluateStatus(rr.status);
+
+		if (rv == 0) {
+			rv = rr.readres_u.reply.data.data_len;
+
+#if DEBUG & DEBUG_SYSCALLS
+			fprintf(stderr,
+				"Read %i (asked for %i) bytes from offset %i to 0x%08x\n",
+				rr.readres_u.reply.data.data_len,
+				count,
+				iop->offset,
+				rr.readres_u.reply.data.data_val);
+#endif
+		}
+	}
+
+	return rv;
+}
+
+static ssize_t nfs_file_read(
+	rtems_libio_t *iop,
+	void *buffer,
+	size_t count
+)
+{
+	ssize_t rv = 0;
+	NfsNode node = iop->pathinfo.node_access;
+	uint32_t offset = iop->offset;
+	char *in = buffer;
+
+	if (iop->offset < 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((uintmax_t) iop->offset >= UINT32_MAX) {
+		errno = EFBIG;
+		return -1;
+	}
+
+	if (count > UINT32_MAX - offset) {
+		count = UINT32_MAX - offset;
+	}
+
+	do {
+		size_t chunk = count <= NFS_MAXDATA ? count : NFS_MAXDATA;
+		ssize_t done = nfs_file_read_chunk(node, offset, in, chunk);
+
+		if (done > 0) {
+			offset += (uint32_t) done;
+			in += done;
+			count -= (size_t) done;
+			rv += done;
+		} else {
+			count = 0;
+			if (done < 0) {
+				rv = -1;
+			}
+		}
+	} while (count > 0);
+
+	if (rv > 0) {
+		iop->offset = offset;
+	}
+
+	return rv;
+}
+
+/* this is called by readdir() / getdents() */
+static ssize_t nfs_dir_read(
+	rtems_libio_t *iop,
+	void          *buffer,
+	size_t        count
+)
+{
+ssize_t rv;
+DirInfo			di     = iop->pathinfo.node_access_2;
+RpcUdpServer	server = ((Nfs)iop->pathinfo.mt_entry->fs_info)->server;
+
+	if ( di->eofreached )
+		return 0;
+
+	di->ptr = di->buf = buffer;
+
+	/* align + round down the buffer */
+	count &= ~ (DIRENT_HEADER_SIZE - 1);
+	di->len = count;
+
+#if 0
+	/* now estimate the number of entries we should ask for */
+	count /= DIRENT_HEADER_SIZE + CONFIG_AVG_NAMLEN;
+
+	/* estimate the encoded size that might take up */
+	count *= dirres_entry_size + CONFIG_AVG_NAMLEN;
+#else
+	/* integer arithmetics are better done the other way round */
+	count *= dirres_entry_size + CONFIG_AVG_NAMLEN;
+	count /= DIRENT_HEADER_SIZE + CONFIG_AVG_NAMLEN;
+#endif
+
+	if (count > NFS_MAXDATA)
+		count = NFS_MAXDATA;
+
+	di->readdirargs.count = count;
+
+#if DEBUG & DEBUG_READDIR
+	fprintf(stderr,
+			"Readdir: asking for %i XDR bytes, buffer is %i\n",
+			count, di->len);
+#endif
+
+	rv = nfscall(
+		server,
+		NFSPROC_READDIR,
+		(xdrproc_t)xdr_readdirargs, &di->readdirargs,
+		(xdrproc_t)xdr_dir_info, di
+	);
+
+	if (rv == 0) {
+		rv = nfsEvaluateStatus(di->status);
+
+		if (rv == 0) {
+			rv = (char*)di->ptr - (char*)buffer;
+		}
+	}
+
+	return rv;
+}
+
+static ssize_t nfs_file_write(
+	rtems_libio_t *iop,
+	const void    *buffer,
+	size_t        count
+)
+{
+ssize_t rv;
+NfsNode 	node = iop->pathinfo.node_access;
+Nfs			nfs  = node->nfs;
+
+	if (count > NFS_MAXDATA)
+		count = NFS_MAXDATA;
+
+
+	SERP_ARGS(node).writearg.beginoffset = UINT32_C(0xdeadbeef);
+	if ( LIBIO_FLAGS_APPEND & iop->flags ) {
+		if ( updateAttr(node, 0) ) {
+			return -1;
+		}
+		if (SERP_ATTR(node).size >= UINT32_MAX) {
+			errno = EFBIG;
+			return -1;
+		}
+		SERP_ARGS(node).writearg.offset = SERP_ATTR(node).size;
+	} else {
+		if (iop->offset < 0) {
+			errno = EINVAL;
+			return -1;
+		}
+		if ((uintmax_t) iop->offset >= UINT32_MAX) {
+			errno = EFBIG;
+			return -1;
+		}
+		SERP_ARGS(node).writearg.offset = iop->offset;
+	}
+
+	if (count > UINT32_MAX - SERP_ARGS(node).writearg.offset) {
+		count = UINT32_MAX - SERP_ARGS(node).writearg.offset;
+	}
+
+	SERP_ARGS(node).writearg.totalcount	   = UINT32_C(0xdeadbeef);
+	SERP_ARGS(node).writearg.data.data_len = count;
+	SERP_ARGS(node).writearg.data.data_val = (void*)buffer;
+
+	/* write XDR buffer size will be chosen by nfscall based
+	 * on the PROC specifier
+	 */
+
+	rv = nfscall(
+		nfs->server,
+		NFSPROC_WRITE,
+		(xdrproc_t)xdr_writeargs, &SERP_FILE(node),
+		(xdrproc_t)xdr_attrstat, &node->serporid
+	);
+
+	if (rv == 0) {
+		rv = nfsEvaluateStatus(node->serporid.status);
+
+		if (rv == 0) {
+			node->age = nowSeconds();
+
+			iop->offset += count;
+			rv = count;
+		} else {
+			/* try at least to recover the current attributes */
+			updateAttr(node, 1 /* force */);
+		}
+	}
+
+	return rv;
+}
+
+static off_t nfs_dir_lseek(
+	rtems_libio_t *iop,
+	off_t          length,
+	int            whence
+)
+{
+	off_t rv = rtems_filesystem_default_lseek_directory(iop, length, whence);
+
+	if (rv == 0) {
+		DirInfo di = iop->pathinfo.node_access_2;
+		nfscookie *cookie = &di->readdirargs.cookie;
+
+		di->eofreached = FALSE;
+
+		/* rewind cookie */
+		memset(cookie, 0, sizeof(*cookie));
+	}
+
+	return rv;
+}
+
+#if 0	/* structure types for reference */
+struct fattr {
+		ftype type;
+		u_int mode;
+		u_int nlink;
+		u_int uid;
+		u_int gid;
+		u_int size;
+		u_int blocksize;
+		u_int rdev;
+		u_int blocks;
+		u_int fsid;
+		u_int fileid;
+		nfstime atime;
+		nfstime mtime;
+		nfstime ctime;
+};
+
+struct  stat
+{
+		dev_t         st_dev;
+		ino_t         st_ino;
+		mode_t        st_mode;
+		nlink_t       st_nlink;
+		uid_t         st_uid;
+		gid_t         st_gid;
+		dev_t         st_rdev;
+		off_t         st_size;
+		/* SysV/sco doesn't have the rest... But Solaris, eabi does.  */
+#if defined(__svr4__) && !defined(__PPC__) && !defined(__sun__)
+		time_t        st_atime;
+		time_t        st_mtime;
+		time_t        st_ctime;
+#else
+		time_t        st_atime;
+		long          st_spare1;
+		time_t        st_mtime;
+		long          st_spare2;
+		time_t        st_ctime;
+		long          st_spare3;
+		long          st_blksize;
+		long          st_blocks;
+		long      st_spare4[2];
+#endif
+};
+#endif
+
+/* common for file/dir/link */
+static int nfs_fstat(
+	const rtems_filesystem_location_info_t *loc,
+	struct stat *buf
+)
+{
+NfsNode	node = loc->node_access;
+fattr	*fa  = &SERP_ATTR(node);
+
+	if (updateAttr(node, 0 /* only if old */)) {
+		return -1;
+	}
+
+/* done by caller
+	memset(buf, 0, sizeof(*buf));
+ */
+
+	/* translate */
+
+	/* one of the branches hopefully is optimized away */
+	if (sizeof(ino_t) < sizeof(u_int)) {
+	buf->st_dev		= NFS_MAKE_DEV_T_INO_HACK((NfsNode)loc->node_access);
+	} else {
+	buf->st_dev		= NFS_MAKE_DEV_T((NfsNode)loc->node_access);
+	}
+	buf->st_mode	= fa->mode;
+	buf->st_nlink	= fa->nlink;
+	buf->st_uid		= fa->uid;
+	buf->st_gid		= fa->gid;
+	buf->st_size	= fa->size;
+	/* Set to "preferred size" of this NFS client implementation */
+	buf->st_blksize	= nfsStBlksize ? nfsStBlksize : fa->blocksize;
+	buf->st_rdev	= fa->rdev;
+	buf->st_blocks	= fa->blocks;
+	buf->st_ino     = fa->fileid;
+	buf->st_atime	= fa->atime.seconds;
+	buf->st_mtime	= fa->mtime.seconds;
+	buf->st_ctime	= fa->ctime.seconds;
+
+#if 0 /* NFS should return the modes */
+	switch(fa->type) {
+		default:
+		case NFNON:
+		case NFBAD:
+				break;
+
+		case NFSOCK: buf->st_mode |= S_IFSOCK; break;
+		case NFFIFO: buf->st_mode |= S_IFIFO;  break;
+		case NFREG : buf->st_mode |= S_IFREG;  break;
+		case NFDIR : buf->st_mode |= S_IFDIR;  break;
+		case NFBLK : buf->st_mode |= S_IFBLK;  break;
+		case NFCHR : buf->st_mode |= S_IFCHR;  break;
+		case NFLNK : buf->st_mode |= S_IFLNK;  break;
+	}
+#endif
+
+	return 0;
+}
+
+/* a helper which does the real work for
+ * a couple of handlers (such as chmod,
+ * ftruncate or utime)
+ */
+static int
+nfs_sattr(NfsNode node, sattr *arg, u_long mask)
+{
+int rv;
+struct timeval				now;
+nfstime					nfsnow, t;
+u_int					mode;
+
+	if (updateAttr(node, 0 /* only if old */))
+		return -1;
+
+        rtems_clock_get_tod_timeval(&now);
+
+	/* TODO: add rtems EPOCH - UNIX EPOCH seconds */
+	nfsnow.seconds  = now.tv_sec;
+	nfsnow.useconds = now.tv_usec;
+
+	/* merge permission bits into existing type bits */
+	mode = SERP_ATTR(node).mode;
+	if (mask & SATTR_MODE) {
+		mode &= S_IFMT;
+		mode |= arg->mode & ~S_IFMT;
+	} else {
+		mode = -1;
+	}
+	SERP_ARGS(node).sattrarg.attributes.mode  = mode;
+
+	SERP_ARGS(node).sattrarg.attributes.uid	  =
+		(mask & SATTR_UID)  ? arg->uid : -1;
+
+	SERP_ARGS(node).sattrarg.attributes.gid	  =
+		(mask & SATTR_GID)  ? arg->gid : -1;
+
+	SERP_ARGS(node).sattrarg.attributes.size  =
+		(mask & SATTR_SIZE) ? arg->size : -1;
+
+	if (mask & SATTR_ATIME)
+		t = arg->atime;
+	else if (mask & SATTR_TOUCHA)
+		t = nfsnow;
+	else
+		t.seconds = t.useconds = -1;
+	SERP_ARGS(node).sattrarg.attributes.atime = t;
+
+	if (mask & SATTR_ATIME)
+		t = arg->mtime;
+	else if (mask & SATTR_TOUCHA)
+		t = nfsnow;
+	else
+		t.seconds = t.useconds = -1;
+	SERP_ARGS(node).sattrarg.attributes.mtime = t;
+
+	node->serporid.status = NFS_OK;
+
+	rv = nfscall(
+		node->nfs->server,
+		NFSPROC_SETATTR,
+		(xdrproc_t)xdr_sattrargs, &SERP_FILE(node),
+		(xdrproc_t)xdr_attrstat, &node->serporid
+	);
+
+	if (rv == 0) {
+		rv = nfsEvaluateStatus(node->serporid.status);
+
+		if (rv == 0) {
+			node->age = nowSeconds();
+		} else {
+#if DEBUG & DEBUG_SYSCALLS
+			fprintf(stderr,"nfs_sattr: %s\n",strerror(errno));
+#endif
+			/* try at least to recover the current attributes */
+			updateAttr(node, 1 /* force */);
+		}
+	} else {
+#if DEBUG & DEBUG_SYSCALLS
+		fprintf(stderr,
+				"nfs_sattr (mask 0x%08x): %s",
+				mask,
+				strerror(errno));
+#endif
+	}
+
+	return rv;
+}
+
+/* just set the size attribute to 'length'
+ * the server will take care of the rest :-)
+ */
+static int nfs_file_ftruncate(
+	rtems_libio_t *iop,
+	off_t          length
+)
+{
+sattr					arg;
+
+	if (length < 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((uintmax_t) length > UINT32_MAX) {
+		errno = EFBIG;
+		return -1;
+	}
+
+	arg.size = length;
+	/* must not modify any other attribute; if we are not the owner
+	 * of the file or directory but only have write access changing
+	 * any attribute besides 'size' will fail...
+	 */
+	return nfs_sattr(iop->pathinfo.node_access,
+					 &arg,
+					 SATTR_SIZE);
+}
+
+/* the file handlers table */
+static const
+struct _rtems_filesystem_file_handlers_r nfs_file_file_handlers = {
+	.open_h      = nfs_file_open,
+	.close_h     = nfs_file_close,
+	.read_h      = nfs_file_read,
+	.write_h     = nfs_file_write,
+	.ioctl_h     = rtems_filesystem_default_ioctl,
+	.lseek_h     = rtems_filesystem_default_lseek_file,
+	.fstat_h     = nfs_fstat,
+	.ftruncate_h = nfs_file_ftruncate,
+	.fsync_h     = rtems_filesystem_default_fsync_or_fdatasync,
+	.fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
+	.fcntl_h     = rtems_filesystem_default_fcntl,
+	.kqfilter_h  = rtems_filesystem_default_kqfilter,
+	.poll_h      = rtems_filesystem_default_poll,
+	.readv_h     = rtems_filesystem_default_readv,
+	.writev_h    = rtems_filesystem_default_writev
+};
+
+/* the directory handlers table */
+static const
+struct _rtems_filesystem_file_handlers_r nfs_dir_file_handlers = {
+	.open_h      = nfs_dir_open,
+	.close_h     = nfs_dir_close,
+	.read_h      = nfs_dir_read,
+	.write_h     = rtems_filesystem_default_write,
+	.ioctl_h     = rtems_filesystem_default_ioctl,
+	.lseek_h     = nfs_dir_lseek,
+	.fstat_h     = nfs_fstat,
+	.ftruncate_h = rtems_filesystem_default_ftruncate_directory,
+	.fsync_h     = rtems_filesystem_default_fsync_or_fdatasync,
+	.fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
+	.fcntl_h     = rtems_filesystem_default_fcntl,
+	.kqfilter_h  = rtems_filesystem_default_kqfilter,
+	.poll_h      = rtems_filesystem_default_poll,
+	.readv_h     = rtems_filesystem_default_readv,
+	.writev_h    = rtems_filesystem_default_writev
+};
+
+/* the link handlers table */
+static const
+struct _rtems_filesystem_file_handlers_r nfs_link_file_handlers = {
+	.open_h      = rtems_filesystem_default_open,
+	.close_h     = rtems_filesystem_default_close,
+	.read_h      = rtems_filesystem_default_read,
+	.write_h     = rtems_filesystem_default_write,
+	.ioctl_h     = rtems_filesystem_default_ioctl,
+	.lseek_h     = rtems_filesystem_default_lseek,
+	.fstat_h     = nfs_fstat,
+	.ftruncate_h = rtems_filesystem_default_ftruncate,
+	.fsync_h     = rtems_filesystem_default_fsync_or_fdatasync,
+	.fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
+	.fcntl_h     = rtems_filesystem_default_fcntl,
+	.kqfilter_h  = rtems_filesystem_default_kqfilter,
+	.poll_h      = rtems_filesystem_default_poll,
+	.readv_h     = rtems_filesystem_default_readv,
+	.writev_h    = rtems_filesystem_default_writev
+};
+
+/* we need a dummy driver entry table to get a
+ * major number from the system
+ */
+static
+rtems_device_driver nfs_initialize(
+		rtems_device_major_number	major,
+		rtems_device_minor_number	minor,
+		void						*arg
+)
+{
+	/* we don't really use this routine because
+     * we cannot supply an argument (contrary
+     * to what the 'arg' parameter suggests - it
+     * is always set to 0 by the generics :-()
+     * and because we don't want the user to
+     * have to deal with the major number (which
+     * OTOH is something WE are interested in. The
+     * only reason for using this API was getting
+     * a major number, after all).
+     *
+	 * Something must be present, however, to
+	 * reserve a slot in the driver table.
+	 */
+	return RTEMS_SUCCESSFUL;
+}
+
+static rtems_driver_address_table	drvNfs = {
+		nfs_initialize,
+		0,					/* open    */
+		0,					/* close   */
+		0,					/* read    */
+		0,					/* write   */
+		0					/* control */
+};
+
+/* Dump a list of the currently mounted NFS to a  file */
+int
+nfsMountsShow(FILE *f)
+{
+char	*mntpt = 0;
+Nfs		nfs;
+
+	if (!f)
+		f = stdout;
+
+	if ( !(mntpt=malloc(MAXPATHLEN)) ) {
+		fprintf(stderr,"nfsMountsShow(): no memory\n");
+		return -1;
+	}
+
+	fprintf(f,"Currently Mounted NFS:\n");
+
+	LOCK(nfsGlob.llock);
+
+	for (nfs = nfsGlob.mounted_fs; nfs; nfs=nfs->next) {
+		fprintf(f,"%s on ", nfs->mt_entry->dev);
+		if (rtems_filesystem_resolve_location(mntpt, MAXPATHLEN, &nfs->mt_entry->mt_fs_root->location))
+			fprintf(f,"<UNABLE TO LOOKUP MOUNTPOINT>\n");
+		else
+			fprintf(f,"%s\n",mntpt);
+	}
+
+	UNLOCK(nfsGlob.llock);
+
+	free(mntpt);
+	return 0;
+}
+
+#if 0
+CCJ_REMOVE_MOUNT
+/* convenience wrapper
+ *
+ * NOTE: this routine calls NON-REENTRANT
+ *       gethostbyname() if the host is
+ *       not in 'dot' notation.
+ */
+int
+nfsMount(char *uidhost, char *path, char *mntpoint)
+{
+struct stat								st;
+int										devl;
+char									*host;
+int										rval = -1;
+char									*dev =  0;
+
+	if (!uidhost || !path || !mntpoint) {
+		fprintf(stderr,"usage: nfsMount(""[uid.gid@]host"",""path"",""mountpoint"")\n");
+		nfsMountsShow(stderr);
+		return -1;
+	}
+
+	if ( !(dev = malloc((devl=strlen(uidhost) + 20 + strlen(path)+1))) ) {
+		fprintf(stderr,"nfsMount: out of memory\n");
+		return -1;
+	}
+
+	/* Try to create the mount point if nonexistent */
+	if (stat(mntpoint, &st)) {
+		if (ENOENT != errno) {
+			perror("nfsMount trying to create mount point - stat failed");
+			goto cleanup;
+		} else if (mkdir(mntpoint,0777)) {
+			perror("nfsMount trying to create mount point");
+			goto cleanup;
+		}
+	}
+
+	if ( !(host=strchr(uidhost,UIDSEP)) ) {
+		host = uidhost;
+	} else {
+		host++;
+	}
+
+	if (isdigit((unsigned char)*host)) {
+		/* avoid using gethostbyname */
+		sprintf(dev,"%s:%s",uidhost,path);
+	} else {
+		struct hostent *h;
+
+		/* copy the uid part (hostname will be
+		 * overwritten)
+		 */
+		strcpy(dev, uidhost);
+
+		/* NOTE NOTE NOTE: gethostbyname is NOT
+		 * thread safe. This is UGLY
+		 */
+
+/* BEGIN OF NON-THREAD SAFE REGION */
+
+		h = gethostbyname(host);
+
+		if ( !h ||
+			 !inet_ntop( AF_INET,
+					     (struct in_addr*)h->h_addr_list[0],
+						 dev  + (host - uidhost),
+						 devl - (host - uidhost) )
+			) {
+			fprintf(stderr,"nfsMount: host '%s' not found\n",host);
+			goto cleanup;
+		}
+
+/* END OF NON-THREAD SAFE REGION */
+
+		/* append ':<path>' */
+		strcat(dev,":");
+		strcat(dev,path);
+	}
+
+	printf("Trying to mount %s on %s\n",dev,mntpoint);
+
+	if (mount(dev,
+			  mntpoint,
+			  "nfs",
+ 			  RTEMS_FILESYSTEM_READ_WRITE,
+ 			  NULL)) {
+		perror("nfsMount - mount");
+		goto cleanup;
+	}
+
+	rval = 0;
+
+cleanup:
+	free(dev);
+	return rval;
+}
+#endif
+
+/* HERE COMES A REALLY UGLY HACK */
+
+/* This is stupid; it is _very_ hard to find the path
+ * leading to a rtems_filesystem_location_info_t node :-(
+ * The only easy way is making the location the current
+ * directory and issue a getcwd().
+ * However, since we don't want to tamper with the
+ * current directory, we must create a separate
+ * task to do the job for us - sigh.
+ */
+
+typedef struct ResolvePathArgRec_ {
+	rtems_filesystem_location_info_t	*loc;	/* IN: location to resolve	*/
+	char								*buf;	/* IN/OUT: buffer where to put the path */
+	int									len;	/* IN: buffer length		*/
+	rtems_id							sync;	/* IN: synchronization		*/
+	rtems_status_code					status; /* OUT: result				*/
+} ResolvePathArgRec, *ResolvePathArg;
+
+static void
+resolve_path(rtems_task_argument arg)
+{
+ResolvePathArg						rpa = (ResolvePathArg)arg;
+rtems_filesystem_location_info_t	old;
+
+	/* IMPORTANT: let the helper task have its own libio environment (i.e. cwd) */
+	if (RTEMS_SUCCESSFUL == (rpa->status = rtems_libio_set_private_env())) {
+
+		old = rtems_filesystem_current->location;
+
+		rtems_filesystem_current->location = *(rpa->loc);
+
+		if ( !getcwd(rpa->buf, rpa->len) )
+			rpa->status = RTEMS_UNSATISFIED;
+
+		/* must restore the cwd because 'freenode' will be called on it */
+		rtems_filesystem_current->location = old;
+	}
+	rtems_semaphore_release(rpa->sync);
+	rtems_task_delete(RTEMS_SELF);
+}
+
+
+/* a utility routine to find the path leading to a
+ * rtems_filesystem_location_info_t node
+ *
+ * INPUT: 'loc' and a buffer 'buf' (length 'len') to hold the
+ *        path.
+ * OUTPUT: path copied into 'buf'
+ *
+ * RETURNS: 0 on success, RTEMS error code on error.
+ */
+rtems_status_code
+rtems_filesystem_resolve_location(char *buf, int len, rtems_filesystem_location_info_t *loc)
+{
+ResolvePathArgRec	arg;
+rtems_id			tid = 0;
+rtems_task_priority	pri;
+rtems_status_code	status;
+
+	arg.loc  = loc;
+	arg.buf  = buf;
+	arg.len  = len;
+	arg.sync = 0;
+
+	status = rtems_semaphore_create(
+					rtems_build_name('r','e','s','s'),
+					0,
+					RTEMS_SIMPLE_BINARY_SEMAPHORE,
+					0,
+					&arg.sync);
+
+	if (RTEMS_SUCCESSFUL != status)
+		goto cleanup;
+
+	rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &pri);
+
+	status = rtems_task_create(
+					rtems_build_name('r','e','s','s'),
+					pri,
+					RTEMS_MINIMUM_STACK_SIZE + 50000,
+					RTEMS_DEFAULT_MODES,
+					RTEMS_DEFAULT_ATTRIBUTES,
+					&tid);
+
+	if (RTEMS_SUCCESSFUL != status)
+		goto cleanup;
+
+	status = rtems_task_start(tid, resolve_path, (rtems_task_argument)&arg);
+
+	if (RTEMS_SUCCESSFUL != status) {
+		rtems_task_delete(tid);
+		goto cleanup;
+	}
+
+
+	/* synchronize with the helper task */
+	rtems_semaphore_obtain(arg.sync, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+
+	status = arg.status;
+
+cleanup:
+	if (arg.sync)
+		rtems_semaphore_delete(arg.sync);
+
+	return status;
+}
+
+int
+nfsSetTimeout(uint32_t timeout_ms)
+{
+rtems_interrupt_lock_context lock_context;
+uint32_t	          s,us;
+
+	if ( timeout_ms > 100000 ) {
+		/* out of range */
+		return -1;
+	}
+
+	s  = timeout_ms/1000;
+	us = (timeout_ms % 1000) * 1000;
+
+	NFS_GLOBAL_ACQUIRE(&lock_context);
+	_nfscalltimeout.tv_sec  = s;
+	_nfscalltimeout.tv_usec = us;
+	NFS_GLOBAL_RELEASE(&lock_context);
+
+	return 0;
+}
+
+uint32_t
+nfsGetTimeout( void )
+{
+rtems_interrupt_lock_context lock_context;
+uint32_t              s,us;
+	NFS_GLOBAL_ACQUIRE(&lock_context);
+	s  = _nfscalltimeout.tv_sec;
+	us = _nfscalltimeout.tv_usec;
+	NFS_GLOBAL_RELEASE(&lock_context);
+	return s*1000 + us/1000;
+}
Index: rtemsbsd/nfsclient/nfs_prot.h
===================================================================
--- rtemsbsd/nfsclient/nfs_prot.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/nfs_prot.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,460 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _NFS_PROT_H_RPCGEN
+#define _NFS_PROT_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+/**
+ *  @defgroup libfs_nfsclient_nfs_prot NFS Prot
+ *
+ *  @ingroup libfs
+ */
+/**@{*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NFS_PORT 2049
+#define NFS_MAXDATA 8192
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_FHSIZE 32
+#define NFS_COOKIESIZE 4
+#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
+
+enum nfsstat {
+	NFS_OK = 0,
+	NFSERR_PERM = 1,
+	NFSERR_NOENT = 2,
+	NFSERR_IO = 5,
+	NFSERR_NXIO = 6,
+	NFSERR_ACCES = 13,
+	NFSERR_EXIST = 17,
+	NFSERR_NODEV = 19,
+	NFSERR_NOTDIR = 20,
+	NFSERR_ISDIR = 21,
+	NFSERR_FBIG = 27,
+	NFSERR_NOSPC = 28,
+	NFSERR_ROFS = 30,
+	NFSERR_NAMETOOLONG = 63,
+	NFSERR_NOTEMPTY = 66,
+	NFSERR_DQUOT = 69,
+	NFSERR_STALE = 70,
+	NFSERR_WFLUSH = 99,
+	_NFSSTAT = 0xffffffff
+};
+typedef enum nfsstat nfsstat;
+
+enum ftype {
+	NFNON = 0,
+	NFREG = 1,
+	NFDIR = 2,
+	NFBLK = 3,
+	NFCHR = 4,
+	NFLNK = 5,
+	NFSOCK = 6,
+	NFBAD = 7,
+	NFFIFO = 8,
+	_FTYPE = 0xffffffff
+};
+typedef enum ftype ftype;
+
+struct nfs_fh {
+	char data[NFS_FHSIZE];
+};
+typedef struct nfs_fh nfs_fh;
+
+struct nfstime {
+	u_int seconds;
+	u_int useconds;
+};
+typedef struct nfstime nfstime;
+
+struct fattr {
+	ftype type;
+	u_int mode;
+	u_int nlink;
+	u_int uid;
+	u_int gid;
+	u_int size;
+	u_int blocksize;
+	u_int rdev;
+	u_int blocks;
+	u_int fsid;
+	u_int fileid;
+	nfstime atime;
+	nfstime mtime;
+	nfstime ctime;
+};
+typedef struct fattr fattr;
+
+struct sattr {
+	u_int mode;
+	u_int uid;
+	u_int gid;
+	u_int size;
+	nfstime atime;
+	nfstime mtime;
+};
+typedef struct sattr sattr;
+
+typedef char *filename;
+
+typedef char *nfspath;
+
+struct attrstat {
+	nfsstat status;
+	union {
+		fattr attributes;
+	} attrstat_u;
+};
+typedef struct attrstat attrstat;
+
+struct sattrargs {
+	nfs_fh file;
+	sattr attributes;
+};
+typedef struct sattrargs sattrargs;
+
+struct diropargs {
+	nfs_fh dir;
+	filename name;
+};
+typedef struct diropargs diropargs;
+
+struct diropokres {
+	nfs_fh file;
+	fattr attributes;
+};
+typedef struct diropokres diropokres;
+
+struct diropres {
+	nfsstat status;
+	union {
+		diropokres diropres;
+	} diropres_u;
+};
+typedef struct diropres diropres;
+
+struct readlinkres {
+	nfsstat status;
+	union {
+		nfspath data;
+	} readlinkres_u;
+};
+typedef struct readlinkres readlinkres;
+
+struct readargs {
+	nfs_fh file;
+	u_int offset;
+	u_int count;
+	u_int totalcount;
+};
+typedef struct readargs readargs;
+
+struct readokres {
+	fattr attributes;
+	struct {
+		u_int data_len;
+		char *data_val;
+	} data;
+};
+typedef struct readokres readokres;
+
+struct readres {
+	nfsstat status;
+	union {
+		readokres reply;
+	} readres_u;
+};
+typedef struct readres readres;
+
+struct writeargs {
+	nfs_fh file;
+	u_int beginoffset;
+	u_int offset;
+	u_int totalcount;
+	struct {
+		u_int data_len;
+		char *data_val;
+	} data;
+};
+typedef struct writeargs writeargs;
+
+struct createargs {
+	diropargs where;
+	sattr attributes;
+};
+typedef struct createargs createargs;
+
+struct renameargs {
+	diropargs from;
+	diropargs to;
+};
+typedef struct renameargs renameargs;
+
+struct linkargs {
+	nfs_fh from;
+	diropargs to;
+};
+typedef struct linkargs linkargs;
+
+struct symlinkargs {
+	diropargs from;
+	nfspath to;
+	sattr attributes;
+};
+typedef struct symlinkargs symlinkargs;
+
+struct nfscookie {
+	char data[NFS_COOKIESIZE];
+};
+typedef struct nfscookie nfscookie;
+
+struct readdirargs {
+	nfs_fh dir;
+	nfscookie cookie;
+	u_int count;
+};
+typedef struct readdirargs readdirargs;
+
+struct entry {
+	u_int fileid;
+	filename name;
+	nfscookie cookie;
+	struct entry *nextentry;
+};
+typedef struct entry entry;
+
+struct dirlist {
+	entry *entries;
+	bool_t eof;
+};
+typedef struct dirlist dirlist;
+
+struct readdirres {
+	nfsstat status;
+	union {
+		dirlist reply;
+	} readdirres_u;
+};
+typedef struct readdirres readdirres;
+
+struct statfsokres {
+	u_int tsize;
+	u_int bsize;
+	u_int blocks;
+	u_int bfree;
+	u_int bavail;
+};
+typedef struct statfsokres statfsokres;
+
+struct statfsres {
+	nfsstat status;
+	union {
+		statfsokres reply;
+	} statfsres_u;
+};
+typedef struct statfsres statfsres;
+
+#define NFS_PROGRAM 100003
+#define NFS_VERSION 2
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define NFSPROC_NULL 0
+extern  void * nfsproc_null_2(void *, CLIENT *);
+extern  void * nfsproc_null_2_svc(void *, struct svc_req *);
+#define NFSPROC_GETATTR 1
+extern  attrstat * nfsproc_getattr_2(nfs_fh *, CLIENT *);
+extern  attrstat * nfsproc_getattr_2_svc(nfs_fh *, struct svc_req *);
+#define NFSPROC_SETATTR 2
+extern  attrstat * nfsproc_setattr_2(sattrargs *, CLIENT *);
+extern  attrstat * nfsproc_setattr_2_svc(sattrargs *, struct svc_req *);
+#define NFSPROC_ROOT 3
+extern  void * nfsproc_root_2(void *, CLIENT *);
+extern  void * nfsproc_root_2_svc(void *, struct svc_req *);
+#define NFSPROC_LOOKUP 4
+extern  diropres * nfsproc_lookup_2(diropargs *, CLIENT *);
+extern  diropres * nfsproc_lookup_2_svc(diropargs *, struct svc_req *);
+#define NFSPROC_READLINK 5
+extern  readlinkres * nfsproc_readlink_2(nfs_fh *, CLIENT *);
+extern  readlinkres * nfsproc_readlink_2_svc(nfs_fh *, struct svc_req *);
+#define NFSPROC_READ 6
+extern  readres * nfsproc_read_2(readargs *, CLIENT *);
+extern  readres * nfsproc_read_2_svc(readargs *, struct svc_req *);
+#define NFSPROC_WRITECACHE 7
+extern  void * nfsproc_writecache_2(void *, CLIENT *);
+extern  void * nfsproc_writecache_2_svc(void *, struct svc_req *);
+#define NFSPROC_WRITE 8
+extern  attrstat * nfsproc_write_2(writeargs *, CLIENT *);
+extern  attrstat * nfsproc_write_2_svc(writeargs *, struct svc_req *);
+#define NFSPROC_CREATE 9
+extern  diropres * nfsproc_create_2(createargs *, CLIENT *);
+extern  diropres * nfsproc_create_2_svc(createargs *, struct svc_req *);
+#define NFSPROC_REMOVE 10
+extern  nfsstat * nfsproc_remove_2(diropargs *, CLIENT *);
+extern  nfsstat * nfsproc_remove_2_svc(diropargs *, struct svc_req *);
+#define NFSPROC_RENAME 11
+extern  nfsstat * nfsproc_rename_2(renameargs *, CLIENT *);
+extern  nfsstat * nfsproc_rename_2_svc(renameargs *, struct svc_req *);
+#define NFSPROC_LINK 12
+extern  nfsstat * nfsproc_link_2(linkargs *, CLIENT *);
+extern  nfsstat * nfsproc_link_2_svc(linkargs *, struct svc_req *);
+#define NFSPROC_SYMLINK 13
+extern  nfsstat * nfsproc_symlink_2(symlinkargs *, CLIENT *);
+extern  nfsstat * nfsproc_symlink_2_svc(symlinkargs *, struct svc_req *);
+#define NFSPROC_MKDIR 14
+extern  diropres * nfsproc_mkdir_2(createargs *, CLIENT *);
+extern  diropres * nfsproc_mkdir_2_svc(createargs *, struct svc_req *);
+#define NFSPROC_RMDIR 15
+extern  nfsstat * nfsproc_rmdir_2(diropargs *, CLIENT *);
+extern  nfsstat * nfsproc_rmdir_2_svc(diropargs *, struct svc_req *);
+#define NFSPROC_READDIR 16
+extern  readdirres * nfsproc_readdir_2(readdirargs *, CLIENT *);
+extern  readdirres * nfsproc_readdir_2_svc(readdirargs *, struct svc_req *);
+#define NFSPROC_STATFS 17
+extern  statfsres * nfsproc_statfs_2(nfs_fh *, CLIENT *);
+extern  statfsres * nfsproc_statfs_2_svc(nfs_fh *, struct svc_req *);
+extern int nfs_program_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define NFSPROC_NULL 0
+extern  void * nfsproc_null_2();
+extern  void * nfsproc_null_2_svc();
+#define NFSPROC_GETATTR 1
+extern  attrstat * nfsproc_getattr_2();
+extern  attrstat * nfsproc_getattr_2_svc();
+#define NFSPROC_SETATTR 2
+extern  attrstat * nfsproc_setattr_2();
+extern  attrstat * nfsproc_setattr_2_svc();
+#define NFSPROC_ROOT 3
+extern  void * nfsproc_root_2();
+extern  void * nfsproc_root_2_svc();
+#define NFSPROC_LOOKUP 4
+extern  diropres * nfsproc_lookup_2();
+extern  diropres * nfsproc_lookup_2_svc();
+#define NFSPROC_READLINK 5
+extern  readlinkres * nfsproc_readlink_2();
+extern  readlinkres * nfsproc_readlink_2_svc();
+#define NFSPROC_READ 6
+extern  readres * nfsproc_read_2();
+extern  readres * nfsproc_read_2_svc();
+#define NFSPROC_WRITECACHE 7
+extern  void * nfsproc_writecache_2();
+extern  void * nfsproc_writecache_2_svc();
+#define NFSPROC_WRITE 8
+extern  attrstat * nfsproc_write_2();
+extern  attrstat * nfsproc_write_2_svc();
+#define NFSPROC_CREATE 9
+extern  diropres * nfsproc_create_2();
+extern  diropres * nfsproc_create_2_svc();
+#define NFSPROC_REMOVE 10
+extern  nfsstat * nfsproc_remove_2();
+extern  nfsstat * nfsproc_remove_2_svc();
+#define NFSPROC_RENAME 11
+extern  nfsstat * nfsproc_rename_2();
+extern  nfsstat * nfsproc_rename_2_svc();
+#define NFSPROC_LINK 12
+extern  nfsstat * nfsproc_link_2();
+extern  nfsstat * nfsproc_link_2_svc();
+#define NFSPROC_SYMLINK 13
+extern  nfsstat * nfsproc_symlink_2();
+extern  nfsstat * nfsproc_symlink_2_svc();
+#define NFSPROC_MKDIR 14
+extern  diropres * nfsproc_mkdir_2();
+extern  diropres * nfsproc_mkdir_2_svc();
+#define NFSPROC_RMDIR 15
+extern  nfsstat * nfsproc_rmdir_2();
+extern  nfsstat * nfsproc_rmdir_2_svc();
+#define NFSPROC_READDIR 16
+extern  readdirres * nfsproc_readdir_2();
+extern  readdirres * nfsproc_readdir_2_svc();
+#define NFSPROC_STATFS 17
+extern  statfsres * nfsproc_statfs_2();
+extern  statfsres * nfsproc_statfs_2_svc();
+extern int nfs_program_2_freeresult ();
+#endif /* K&R C */
+
+/* the xdr functions */
+
+#if defined(__STDC__) || defined(__cplusplus)
+extern  bool_t xdr_nfsstat (XDR *, nfsstat*);
+extern  bool_t xdr_ftype (XDR *, ftype*);
+extern  bool_t xdr_nfs_fh (XDR *, nfs_fh*);
+extern  bool_t xdr_nfstime (XDR *, nfstime*);
+extern  bool_t xdr_fattr (XDR *, fattr*);
+extern  bool_t xdr_sattr (XDR *, sattr*);
+extern  bool_t xdr_filename (XDR *, filename*);
+extern  bool_t xdr_nfspath (XDR *, nfspath*);
+extern  bool_t xdr_attrstat (XDR *, attrstat*);
+extern  bool_t xdr_sattrargs (XDR *, sattrargs*);
+extern  bool_t xdr_diropargs (XDR *, diropargs*);
+extern  bool_t xdr_diropokres (XDR *, diropokres*);
+extern  bool_t xdr_diropres (XDR *, diropres*);
+extern  bool_t xdr_readlinkres (XDR *, readlinkres*);
+extern  bool_t xdr_readargs (XDR *, readargs*);
+extern  bool_t xdr_readokres (XDR *, readokres*);
+extern  bool_t xdr_readres (XDR *, readres*);
+extern  bool_t xdr_writeargs (XDR *, writeargs*);
+extern  bool_t xdr_createargs (XDR *, createargs*);
+extern  bool_t xdr_renameargs (XDR *, renameargs*);
+extern  bool_t xdr_linkargs (XDR *, linkargs*);
+extern  bool_t xdr_symlinkargs (XDR *, symlinkargs*);
+extern  bool_t xdr_nfscookie (XDR *, nfscookie*);
+extern  bool_t xdr_readdirargs (XDR *, readdirargs*);
+extern  bool_t xdr_entry (XDR *, entry*);
+extern  bool_t xdr_dirlist (XDR *, dirlist*);
+extern  bool_t xdr_readdirres (XDR *, readdirres*);
+extern  bool_t xdr_statfsokres (XDR *, statfsokres*);
+extern  bool_t xdr_statfsres (XDR *, statfsres*);
+
+#else /* K&R C */
+extern bool_t xdr_nfsstat ();
+extern bool_t xdr_ftype ();
+extern bool_t xdr_nfs_fh ();
+extern bool_t xdr_nfstime ();
+extern bool_t xdr_fattr ();
+extern bool_t xdr_sattr ();
+extern bool_t xdr_filename ();
+extern bool_t xdr_nfspath ();
+extern bool_t xdr_attrstat ();
+extern bool_t xdr_sattrargs ();
+extern bool_t xdr_diropargs ();
+extern bool_t xdr_diropokres ();
+extern bool_t xdr_diropres ();
+extern bool_t xdr_readlinkres ();
+extern bool_t xdr_readargs ();
+extern bool_t xdr_readokres ();
+extern bool_t xdr_readres ();
+extern bool_t xdr_writeargs ();
+extern bool_t xdr_createargs ();
+extern bool_t xdr_renameargs ();
+extern bool_t xdr_linkargs ();
+extern bool_t xdr_symlinkargs ();
+extern bool_t xdr_nfscookie ();
+extern bool_t xdr_readdirargs ();
+extern bool_t xdr_entry ();
+extern bool_t xdr_dirlist ();
+extern bool_t xdr_readdirres ();
+extern bool_t xdr_statfsokres ();
+extern bool_t xdr_statfsres ();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+/**@}*/
+#endif /* !_NFS_PROT_H_RPCGEN */
Index: rtemsbsd/nfsclient/nfs_prot.x
===================================================================
--- rtemsbsd/nfsclient/nfs_prot.x	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/nfs_prot.x	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,1268 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ * 
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ * 
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ * 
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ * 
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ * 
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef RPC_HDR
+%#ifndef lint
+%/*static char sccsid[] = "from: @(#)nfs_prot.x 1.2 87/10/12 Copyr 1987 Sun Micro";*/
+%/*static char sccsid[] = "from: @(#)nfs_prot.x	2.1 88/08/01 4.0 RPCSRC";*/
+%static char rcsid[] = "$Id$";
+%#endif /* not lint */
+#endif
+
+const NFS_PORT          = 2049;
+const NFS_MAXDATA       = 8192;
+const NFS_MAXPATHLEN    = 1024;
+const NFS_MAXNAMLEN	= 255;
+const NFS_FHSIZE	= 32;
+const NFS_COOKIESIZE	= 4;
+const NFS_FIFO_DEV	= -1;	/* size kludge for named pipes */
+
+/*
+ * File types
+ */
+const NFSMODE_FMT  = 0170000;	/* type of file */
+const NFSMODE_DIR  = 0040000;	/* directory */
+const NFSMODE_CHR  = 0020000;	/* character special */
+const NFSMODE_BLK  = 0060000;	/* block special */
+const NFSMODE_REG  = 0100000;	/* regular */
+const NFSMODE_LNK  = 0120000;	/* symbolic link */
+const NFSMODE_SOCK = 0140000;	/* socket */
+const NFSMODE_FIFO = 0010000;	/* fifo */
+
+/*
+ * Error status
+ */
+enum nfsstat {
+	NFS_OK= 0,		/* no error */
+	NFSERR_PERM=1,		/* Not owner */
+	NFSERR_NOENT=2,		/* No such file or directory */
+	NFSERR_IO=5,		/* I/O error */
+	NFSERR_NXIO=6,		/* No such device or address */
+	NFSERR_ACCES=13,	/* Permission denied */
+	NFSERR_EXIST=17,	/* File exists */
+	NFSERR_NODEV=19,	/* No such device */
+	NFSERR_NOTDIR=20,	/* Not a directory*/
+	NFSERR_ISDIR=21,	/* Is a directory */
+	NFSERR_FBIG=27,		/* File too large */
+	NFSERR_NOSPC=28,	/* No space left on device */
+	NFSERR_ROFS=30,		/* Read-only file system */
+	NFSERR_NAMETOOLONG=63,	/* File name too long */
+	NFSERR_NOTEMPTY=66,	/* Directory not empty */
+	NFSERR_DQUOT=69,	/* Disc quota exceeded */
+	NFSERR_STALE=70,	/* Stale NFS file handle */
+	NFSERR_WFLUSH=99	/* write cache flushed */
+};
+
+/*
+ * File types
+ */
+enum ftype {
+	NFNON = 0,	/* non-file */
+	NFREG = 1,	/* regular file */
+	NFDIR = 2,	/* directory */
+	NFBLK = 3,	/* block special */
+	NFCHR = 4,	/* character special */
+	NFLNK = 5,	/* symbolic link */
+	NFSOCK = 6,	/* unix domain sockets */
+	NFBAD = 7,	/* unused */
+	NFFIFO = 8 	/* named pipe */
+};
+
+/*
+ * File access handle
+ */
+struct nfs_fh {
+	opaque data[NFS_FHSIZE];
+};
+
+/* 
+ * Timeval
+ */
+struct nfstime {
+	unsigned seconds;
+	unsigned useconds;
+};
+
+
+/*
+ * File attributes
+ */
+struct fattr {
+	ftype type;		/* file type */
+	unsigned mode;		/* protection mode bits */
+	unsigned nlink;		/* # hard links */
+	unsigned uid;		/* owner user id */
+	unsigned gid;		/* owner group id */
+	unsigned size;		/* file size in bytes */
+	unsigned blocksize;	/* prefered block size */
+	unsigned rdev;		/* special device # */
+	unsigned blocks;	/* Kb of disk used by file */
+	unsigned fsid;		/* device # */
+	unsigned fileid;	/* inode # */
+	nfstime	atime;		/* time of last access */
+	nfstime	mtime;		/* time of last modification */
+	nfstime	ctime;		/* time of last change */
+};
+
+/*
+ * File attributes which can be set
+ */
+struct sattr {
+	unsigned mode;	/* protection mode bits */
+	unsigned uid;	/* owner user id */
+	unsigned gid;	/* owner group id */
+	unsigned size;	/* file size in bytes */
+	nfstime	atime;	/* time of last access */
+	nfstime	mtime;	/* time of last modification */
+};
+
+
+typedef string filename<NFS_MAXNAMLEN>; 
+typedef string nfspath<NFS_MAXPATHLEN>;
+
+/*
+ * Reply status with file attributes
+ */
+union attrstat switch (nfsstat status) {
+case NFS_OK:
+	fattr attributes;
+default:
+	void;
+};
+
+struct sattrargs {
+	nfs_fh file;
+	sattr attributes;
+};
+
+/*
+ * Arguments for directory operations
+ */
+struct diropargs {
+	nfs_fh	dir;	/* directory file handle */
+	filename name;		/* name (up to NFS_MAXNAMLEN bytes) */
+};
+
+struct diropokres {
+	nfs_fh file;
+	fattr attributes;
+};
+
+/*
+ * Results from directory operation
+ */
+union diropres switch (nfsstat status) {
+case NFS_OK:
+	diropokres diropres;
+default:
+	void;
+};
+
+union readlinkres switch (nfsstat status) {
+case NFS_OK:
+	nfspath data;
+default:
+	void;
+};
+
+/*
+ * Arguments to remote read
+ */
+struct readargs {
+	nfs_fh file;		/* handle for file */
+	unsigned offset;	/* byte offset in file */
+	unsigned count;		/* immediate read count */
+	unsigned totalcount;	/* total read count (from this offset)*/
+};
+
+/*
+ * Status OK portion of remote read reply
+ */
+struct readokres {
+	fattr	attributes;	/* attributes, need for pagin*/
+	opaque data<NFS_MAXDATA>;
+};
+
+union readres switch (nfsstat status) {
+case NFS_OK:
+	readokres reply;
+default:
+	void;
+};
+
+/*
+ * Arguments to remote write 
+ */
+struct writeargs {
+	nfs_fh	file;		/* handle for file */
+	unsigned beginoffset;	/* beginning byte offset in file */
+	unsigned offset;	/* current byte offset in file */
+	unsigned totalcount;	/* total write count (to this offset)*/
+	opaque data<NFS_MAXDATA>;
+};
+
+struct createargs {
+	diropargs where;
+	sattr attributes;
+};
+
+struct renameargs {
+	diropargs from;
+	diropargs to;
+};
+
+struct linkargs {
+	nfs_fh from;
+	diropargs to;
+};
+
+struct symlinkargs {
+	diropargs from;
+	nfspath to;
+	sattr attributes;
+};
+
+
+/* TS, 10/21/2002; converted cookie to struct */
+struct nfscookie {
+ 	opaque data[NFS_COOKIESIZE];
+};
+
+/*
+ * Arguments to readdir
+ */
+struct readdirargs {
+	nfs_fh dir;		/* directory handle */
+	nfscookie cookie;
+	unsigned count;		/* number of directory bytes to read */
+};
+
+struct entry {
+	unsigned fileid;
+	filename name;
+	nfscookie cookie;
+	entry *nextentry;
+};
+
+struct dirlist {
+	entry *entries;
+	bool eof;
+};
+
+union readdirres switch (nfsstat status) {
+case NFS_OK:
+	dirlist reply;
+default:
+	void;
+};
+
+struct statfsokres {
+	unsigned tsize;	/* preferred transfer size in bytes */
+	unsigned bsize;	/* fundamental file system block size */
+	unsigned blocks;	/* total blocks in file system */
+	unsigned bfree;	/* free blocks in fs */
+	unsigned bavail;	/* free blocks avail to non-superuser */
+};
+
+union statfsres switch (nfsstat status) {
+case NFS_OK:
+	statfsokres reply;
+default:
+	void;
+};
+
+#ifdef WANT_NFS3
+
+/*
+ * NFSv3 constants and types
+ */
+const NFS3_FHSIZE	= 64;	/* maximum size in bytes of a file handle */
+const NFS3_COOKIEVERFSIZE = 8;	/* size of a cookie verifier for READDIR */
+const NFS3_CREATEVERFSIZE = 8;	/* size of the verifier used for CREATE */
+const NFS3_WRITEVERFSIZE = 8;	/* size of the verifier used for WRITE */
+
+typedef unsigned hyper uint64;
+typedef hyper int64;
+typedef unsigned long uint32;
+typedef long int32;
+typedef string filename3<>;
+typedef string nfspath3<>;
+typedef uint64 fileid3;
+typedef uint64 cookie3;
+typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
+typedef opaque createverf3[NFS3_CREATEVERFSIZE];
+typedef opaque writeverf3[NFS3_WRITEVERFSIZE];
+typedef uint32 uid3;
+typedef uint32 gid3;
+typedef uint64 size3;
+typedef uint64 offset3;
+typedef uint32 mode3;
+typedef uint32 count3;
+
+/*
+ * Error status (v3)
+ */
+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
+};
+
+/*
+ * File types (v3)
+ */
+enum ftype3 {
+	NF3REG	= 1,		/* regular file */
+	NF3DIR	= 2,		/* directory */
+	NF3BLK	= 3,		/* block special */
+	NF3CHR	= 4,		/* character special */
+	NF3LNK	= 5,		/* symbolic link */
+	NF3SOCK	= 6,		/* unix domain sockets */
+	NF3FIFO	= 7		/* named pipe */
+};
+
+struct specdata3 {
+	uint32	specdata1;
+	uint32	specdata2;
+};
+
+/*
+ * File access handle (v3)
+ */
+struct nfs_fh3 {
+	opaque data<NFS3_FHSIZE>;
+};
+
+/* 
+ * Timeval (v3)
+ */
+struct nfstime3 {
+	uint32	seconds;
+	uint32	nseconds;
+};
+
+
+/*
+ * File attributes (v3)
+ */
+struct fattr3 {
+	ftype3	type;		/* file type */
+	mode3	mode;		/* protection mode bits */
+	uint32	nlink;		/* # hard links */
+	uid3	uid;		/* owner user id */
+	gid3	gid;		/* owner group id */
+	size3	size;		/* file size in bytes */
+	size3	used;		/* prefered block size */
+	specdata3 rdev;		/* special device # */
+	uint64 fsid;		/* device # */
+	fileid3	fileid;		/* inode # */
+	nfstime3 atime;		/* time of last access */
+	nfstime3 mtime;		/* time of last modification */
+	nfstime3 ctime;		/* time of last change */
+};
+
+union post_op_attr switch (bool attributes_follow) {
+case TRUE:
+	fattr3	attributes;
+case FALSE:
+	void;
+};
+
+struct wcc_attr {
+	size3	size;
+	nfstime3 mtime;
+	nfstime3 ctime;
+};
+
+union pre_op_attr switch (bool attributes_follow) {
+case TRUE:
+	wcc_attr attributes;
+case FALSE:
+	void;
+};
+
+struct wcc_data {
+	pre_op_attr before;
+	post_op_attr after;
+};
+
+union post_op_fh3 switch (bool handle_follows) {
+case TRUE:
+	nfs_fh3	handle;
+case FALSE:
+	void;
+};
+
+/*
+ * File attributes which can be set (v3)
+ */
+enum time_how {
+	DONT_CHANGE		= 0,
+	SET_TO_SERVER_TIME	= 1,
+	SET_TO_CLIENT_TIME	= 2
+};
+
+union set_mode3 switch (bool set_it) {
+case TRUE:
+	mode3	mode;
+default:
+	void;
+};
+
+union set_uid3 switch (bool set_it) {
+case TRUE:
+	uid3	uid;
+default:
+	void;
+};
+
+union set_gid3 switch (bool set_it) {
+case TRUE:
+	gid3	gid;
+default:
+	void;
+};
+
+union set_size3 switch (bool set_it) {
+case TRUE:
+	size3	size;
+default:
+	void;
+};
+
+union set_atime switch (time_how set_it) {
+case SET_TO_CLIENT_TIME:
+	nfstime3	atime;
+default:
+	void;
+};
+
+union set_mtime switch (time_how set_it) {
+case SET_TO_CLIENT_TIME:
+	nfstime3	mtime;
+default:
+	void;
+};
+
+struct sattr3 {
+	set_mode3	mode;
+	set_uid3	uid;
+	set_gid3	gid;
+	set_size3	size;
+	set_atime	atime;
+	set_mtime	mtime;
+};
+
+/*
+ * Arguments for directory operations (v3)
+ */
+struct diropargs3 {
+	nfs_fh3	dir;		/* directory file handle */
+	filename3 name;		/* name (up to NFS_MAXNAMLEN bytes) */
+};
+
+/*
+ * Arguments to getattr (v3).
+ */
+struct GETATTR3args {
+	nfs_fh3		object;
+};
+
+struct GETATTR3resok {
+	fattr3		obj_attributes;
+};
+
+union GETATTR3res switch (nfsstat3 status) {
+case NFS3_OK:
+	GETATTR3resok	resok;
+default:
+	void;
+};
+
+/*
+ * Arguments to setattr (v3).
+ */
+union sattrguard3 switch (bool check) {
+case TRUE:
+	nfstime3	obj_ctime;
+case FALSE:
+	void;
+};
+
+struct SETATTR3args {
+	nfs_fh3		object;
+	sattr3		new_attributes;
+	sattrguard3	guard;
+};
+
+struct SETATTR3resok {
+	wcc_data	obj_wcc;
+};
+
+struct SETATTR3resfail {
+	wcc_data	obj_wcc;
+};
+
+union SETATTR3res switch (nfsstat3 status) {
+case NFS3_OK:
+	SETATTR3resok	resok;
+default:
+	SETATTR3resfail	resfail;
+};
+
+/*
+ * Arguments to lookup (v3).
+ */
+struct LOOKUP3args {
+	diropargs3	what;
+};
+
+struct LOOKUP3resok {
+	nfs_fh3		object;
+	post_op_attr	obj_attributes;
+	post_op_attr	dir_attributes;
+};
+
+struct LOOKUP3resfail {
+	post_op_attr	dir_attributes;
+};
+
+union LOOKUP3res switch (nfsstat3 status) {
+case NFS3_OK:
+	LOOKUP3resok	resok;
+default:
+	LOOKUP3resfail	resfail;
+};
+
+/*
+ * Arguments to access (v3).
+ */
+const ACCESS3_READ	= 0x0001;
+const ACCESS3_LOOKUP	= 0x0002;
+const ACCESS3_MODIFY	= 0x0004;
+const ACCESS3_EXTEND	= 0x0008;
+const ACCESS3_DELETE	= 0x0010;
+const ACCESS3_EXECUTE	= 0x0020;
+
+struct ACCESS3args {
+	nfs_fh3		object;
+	uint32		access;
+};
+
+struct ACCESS3resok {
+	post_op_attr	obj_attributes;
+	uint32		access;
+};
+
+struct ACCESS3resfail {
+	post_op_attr	obj_attributes;
+};
+
+union ACCESS3res switch (nfsstat3 status) {
+case NFS3_OK:
+	ACCESS3resok	resok;
+default:
+	ACCESS3resfail	resfail;
+};
+
+/*
+ * Arguments to readlink (v3).
+ */
+struct READLINK3args {
+	nfs_fh3		symlink;
+};
+
+struct READLINK3resok {
+	post_op_attr	symlink_attributes;
+	nfspath3	data;
+};
+
+struct READLINK3resfail {
+	post_op_attr	symlink_attributes;
+};
+
+union READLINK3res switch (nfsstat3 status) {
+case NFS3_OK:
+	READLINK3resok	resok;
+default:
+	READLINK3resfail resfail;
+};
+
+/*
+ * Arguments to read (v3).
+ */
+struct READ3args {
+	nfs_fh3		file;
+	offset3		offset;
+	count3		count;
+};
+
+struct READ3resok {
+	post_op_attr	file_attributes;
+	count3		count;
+	bool		eof;
+	opaque		data<>;
+};
+
+struct READ3resfail {
+	post_op_attr	file_attributes;
+};
+
+/* XXX: solaris 2.6 uses ``nfsstat'' here */
+union READ3res switch (nfsstat3 status) {
+case NFS3_OK:
+	READ3resok	resok;
+default:
+	READ3resfail	resfail;
+};
+
+/*
+ * Arguments to write (v3).
+ */
+enum stable_how {
+	UNSTABLE	= 0,
+	DATA_SYNC	= 1,
+	FILE_SYNC	= 2
+};
+
+struct WRITE3args {
+	nfs_fh3		file;
+	offset3		offset;
+	count3		count;
+	stable_how	stable;
+	opaque		data<>;
+};
+
+struct WRITE3resok {
+	wcc_data	file_wcc;
+	count3		count;
+	stable_how	committed;
+	writeverf3	verf;
+};
+
+struct WRITE3resfail {
+	wcc_data	file_wcc;
+};
+
+union WRITE3res switch (nfsstat3 status) {
+case NFS3_OK:
+	WRITE3resok	resok;
+default:
+	WRITE3resfail	resfail;
+};
+
+/*
+ * Arguments to create (v3).
+ */
+enum createmode3 {
+	UNCHECKED	= 0,
+	GUARDED		= 1,
+	EXCLUSIVE	= 2
+};
+
+union createhow3 switch (createmode3 mode) {
+case UNCHECKED:
+case GUARDED:
+	sattr3		obj_attributes;
+case EXCLUSIVE:
+	createverf3	verf;
+};
+
+struct CREATE3args {
+	diropargs3	where;
+	createhow3	how;
+};
+
+struct CREATE3resok {
+	post_op_fh3	obj;
+	post_op_attr	obj_attributes;
+	wcc_data	dir_wcc;
+};
+
+struct CREATE3resfail {
+	wcc_data	dir_wcc;
+};
+
+union CREATE3res switch (nfsstat3 status) {
+case NFS3_OK:
+	CREATE3resok	resok;
+default:
+	CREATE3resfail	resfail;
+};
+
+/*
+ * Arguments to mkdir (v3).
+ */
+struct MKDIR3args {
+	diropargs3	where;
+	sattr3		attributes;
+};
+
+struct MKDIR3resok {
+	post_op_fh3	obj;
+	post_op_attr	obj_attributes;
+	wcc_data	dir_wcc;
+};
+
+struct MKDIR3resfail {
+	wcc_data	dir_wcc;
+};
+
+union MKDIR3res switch (nfsstat3 status) {
+case NFS3_OK:
+	MKDIR3resok	resok;
+default:
+	MKDIR3resfail	resfail;
+};
+
+/*
+ * Arguments to symlink (v3).
+ */
+struct symlinkdata3 {
+	sattr3		symlink_attributes;
+	nfspath3	symlink_data;
+};
+
+struct SYMLINK3args {
+	diropargs3	where;
+	symlinkdata3	symlink;
+};
+
+struct SYMLINK3resok {
+	post_op_fh3	obj;
+	post_op_attr	obj_attributes;
+	wcc_data	dir_wcc;
+};
+
+struct SYMLINK3resfail {
+	wcc_data	dir_wcc;
+};
+
+union SYMLINK3res switch (nfsstat3 status) {
+case NFS3_OK:
+	SYMLINK3resok	resok;
+default:
+	SYMLINK3resfail	resfail;
+};
+
+/*
+ * Arguments to mknod (v3).
+ */
+struct devicedata3 {
+	sattr3		dev_attributes;
+	specdata3	spec;
+};
+
+union mknoddata3 switch (ftype3 type) {
+case NF3CHR:
+case NF3BLK:
+	devicedata3	device;
+case NF3SOCK:
+case NF3FIFO:
+	sattr3		pipe_attributes;
+default:
+	void;
+};
+
+struct MKNOD3args {
+	diropargs3	where;
+	mknoddata3	what;
+};
+
+struct MKNOD3resok {
+	post_op_fh3	obj;
+	post_op_attr	obj_attributes;
+	wcc_data	dir_wcc;
+};
+
+struct MKNOD3resfail {
+	wcc_data	dir_wcc;
+};
+
+union MKNOD3res switch (nfsstat3 status) {
+case NFS3_OK:
+	MKNOD3resok	resok;
+default:
+	MKNOD3resfail	resfail;
+};
+
+/*
+ * Arguments to remove (v3).
+ */
+struct REMOVE3args {
+	diropargs3	object;
+};
+
+struct REMOVE3resok {
+	wcc_data	dir_wcc;
+};
+
+struct REMOVE3resfail {
+	wcc_data	dir_wcc;
+};
+
+union REMOVE3res switch (nfsstat3 status) {
+case NFS3_OK:
+	REMOVE3resok	resok;
+default:
+	REMOVE3resfail	resfail;
+};
+
+/*
+ * Arguments to rmdir (v3).
+ */
+struct RMDIR3args {
+	diropargs3	object;
+};
+
+struct RMDIR3resok {
+	wcc_data	dir_wcc;
+};
+
+struct RMDIR3resfail {
+	wcc_data	dir_wcc;
+};
+
+union RMDIR3res switch (nfsstat3 status) {
+case NFS3_OK:
+	RMDIR3resok	resok;
+default:
+	RMDIR3resfail	resfail;
+};
+
+/*
+ * Arguments to rename (v3).
+ */
+struct RENAME3args {
+	diropargs3	from;
+	diropargs3	to;
+};
+
+struct RENAME3resok {
+	wcc_data	fromdir_wcc;
+	wcc_data	todir_wcc;
+};
+
+struct RENAME3resfail {
+	wcc_data	fromdir_wcc;
+	wcc_data	todir_wcc;
+};
+
+union RENAME3res switch (nfsstat3 status) {
+case NFS3_OK:
+	RENAME3resok	resok;
+default:
+	RENAME3resfail	resfail;
+};
+
+/*
+ * Arguments to link (v3).
+ */
+struct LINK3args {
+	nfs_fh3		file;
+	diropargs3	link;
+};
+
+struct LINK3resok {
+	post_op_attr	file_attributes;
+	wcc_data	linkdir_wcc;
+};
+
+struct LINK3resfail {
+	post_op_attr	file_attributes;
+	wcc_data	linkdir_wcc;
+};
+
+union LINK3res switch (nfsstat3 status) {
+case NFS3_OK:
+	LINK3resok	resok;
+default:
+	LINK3resfail	resfail;
+};
+
+/*
+ * Arguments to readdir (v3).
+ */
+struct READDIR3args {
+	nfs_fh3		dir;
+	cookie3		cookie;
+	cookieverf3	cookieverf;
+	count3		count;
+};
+
+struct entry3 {
+	fileid3		fileid;
+	filename3	name;
+	cookie3		cookie;
+	entry3		*nextentry;
+};
+
+struct dirlist3 {
+	entry3		*entries;
+	bool		eof;
+};
+
+struct READDIR3resok {
+	post_op_attr	dir_attributes;
+	cookieverf3	cookieverf;
+	dirlist3	reply;
+};
+
+struct READDIR3resfail {
+	post_op_attr	dir_attributes;
+};
+
+union READDIR3res switch (nfsstat3 status) {
+case NFS3_OK:
+	READDIR3resok	resok;
+default:
+	READDIR3resfail	resfail;
+};
+
+/*
+ * Arguments to readdirplus (v3).
+ */
+struct READDIRPLUS3args {
+	nfs_fh3		dir;
+	cookie3		cookie;
+	cookieverf3	cookieverf;
+	count3		dircount;
+	count3		maxcount;
+};
+
+struct entryplus3 {
+	fileid3		fileid;
+	filename3	name;
+	cookie3		cookie;
+	post_op_attr	name_attributes;
+	post_op_fh3	name_handle;
+	entryplus3	*nextentry;
+};
+
+struct dirlistplus3 {
+	entryplus3	*entries;
+	bool		eof;
+};
+
+struct READDIRPLUS3resok {
+	post_op_attr	dir_attributes;
+	cookieverf3	cookieverf;
+	dirlistplus3	reply;
+};
+
+struct READDIRPLUS3resfail {
+	post_op_attr	dir_attributes;
+};
+
+union READDIRPLUS3res switch (nfsstat3 status) {
+case NFS3_OK:
+	READDIRPLUS3resok	resok;
+default:
+	READDIRPLUS3resfail	resfail;
+};
+
+/*
+ * Arguments to fsstat (v3).
+ */
+struct FSSTAT3args {
+	nfs_fh3		fsroot;
+};
+
+struct FSSTAT3resok {
+	post_op_attr	obj_attributes;
+	size3		tbytes;
+	size3		fbytes;
+	size3		abytes;
+	size3		tfiles;
+	size3		ffiles;
+	size3		afiles;
+	uint32		invarsec;
+};
+
+struct FSSTAT3resfail {
+	post_op_attr	obj_attributes;
+};
+
+union FSSTAT3res switch (nfsstat3 status) {
+case NFS3_OK:
+	FSSTAT3resok	resok;
+default:
+	FSSTAT3resfail	resfail;
+};
+
+/*
+ * Arguments to fsinfo (v3).
+ */
+const FSF3_LINK		= 0x0001;
+const FSF3_SYMLINK	= 0x0002;
+const FSF3_HOMOGENEOUS	= 0x0008;
+const FSF3_CANSETTIME	= 0x0010;
+
+struct FSINFO3args {
+	nfs_fh3		fsroot;
+};
+
+struct FSINFO3resok {
+	post_op_attr	obj_attributes;
+	uint32		rtmax;
+	uint32		rtpref;
+	uint32		rtmult;
+	uint32		wtmax;
+	uint32		wtpref;
+	uint32		wtmult;
+	uint32		dtpref;
+	size3		maxfilesize;
+	nfstime3	time_delta;
+	uint32		properties;
+};
+
+struct FSINFO3resfail {
+	post_op_attr	obj_attributes;
+};
+
+union FSINFO3res switch (nfsstat3 status) {
+case NFS3_OK:
+	FSINFO3resok	resok;
+default:
+	FSINFO3resfail	resfail;
+};
+
+/*
+ * Arguments to pathconf (v3).
+ */
+struct PATHCONF3args {
+	nfs_fh3		object;
+};
+
+struct PATHCONF3resok {
+	post_op_attr	obj_attributes;
+	uint32		linkmax;
+	uint32		name_max;
+	bool		no_trunc;
+	bool		chown_restricted;
+	bool		case_insensitive;
+	bool		case_preserving;
+};
+
+struct PATHCONF3resfail {
+	post_op_attr	obj_attributes;
+};
+
+union PATHCONF3res switch (nfsstat3 status) {
+case NFS3_OK:
+	PATHCONF3resok	resok;
+default:
+	PATHCONF3resfail	resfail;
+};
+
+/*
+ * Arguments to commit (v3).
+ */
+struct COMMIT3args {
+	nfs_fh3		file;
+	offset3		offset;
+	count3		count;
+};
+
+struct COMMIT3resok {
+	wcc_data	file_wcc;
+	writeverf3	verf;
+};
+
+struct COMMIT3resfail {
+	wcc_data	file_wcc;
+};
+
+union COMMIT3res switch (nfsstat3 status) {
+case NFS3_OK:
+	COMMIT3resok	resok;
+default:
+	COMMIT3resfail	resfail;
+};
+
+#endif /* WANT_NFS3 */
+
+/*
+ * Remote file service routines
+ */
+program NFS_PROGRAM {
+	version NFS_VERSION {
+		void 
+		NFSPROC_NULL(void) = 0;
+
+		attrstat 
+		NFSPROC_GETATTR(nfs_fh) =	1;
+
+		attrstat 
+		NFSPROC_SETATTR(sattrargs) = 2;
+
+		void 
+		NFSPROC_ROOT(void) = 3;
+
+		diropres 
+		NFSPROC_LOOKUP(diropargs) = 4;
+
+		readlinkres 
+		NFSPROC_READLINK(nfs_fh) = 5;
+
+		readres 
+		NFSPROC_READ(readargs) = 6;
+
+		void 
+		NFSPROC_WRITECACHE(void) = 7;
+
+		attrstat
+		NFSPROC_WRITE(writeargs) = 8;
+
+		diropres
+		NFSPROC_CREATE(createargs) = 9;
+
+		nfsstat
+		NFSPROC_REMOVE(diropargs) = 10;
+
+		nfsstat
+		NFSPROC_RENAME(renameargs) = 11;
+
+		nfsstat
+		NFSPROC_LINK(linkargs) = 12;
+
+		nfsstat
+		NFSPROC_SYMLINK(symlinkargs) = 13;
+
+		diropres
+		NFSPROC_MKDIR(createargs) = 14;
+
+		nfsstat
+		NFSPROC_RMDIR(diropargs) = 15;
+
+		readdirres
+		NFSPROC_READDIR(readdirargs) = 16;
+
+		statfsres
+		NFSPROC_STATFS(nfs_fh) = 17;
+	} = 2;
+} = 100003;
+#ifdef WANT_NFS3
+program NFS3_PROGRAM {
+	version NFS_V3 {
+		void
+		NFSPROC3_NULL(void)			= 0;
+
+		GETATTR3res
+		NFSPROC3_GETATTR(GETATTR3args)		= 1;
+
+		SETATTR3res
+		NFSPROC3_SETATTR(SETATTR3args)		= 2;
+
+		LOOKUP3res
+		NFSPROC3_LOOKUP(LOOKUP3args)		= 3;
+
+		ACCESS3res
+		NFSPROC3_ACCESS(ACCESS3args)		= 4;
+
+		READLINK3res
+		NFSPROC3_READLINK(READLINK3args)	= 5;
+
+		READ3res
+		NFSPROC3_READ(READ3args)		= 6;
+
+		WRITE3res
+		NFSPROC3_WRITE(WRITE3args)		= 7;
+
+		CREATE3res
+		NFSPROC3_CREATE(CREATE3args)		= 8;
+
+		MKDIR3res
+		NFSPROC3_MKDIR(MKDIR3args)		= 9;
+
+		SYMLINK3res
+		NFSPROC3_SYMLINK(SYMLINK3args)		= 10;
+
+		MKNOD3res
+		NFSPROC3_MKNOD(MKNOD3args)		= 11;
+
+		REMOVE3res
+		NFSPROC3_REMOVE(REMOVE3args)		= 12;
+
+		RMDIR3res
+		NFSPROC3_RMDIR(RMDIR3args)		= 13;
+
+		RENAME3res
+		NFSPROC3_RENAME(RENAME3args)		= 14;
+
+		LINK3res
+		NFSPROC3_LINK(LINK3args)		= 15;
+
+		READDIR3res
+		NFSPROC3_READDIR(READDIR3args)		= 16;
+
+		READDIRPLUS3res
+		NFSPROC3_READDIRPLUS(READDIRPLUS3args)	= 17;
+
+		FSSTAT3res
+		NFSPROC3_FSSTAT(FSSTAT3args)		= 18;
+
+		FSINFO3res
+		NFSPROC3_FSINFO(FSINFO3args)		= 19;
+
+		PATHCONF3res
+		NFSPROC3_PATHCONF(PATHCONF3args)	= 20;
+
+		COMMIT3res
+		NFSPROC3_COMMIT(COMMIT3args)		= 21;
+	} = 3;
+} = 100003;
+#endif
+
Index: rtemsbsd/nfsclient/nfs_prot_xdr.c
===================================================================
--- rtemsbsd/nfsclient/nfs_prot_xdr.c	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/nfs_prot_xdr.c	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,628 @@
+/**
+ * @file
+ *
+ * @brief NFS Prot XDR
+ * @ingroup libfs_nfsclient_nfs_prot NFS Prot
+ */
+
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "nfs_prot.h"
+#ifndef lint
+/*static char sccsid[] = "from: @(#)nfs_prot.x 1.2 87/10/12 Copyr 1987 Sun Micro";*/
+/*static char sccsid[] = "from: @(#)nfs_prot.x	2.1 88/08/01 4.0 RPCSRC";*/
+  #if !defined(__rtems__)
+    static char rcsid[] = "$Id$";
+  #endif
+#endif /* not lint */
+
+bool_t
+xdr_nfsstat (XDR *xdrs, nfsstat *objp)
+{
+	 if (!xdr_enum (xdrs, (enum_t *) objp))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_ftype (XDR *xdrs, ftype *objp)
+{
+	 if (!xdr_enum (xdrs, (enum_t *) objp))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_nfs_fh (XDR *xdrs, nfs_fh *objp)
+{
+	 if (!xdr_opaque (xdrs, objp->data, NFS_FHSIZE))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_nfstime (XDR *xdrs, nfstime *objp)
+{
+	 if (!xdr_u_int (xdrs, &objp->seconds))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->useconds))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_fattr (XDR *xdrs, fattr *objp)
+{
+	register int32_t *buf;
+
+
+	if (xdrs->x_op == XDR_ENCODE) {
+		 if (!xdr_ftype (xdrs, &objp->type))
+			 return FALSE;
+		buf = XDR_INLINE (xdrs, 10 * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			 if (!xdr_u_int (xdrs, &objp->mode))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->nlink))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->uid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->gid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->size))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->blocksize))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->rdev))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->blocks))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->fsid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->fileid))
+				 return FALSE;
+
+		} else {
+		IXDR_PUT_U_LONG(buf, objp->mode);
+		IXDR_PUT_U_LONG(buf, objp->nlink);
+		IXDR_PUT_U_LONG(buf, objp->uid);
+		IXDR_PUT_U_LONG(buf, objp->gid);
+		IXDR_PUT_U_LONG(buf, objp->size);
+		IXDR_PUT_U_LONG(buf, objp->blocksize);
+		IXDR_PUT_U_LONG(buf, objp->rdev);
+		IXDR_PUT_U_LONG(buf, objp->blocks);
+		IXDR_PUT_U_LONG(buf, objp->fsid);
+		IXDR_PUT_U_LONG(buf, objp->fileid);
+		}
+		 if (!xdr_nfstime (xdrs, &objp->atime))
+			 return FALSE;
+		 if (!xdr_nfstime (xdrs, &objp->mtime))
+			 return FALSE;
+		 if (!xdr_nfstime (xdrs, &objp->ctime))
+			 return FALSE;
+		return TRUE;
+	} else if (xdrs->x_op == XDR_DECODE) {
+		 if (!xdr_ftype (xdrs, &objp->type))
+			 return FALSE;
+		buf = XDR_INLINE (xdrs, 10 * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			 if (!xdr_u_int (xdrs, &objp->mode))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->nlink))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->uid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->gid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->size))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->blocksize))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->rdev))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->blocks))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->fsid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->fileid))
+				 return FALSE;
+
+		} else {
+		objp->mode = IXDR_GET_U_LONG(buf);
+		objp->nlink = IXDR_GET_U_LONG(buf);
+		objp->uid = IXDR_GET_U_LONG(buf);
+		objp->gid = IXDR_GET_U_LONG(buf);
+		objp->size = IXDR_GET_U_LONG(buf);
+		objp->blocksize = IXDR_GET_U_LONG(buf);
+		objp->rdev = IXDR_GET_U_LONG(buf);
+		objp->blocks = IXDR_GET_U_LONG(buf);
+		objp->fsid = IXDR_GET_U_LONG(buf);
+		objp->fileid = IXDR_GET_U_LONG(buf);
+		}
+		 if (!xdr_nfstime (xdrs, &objp->atime))
+			 return FALSE;
+		 if (!xdr_nfstime (xdrs, &objp->mtime))
+			 return FALSE;
+		 if (!xdr_nfstime (xdrs, &objp->ctime))
+			 return FALSE;
+	 return TRUE;
+	}
+
+	 if (!xdr_ftype (xdrs, &objp->type))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->mode))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->nlink))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->uid))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->gid))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->size))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->blocksize))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->rdev))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->blocks))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->fsid))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->fileid))
+		 return FALSE;
+	 if (!xdr_nfstime (xdrs, &objp->atime))
+		 return FALSE;
+	 if (!xdr_nfstime (xdrs, &objp->mtime))
+		 return FALSE;
+	 if (!xdr_nfstime (xdrs, &objp->ctime))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_sattr (XDR *xdrs, sattr *objp)
+{
+	register int32_t *buf;
+
+
+	if (xdrs->x_op == XDR_ENCODE) {
+		buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			 if (!xdr_u_int (xdrs, &objp->mode))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->uid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->gid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->size))
+				 return FALSE;
+
+		} else {
+		IXDR_PUT_U_LONG(buf, objp->mode);
+		IXDR_PUT_U_LONG(buf, objp->uid);
+		IXDR_PUT_U_LONG(buf, objp->gid);
+		IXDR_PUT_U_LONG(buf, objp->size);
+		}
+		 if (!xdr_nfstime (xdrs, &objp->atime))
+			 return FALSE;
+		 if (!xdr_nfstime (xdrs, &objp->mtime))
+			 return FALSE;
+		return TRUE;
+	} else if (xdrs->x_op == XDR_DECODE) {
+		buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			 if (!xdr_u_int (xdrs, &objp->mode))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->uid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->gid))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->size))
+				 return FALSE;
+
+		} else {
+		objp->mode = IXDR_GET_U_LONG(buf);
+		objp->uid = IXDR_GET_U_LONG(buf);
+		objp->gid = IXDR_GET_U_LONG(buf);
+		objp->size = IXDR_GET_U_LONG(buf);
+		}
+		 if (!xdr_nfstime (xdrs, &objp->atime))
+			 return FALSE;
+		 if (!xdr_nfstime (xdrs, &objp->mtime))
+			 return FALSE;
+	 return TRUE;
+	}
+
+	 if (!xdr_u_int (xdrs, &objp->mode))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->uid))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->gid))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->size))
+		 return FALSE;
+	 if (!xdr_nfstime (xdrs, &objp->atime))
+		 return FALSE;
+	 if (!xdr_nfstime (xdrs, &objp->mtime))
+		 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;
+}
+
+bool_t
+xdr_attrstat (XDR *xdrs, attrstat *objp)
+{
+	 if (!xdr_nfsstat (xdrs, &objp->status))
+		 return FALSE;
+	switch (objp->status) {
+	case NFS_OK:
+		 if (!xdr_fattr (xdrs, &objp->attrstat_u.attributes))
+			 return FALSE;
+		break;
+	default:
+		break;
+	}
+	return TRUE;
+}
+
+bool_t
+xdr_sattrargs (XDR *xdrs, sattrargs *objp)
+{
+	 if (!xdr_nfs_fh (xdrs, &objp->file))
+		 return FALSE;
+	 if (!xdr_sattr (xdrs, &objp->attributes))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_diropargs (XDR *xdrs, diropargs *objp)
+{
+	 if (!xdr_nfs_fh (xdrs, &objp->dir))
+		 return FALSE;
+	 if (!xdr_filename (xdrs, &objp->name))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_diropokres (XDR *xdrs, diropokres *objp)
+{
+	 if (!xdr_nfs_fh (xdrs, &objp->file))
+		 return FALSE;
+	 if (!xdr_fattr (xdrs, &objp->attributes))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_diropres (XDR *xdrs, diropres *objp)
+{
+	 if (!xdr_nfsstat (xdrs, &objp->status))
+		 return FALSE;
+	switch (objp->status) {
+	case NFS_OK:
+		 if (!xdr_diropokres (xdrs, &objp->diropres_u.diropres))
+			 return FALSE;
+		break;
+	default:
+		break;
+	}
+	return TRUE;
+}
+
+bool_t
+xdr_readlinkres (XDR *xdrs, readlinkres *objp)
+{
+	 if (!xdr_nfsstat (xdrs, &objp->status))
+		 return FALSE;
+	switch (objp->status) {
+	case NFS_OK:
+		 if (!xdr_nfspath (xdrs, &objp->readlinkres_u.data))
+			 return FALSE;
+		break;
+	default:
+		break;
+	}
+	return TRUE;
+}
+
+bool_t
+xdr_readargs (XDR *xdrs, readargs *objp)
+{
+	 if (!xdr_nfs_fh (xdrs, &objp->file))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->offset))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->count))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->totalcount))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_readokres (XDR *xdrs, readokres *objp)
+{
+	 if (!xdr_fattr (xdrs, &objp->attributes))
+		 return FALSE;
+	 if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, NFS_MAXDATA))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_readres (XDR *xdrs, readres *objp)
+{
+	 if (!xdr_nfsstat (xdrs, &objp->status))
+		 return FALSE;
+	switch (objp->status) {
+	case NFS_OK:
+		 if (!xdr_readokres (xdrs, &objp->readres_u.reply))
+			 return FALSE;
+		break;
+	default:
+		break;
+	}
+	return TRUE;
+}
+
+bool_t
+xdr_writeargs (XDR *xdrs, writeargs *objp)
+{
+	register int32_t *buf;
+
+
+	if (xdrs->x_op == XDR_ENCODE) {
+		 if (!xdr_nfs_fh (xdrs, &objp->file))
+			 return FALSE;
+		buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			 if (!xdr_u_int (xdrs, &objp->beginoffset))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->offset))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->totalcount))
+				 return FALSE;
+
+		} else {
+		IXDR_PUT_U_LONG(buf, objp->beginoffset);
+		IXDR_PUT_U_LONG(buf, objp->offset);
+		IXDR_PUT_U_LONG(buf, objp->totalcount);
+		}
+		 if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, NFS_MAXDATA))
+			 return FALSE;
+		return TRUE;
+	} else if (xdrs->x_op == XDR_DECODE) {
+		 if (!xdr_nfs_fh (xdrs, &objp->file))
+			 return FALSE;
+		buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			 if (!xdr_u_int (xdrs, &objp->beginoffset))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->offset))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->totalcount))
+				 return FALSE;
+
+		} else {
+		objp->beginoffset = IXDR_GET_U_LONG(buf);
+		objp->offset = IXDR_GET_U_LONG(buf);
+		objp->totalcount = IXDR_GET_U_LONG(buf);
+		}
+		 if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, NFS_MAXDATA))
+			 return FALSE;
+	 return TRUE;
+	}
+
+	 if (!xdr_nfs_fh (xdrs, &objp->file))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->beginoffset))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->offset))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->totalcount))
+		 return FALSE;
+	 if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, NFS_MAXDATA))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_createargs (XDR *xdrs, createargs *objp)
+{
+	 if (!xdr_diropargs (xdrs, &objp->where))
+		 return FALSE;
+	 if (!xdr_sattr (xdrs, &objp->attributes))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_renameargs (XDR *xdrs, renameargs *objp)
+{
+	 if (!xdr_diropargs (xdrs, &objp->from))
+		 return FALSE;
+	 if (!xdr_diropargs (xdrs, &objp->to))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_linkargs (XDR *xdrs, linkargs *objp)
+{
+	 if (!xdr_nfs_fh (xdrs, &objp->from))
+		 return FALSE;
+	 if (!xdr_diropargs (xdrs, &objp->to))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_symlinkargs (XDR *xdrs, symlinkargs *objp)
+{
+	 if (!xdr_diropargs (xdrs, &objp->from))
+		 return FALSE;
+	 if (!xdr_nfspath (xdrs, &objp->to))
+		 return FALSE;
+	 if (!xdr_sattr (xdrs, &objp->attributes))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_nfscookie (XDR *xdrs, nfscookie *objp)
+{
+	 if (!xdr_opaque (xdrs, objp->data, NFS_COOKIESIZE))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_readdirargs (XDR *xdrs, readdirargs *objp)
+{
+	 if (!xdr_nfs_fh (xdrs, &objp->dir))
+		 return FALSE;
+	 if (!xdr_nfscookie (xdrs, &objp->cookie))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->count))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_entry (XDR *xdrs, entry *objp)
+{
+	 if (!xdr_u_int (xdrs, &objp->fileid))
+		 return FALSE;
+	 if (!xdr_filename (xdrs, &objp->name))
+		 return FALSE;
+	 if (!xdr_nfscookie (xdrs, &objp->cookie))
+		 return FALSE;
+	 if (!xdr_pointer (xdrs, (char **)&objp->nextentry, sizeof (entry), (xdrproc_t) xdr_entry))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_dirlist (XDR *xdrs, dirlist *objp)
+{
+	 if (!xdr_pointer (xdrs, (char **)&objp->entries, sizeof (entry), (xdrproc_t) xdr_entry))
+		 return FALSE;
+	 if (!xdr_bool (xdrs, &objp->eof))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_readdirres (XDR *xdrs, readdirres *objp)
+{
+	 if (!xdr_nfsstat (xdrs, &objp->status))
+		 return FALSE;
+	switch (objp->status) {
+	case NFS_OK:
+		 if (!xdr_dirlist (xdrs, &objp->readdirres_u.reply))
+			 return FALSE;
+		break;
+	default:
+		break;
+	}
+	return TRUE;
+}
+
+bool_t
+xdr_statfsokres (XDR *xdrs, statfsokres *objp)
+{
+	register int32_t *buf;
+
+
+	if (xdrs->x_op == XDR_ENCODE) {
+		buf = XDR_INLINE (xdrs, 5 * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			 if (!xdr_u_int (xdrs, &objp->tsize))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->bsize))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->blocks))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->bfree))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->bavail))
+				 return FALSE;
+		} else {
+			IXDR_PUT_U_LONG(buf, objp->tsize);
+			IXDR_PUT_U_LONG(buf, objp->bsize);
+			IXDR_PUT_U_LONG(buf, objp->blocks);
+			IXDR_PUT_U_LONG(buf, objp->bfree);
+			IXDR_PUT_U_LONG(buf, objp->bavail);
+		}
+		return TRUE;
+	} else if (xdrs->x_op == XDR_DECODE) {
+		buf = XDR_INLINE (xdrs, 5 * BYTES_PER_XDR_UNIT);
+		if (buf == NULL) {
+			 if (!xdr_u_int (xdrs, &objp->tsize))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->bsize))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->blocks))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->bfree))
+				 return FALSE;
+			 if (!xdr_u_int (xdrs, &objp->bavail))
+				 return FALSE;
+		} else {
+			objp->tsize = IXDR_GET_U_LONG(buf);
+			objp->bsize = IXDR_GET_U_LONG(buf);
+			objp->blocks = IXDR_GET_U_LONG(buf);
+			objp->bfree = IXDR_GET_U_LONG(buf);
+			objp->bavail = IXDR_GET_U_LONG(buf);
+		}
+	 return TRUE;
+	}
+
+	 if (!xdr_u_int (xdrs, &objp->tsize))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->bsize))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->blocks))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->bfree))
+		 return FALSE;
+	 if (!xdr_u_int (xdrs, &objp->bavail))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_statfsres (XDR *xdrs, statfsres *objp)
+{
+	 if (!xdr_nfsstat (xdrs, &objp->status))
+		 return FALSE;
+	switch (objp->status) {
+	case NFS_OK:
+		 if (!xdr_statfsokres (xdrs, &objp->statfsres_u.reply))
+			 return FALSE;
+		break;
+	default:
+		break;
+	}
+	return TRUE;
+}
Index: rtemsbsd/nfsclient/nfsclient-private.h
===================================================================
--- rtemsbsd/nfsclient/nfsclient-private.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/nfsclient-private.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,14 @@
+struct mbuf;
+struct sockaddr;
+
+ssize_t sendto_nocpy(int s, const void *buf, size_t buflen, int flags,
+    const struct sockaddr *toaddr, int tolen, void *closure, void
+    (*freeproc)(caddr_t, u_int), void (*refproc)(caddr_t, u_int));
+
+ssize_t recv_mbuf_from(int s, struct mbuf **ppm, long len,
+    struct sockaddr *fromaddr, int *fromlen);
+
+struct __rpc_xdr;
+enum xdr_op;
+
+void xdrmbuf_create(struct __rpc_xdr *, struct mbuf *, enum xdr_op);
Index: rtemsbsd/nfsclient/rpcio.c
===================================================================
--- rtemsbsd/nfsclient/rpcio.c	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/rpcio.c	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,1814 @@
+/**
+ * @file
+ *
+ * @brief RPC Multiplexor for a Multitasking Environment
+ * @ingroup libfs
+ *
+ * This code funnels arbitrary task's UDP/RPC requests
+ * through one socket to arbitrary servers.
+ * The replies are gathered and dispatched to the
+ * requestors.
+ * One task handles all the sending and receiving
+ * work including retries.
+ * It is up to the requestor, however, to do
+ * the XDR encoding of the arguments / decoding
+ * of the results (except for the RPC header which
+ * is handled by the daemon).
+ */
+
+/*
+ * Author: Till Straumann <strauman@slac.stanford.edu>, 2002
+ *
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
+ * 	   Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The NFS-2 client implementation for RTEMS was produced by
+ *     the Stanford Linear Accelerator Center, Stanford University,
+ * 	   under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software.  Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+
+#include <rtems.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <stdlib.h>
+#include <time.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/cpuset.h>
+
+#include "rpcio.h"
+#include "nfsclient-private.h"
+
+/****************************************************************/
+/* CONFIGURABLE PARAMETERS                                      */
+/****************************************************************/
+
+#define MBUF_RX			/* If defined: use mbuf XDR stream for
+						 *  decoding directly out of mbufs
+						 *  Otherwise, the regular 'recvfrom()'
+						 *  interface will be used involving an
+						 *  extra buffer allocation + copy step.
+						 */
+
+#define MBUF_TX			/* If defined: avoid copying data when
+						 *  sending. Instead, use a wrapper to
+						 *  'sosend()' which will point an MBUF
+						 *  directly to our buffer space.
+						 *  Note that the BSD stack does not copy
+						 *  data when fragmenting packets - it
+						 *  merely uses an mbuf chain pointing
+						 *  into different areas of the data.
+						 *
+						 * If undefined, the regular 'sendto()'
+						 *  interface is used.
+						 */
+
+#undef REJECT_SERVERIP_MISMATCH
+						/* If defined, RPC replies must come from the server
+						 * that was queried. Eric Norum has reported problems
+						 * with clustered NFS servers. So we disable this
+						 * reducing paranoia...
+						 */
+
+/* daemon task parameters */
+#define RPCIOD_STACK		10000
+#define RPCIOD_PRIO			100	/* *fallback* priority */
+
+/* depth of the message queue for sending
+ * RPC requests to the daemon
+ */
+#define RPCIOD_QDEPTH		20
+
+/* Maximum retry limit for retransmission */
+#define RPCIOD_RETX_CAP_S	3 /* seconds */
+
+/* Default timeout for RPC calls */
+#define RPCIOD_DEFAULT_TIMEOUT	(&_rpc_default_timeout)
+static struct timeval _rpc_default_timeout = { 10 /* secs */, 0 /* usecs */ };
+
+/* how many times should we try to resend a failed
+ * transaction with refreshed AUTHs
+ */
+#define RPCIOD_REFRESH		2
+
+/* Events we are using; the RPC_EVENT
+ * MUST NOT be used by any application
+ * thread doing RPC IO (e.g. NFS)
+ */
+#define RTEMS_RPC_EVENT		RTEMS_EVENT_30	/* THE event used by RPCIO. Every task doing
+											 * RPC IO will receive this - hence it is
+											 * RESERVED
+											 */
+#define RPCIOD_RX_EVENT		RTEMS_EVENT_1	/* Events the RPCIOD is using/waiting for */
+#define RPCIOD_TX_EVENT		RTEMS_EVENT_2
+#define RPCIOD_KILL_EVENT	RTEMS_EVENT_3	/* send to the daemon to kill it          */
+
+#define LD_XACT_HASH		8				/* ld of the size of the transaction hash table  */
+
+
+/* Debugging Flags                                              */
+
+/* NOTE: defining DEBUG 0 leaves some 'assert()' paranoia checks
+ *       but produces no output
+ */
+
+#define	DEBUG_TRACE_XACT	(1<<0)
+#define DEBUG_EVENTS		(1<<1)
+#define DEBUG_MALLOC		(1<<2)
+#define DEBUG_TIMEOUT		(1<<3)
+#define DEBUG_PACKLOSS		(1<<4)	/* This introduces random, artificial packet losses to test retransmission */
+
+#define DEBUG_PACKLOSS_FRACT (0xffffffff/10)
+
+/* USE PARENTHESIS WHEN 'or'ing MULTIPLE FLAGS: (DEBUG_XX | DEBUG_YY) */
+#define DEBUG				(0)
+
+/****************************************************************/
+/* END OF CONFIGURABLE SECTION                                  */
+/****************************************************************/
+
+/* prevent rollover of our timers by readjusting the epoch on the fly */
+#if	(DEBUG) & DEBUG_TIMEOUT
+#define RPCIOD_EPOCH_SECS	10
+#else
+#define RPCIOD_EPOCH_SECS	10000
+#endif
+
+#ifdef	DEBUG
+#define ASSERT(arg)			assert(arg)
+#else
+#define ASSERT(arg)			if (arg)
+#endif
+
+/****************************************************************/
+/* MACROS                                                       */
+/****************************************************************/
+
+
+#define XACT_HASHS		(1<<(LD_XACT_HASH))	/* the hash table size derived from the ld       */
+#define XACT_HASH_MSK	((XACT_HASHS)-1)	/* mask to extract the hash index from a RPC-XID */
+
+
+#define MU_LOCK(mutex)		do { 							\
+							assert(							\
+								RTEMS_SUCCESSFUL ==			\
+								rtems_semaphore_obtain(		\
+										(mutex),			\
+										RTEMS_WAIT,			\
+										RTEMS_NO_TIMEOUT	\
+										) );				\
+							} while(0)
+
+#define MU_UNLOCK(mutex)	do {							\
+							assert(							\
+								RTEMS_SUCCESSFUL ==			\
+								rtems_semaphore_release(	\
+										(mutex)				\
+										) );				\
+							} while(0)
+
+#define MU_CREAT(pmutex)	do {							\
+							assert(							\
+								RTEMS_SUCCESSFUL ==			\
+								rtems_semaphore_create(		\
+										rtems_build_name(	\
+											'R','P','C','l'	\
+											),				\
+										1,					\
+										MUTEX_ATTRIBUTES,	\
+										0,					\
+										(pmutex)) );		\
+							} while (0)
+
+
+#define MU_DESTROY(mutex)	do {							\
+							assert(							\
+								RTEMS_SUCCESSFUL ==			\
+								rtems_semaphore_delete(		\
+										mutex				\
+										) );				\
+							} while (0)
+
+#define MUTEX_ATTRIBUTES	(RTEMS_LOCAL           | 		\
+			   				RTEMS_PRIORITY         | 		\
+			   				RTEMS_INHERIT_PRIORITY | 		\
+						   	RTEMS_BINARY_SEMAPHORE)
+
+#define FIRST_ATTEMPT		0x88888888 /* some time that is never reached */
+
+/****************************************************************/
+/* TYPE DEFINITIONS                                             */
+/****************************************************************/
+
+typedef	rtems_interval		TimeoutT;
+
+/* 100000th implementation of a doubly linked list;
+ * since only one thread is looking at these,
+ * we need no locking
+ */
+typedef struct ListNodeRec_ {
+	struct ListNodeRec_ *next, *prev;
+} ListNodeRec, *ListNode;
+
+
+/* Structure representing an RPC server */
+typedef struct RpcUdpServerRec_ {
+		RpcUdpServer		next;			/* linked list of all servers; protected by hlock */
+		union {
+		struct sockaddr_in	sin;
+		struct sockaddr     sa;
+		}					addr;
+		AUTH				*auth;
+		rtems_id			authlock;		/* must MUTEX the auth object - it's not clear
+											 *  what is better:
+											 *   1 having one (MUTEXed) auth per server
+											 *	   who is shared among all transactions
+											 *	   using that server
+											 *	 2 maintaining an AUTH per transaction
+											 *	   (there are then other options: manage
+											 *	   XACT pools on a per-server basis instead
+											 *	   of associating a server with a XACT when
+											 *   sending)
+											 * experience will show if the current (1)
+											 * approach has to be changed.
+											 */
+		TimeoutT			retry_period;	/* dynamically adjusted retry period
+											 * (based on packet roundtrip time)
+											 */
+		/* STATISTICS */
+		unsigned long		retrans;		/* how many retries were issued by this server         */
+		unsigned long		requests;		/* how many requests have been sent                    */
+		unsigned long       timeouts;		/* how many requests have timed out                    */
+		unsigned long       errors;         /* how many errors have occurred (other than timeouts) */
+		char				name[20];		/* server's address in IP 'dot' notation               */
+} RpcUdpServerRec;
+
+typedef union  RpcBufU_ {
+		uint32_t			xid;
+		char				buf[1];
+} RpcBufU, *RpcBuf;
+
+/* RX Buffer implementation; this is either
+ * an MBUF chain (MBUF_RX configuration)
+ * or a buffer allocated from the heap
+ * where recvfrom copies the (encoded) reply
+ * to. The XDR routines the copy/decode
+ * it into the user's data structures.
+ */
+#ifdef MBUF_RX
+typedef	struct mbuf *		RxBuf;	/* an MBUF chain */
+static  void   				bufFree(struct mbuf **m);
+#define XID(ibuf) 			(*(mtod((ibuf), u_long *)))
+#else
+typedef RpcBuf				RxBuf;
+#define	bufFree(b)			do { MY_FREE(*(b)); *(b)=0; } while(0)
+#define XID(ibuf) 			((ibuf)->xid)
+#endif
+
+/* A RPC 'transaction' consisting
+ * of server and requestor information,
+ * buffer space and an XDR object
+ * (for encoding arguments).
+ */
+typedef struct RpcUdpXactRec_ {
+		ListNodeRec			node;		/* so we can put XACTs on a list                */
+		RpcUdpServer		server;		/* server this XACT goes to                     */
+		long				lifetime;	/* during the lifetime, retry attempts are made */
+		long				tolive;		/* lifetime timer                               */
+		struct rpc_err		status;		/* RPC reply error status                       */
+		long				age;		/* age info; needed to manage retransmission    */
+		long				trip;		/* record round trip time in ticks              */
+		rtems_id			requestor;	/* the task waiting for this XACT to complete   */
+		RpcUdpXactPool		pool;		/* if this XACT belong to a pool, this is it    */
+		XDR					xdrs;		/* argument encoder stream                      */
+		int					xdrpos;     /* stream position after the (permanent) header */
+		xdrproc_t			xres;		/* reply decoder proc - TODO needn't be here    */
+		caddr_t				pres;		/* reply decoded obj  - TODO needn't be here    */
+#ifndef MBUF_RX
+		int					ibufsize;	/* size of the ibuf (bytes)                     */
+#endif
+#ifdef  MBUF_TX
+		int					refcnt;		/* mbuf external storage reference count        */
+#endif
+		int					obufsize;	/* size of the obuf (bytes)                     */
+		RxBuf				ibuf;		/* pointer to input buffer assigned by daemon   */
+		RpcBufU				obuf;       /* output buffer (encoded args) APPENDED HERE   */
+} RpcUdpXactRec;
+
+typedef struct RpcUdpXactPoolRec_ {
+	rtems_id	box;
+	int			prog;
+	int			version;
+	int			xactSize;
+} RpcUdpXactPoolRec;
+
+/* a global hash table where all 'living' transaction
+ * objects are registered.
+ * A number of bits in a transaction's XID maps 1:1 to
+ * an index in this table. Hence, the XACT matching
+ * an RPC/UDP reply packet can quickly be found
+ * The size of this table imposes a hard limit on the
+ * number of all created transactions in the system.
+ */
+static RpcUdpXact xactHashTbl[XACT_HASHS]={0};
+static u_long     xidUpper   [XACT_HASHS]={0};
+static unsigned   xidHashSeed            = 0 ;
+
+/* forward declarations */
+static RpcUdpXact
+sockRcv(void);
+
+static void
+rpcio_daemon(rtems_task_argument);
+
+#ifdef MBUF_TX
+ssize_t
+sendto_nocpy (
+		int s,
+		const void *buf, size_t buflen,
+		int flags,
+		const struct sockaddr *toaddr, int tolen,
+		void *closure,
+		void (*freeproc)(caddr_t, u_int),
+		void (*refproc)(caddr_t, u_int)
+);
+static void paranoia_free(caddr_t closure, u_int size);
+static void paranoia_ref (caddr_t closure, u_int size);
+#define SENDTO	sendto_nocpy
+#else
+#define SENDTO	sendto
+#endif
+
+static RpcUdpServer		rpcUdpServers = 0;	/* linked list of all servers; protected by llock */
+
+static int				ourSock = -1;		/* the socket we are using for communication */
+static rtems_id			rpciod  = 0;		/* task id of the RPC daemon                 */
+static rtems_id			msgQ    = 0;		/* message queue where the daemon picks up
+											 * requests
+											 */
+#ifndef NDEBUG
+static rtems_id			llock	= 0;		/* MUTEX protecting the server list */
+static rtems_id			hlock	= 0;		/* MUTEX protecting the hash table and the list of servers */
+#endif
+static rtems_id			fini	= 0;		/* a synchronization semaphore we use during
+											 * module cleanup / driver unloading
+											 */
+static rtems_interval	ticksPerSec;		/* cached system clock rate (WHO IS ASSUMED NOT
+											 * TO CHANGE)
+											 */
+
+rtems_task_priority		rpciodPriority = 0;
+#ifdef RTEMS_SMP
+const cpu_set_t			*rpciodCpuset = 0;
+size_t				rpciodCpusetSize = 0;
+#endif
+
+#if (DEBUG) & DEBUG_MALLOC
+/* malloc wrappers for debugging */
+static int nibufs = 0;
+
+static inline void *MY_MALLOC(int s)
+{
+	if (s) {
+		void *rval;
+		MU_LOCK(hlock);
+		assert(nibufs++ < 2000);
+		MU_UNLOCK(hlock);
+		assert((rval = malloc(s)) != 0);
+		return rval;
+	}
+	return 0;
+}
+
+static inline void *MY_CALLOC(int n, int s)
+{
+	if (s) {
+		void *rval;
+		MU_LOCK(hlock);
+		assert(nibufs++ < 2000);
+		MU_UNLOCK(hlock);
+		assert((rval = calloc(n,s)) != 0);
+		return rval;
+	}
+	return 0;
+}
+
+
+static inline void MY_FREE(void *p)
+{
+	if (p) {
+		MU_LOCK(hlock);
+		nibufs--;
+		MU_UNLOCK(hlock);
+		free(p);
+	}
+}
+#else
+#define MY_MALLOC	malloc
+#define MY_CALLOC	calloc
+#define MY_FREE		free
+#endif
+
+static inline bool_t
+locked_marshal(RpcUdpServer s, XDR *xdrs)
+{
+bool_t rval;
+	MU_LOCK(s->authlock);
+	rval = AUTH_MARSHALL(s->auth, xdrs);
+	MU_UNLOCK(s->authlock);
+	return rval;
+}
+
+/* Locked operations on a server's auth object */
+static inline bool_t
+locked_validate(RpcUdpServer s, struct opaque_auth *v)
+{
+bool_t rval;
+	MU_LOCK(s->authlock);
+	rval = AUTH_VALIDATE(s->auth, v);
+	MU_UNLOCK(s->authlock);
+	return rval;
+}
+
+static inline bool_t
+locked_refresh(RpcUdpServer s)
+{
+bool_t rval;
+	MU_LOCK(s->authlock);
+	rval = AUTH_REFRESH(s->auth);
+	MU_UNLOCK(s->authlock);
+	return rval;
+}
+
+/* Create a server object
+ *
+ */
+enum clnt_stat
+rpcUdpServerCreate(
+	struct sockaddr_in	*paddr,
+	rpcprog_t		prog,
+	rpcvers_t		vers,
+	u_long			uid,
+	u_long			gid,
+	RpcUdpServer		*psrv
+	)
+{
+RpcUdpServer	rval;
+u_short			port;
+char			hname[MAX_MACHINE_NAME + 1];
+int				theuid, thegid;
+int				thegids[NGRPS];
+gid_t			gids[NGROUPS];
+int				len,i;
+AUTH			*auth;
+enum clnt_stat	pmap_err;
+struct pmap		pmaparg;
+
+	if ( gethostname(hname, MAX_MACHINE_NAME) ) {
+		fprintf(stderr,
+				"RPCIO - error: I have no hostname ?? (%s)\n",
+				strerror(errno));
+		return RPC_UNKNOWNHOST;
+	}
+
+	if ( (len = getgroups(NGROUPS, gids) < 0 ) ) {
+		fprintf(stderr,
+				"RPCIO - error: I unable to get group ids (%s)\n",
+				strerror(errno));
+		return RPC_FAILED;
+	}
+
+	if ( len > NGRPS )
+		len = NGRPS;
+
+	for (i=0; i<len; i++)
+		thegids[i] = (int)gids[i];
+
+	theuid = (int) ((RPCIOD_DEFAULT_ID == uid) ? geteuid() : uid);
+	thegid = (int) ((RPCIOD_DEFAULT_ID == gid) ? getegid() : gid);
+
+	if ( !(auth = authunix_create(hname, theuid, thegid, len, thegids)) ) {
+		fprintf(stderr,
+				"RPCIO - error: unable to create RPC AUTH\n");
+		return RPC_FAILED;
+	}
+
+	/* if they specified no port try to ask the portmapper */
+	if (!paddr->sin_port) {
+
+		paddr->sin_port = htons(PMAPPORT);
+
+        pmaparg.pm_prog = prog;
+        pmaparg.pm_vers = vers;
+        pmaparg.pm_prot = IPPROTO_UDP;
+        pmaparg.pm_port = 0;  /* not needed or used */
+
+
+		/* dont use non-reentrant pmap_getport ! */
+
+		pmap_err = rpcUdpCallRp(
+						paddr,
+						PMAPPROG,
+						PMAPVERS,
+						PMAPPROC_GETPORT,
+						xdr_pmap,
+						&pmaparg,
+						xdr_u_short,
+						&port,
+						uid,
+						gid,
+						0);
+
+		if ( RPC_SUCCESS != pmap_err ) {
+			paddr->sin_port = 0;
+			return pmap_err;
+		}
+
+		paddr->sin_port = htons(port);
+	}
+
+	if (0==paddr->sin_port) {
+			return RPC_PROGNOTREGISTERED;
+	}
+
+	rval       			= (RpcUdpServer)MY_MALLOC(sizeof(*rval));
+	memset(rval, 0, sizeof(*rval));
+
+	if (!inet_ntop(AF_INET, &paddr->sin_addr, rval->name, sizeof(rval->name)))
+		sprintf(rval->name,"?.?.?.?");
+	rval->addr.sin		= *paddr;
+
+	/* start with a long retransmission interval - it
+	 * will be adapted dynamically
+	 */
+	rval->retry_period  = RPCIOD_RETX_CAP_S * ticksPerSec;
+
+	rval->auth 			= auth;
+
+	MU_CREAT( &rval->authlock );
+
+	/* link into list */
+	MU_LOCK( llock );
+	rval->next = rpcUdpServers;
+	rpcUdpServers = rval;
+	MU_UNLOCK( llock );
+
+	*psrv				= rval;
+	return RPC_SUCCESS;
+}
+
+void
+rpcUdpServerDestroy(RpcUdpServer s)
+{
+RpcUdpServer prev;
+	if (!s)
+		return;
+	/* we should probably verify (but how?) that nobody
+	 * (at least: no outstanding XACTs) is using this
+	 * server;
+	 */
+
+	/* remove from server list */
+	MU_LOCK(llock);
+	prev = rpcUdpServers;
+	if ( s == prev ) {
+		rpcUdpServers = s->next;
+	} else {
+		for ( ; prev ; prev = prev->next) {
+			if (prev->next == s) {
+				prev->next = s->next;
+				break;
+			}
+		}
+	}
+	MU_UNLOCK(llock);
+
+	/* MUST have found it */
+	assert(prev);
+
+	auth_destroy(s->auth);
+
+	MU_DESTROY(s->authlock);
+	MY_FREE(s);
+}
+
+int
+rpcUdpStats(FILE *f)
+{
+RpcUdpServer s;
+
+	if (!f) f = stdout;
+
+	fprintf(f,"RPCIOD statistics:\n");
+
+	MU_LOCK(llock);
+	for (s = rpcUdpServers; s; s=s->next) {
+		fprintf(f,"\nServer -- %s:\n", s->name);
+		fprintf(f,"  requests    sent: %10ld, retransmitted: %10ld\n",
+						s->requests, s->retrans);
+		fprintf(f,"         timed out: %10ld,   send errors: %10ld\n",
+						s->timeouts, s->errors);
+		fprintf(f,"  current retransmission interval: %dms\n",
+						(unsigned)(s->retry_period * 1000 / ticksPerSec) );
+	}
+	MU_UNLOCK(llock);
+
+	return 0;
+}
+
+RpcUdpXact
+rpcUdpXactCreate(
+	u_long	program,
+	u_long	version,
+	u_long	size
+	)
+{
+RpcUdpXact		rval=0;
+struct rpc_msg	header;
+register int	i,j;
+
+	if (!size)
+		size = UDPMSGSIZE;
+	/* word align */
+	size = (size + 3) & ~3;
+
+	rval = (RpcUdpXact)MY_CALLOC(1,sizeof(*rval) - sizeof(rval->obuf) + size);
+
+	if (rval) {
+
+		header.rm_xid             = 0;
+		header.rm_direction       = CALL;
+		header.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+		header.rm_call.cb_prog    = program;
+		header.rm_call.cb_vers    = version;
+		xdrmem_create(&(rval->xdrs), rval->obuf.buf, size, XDR_ENCODE);
+
+		if (!xdr_callhdr(&(rval->xdrs), &header)) {
+			MY_FREE(rval);
+			return 0;
+		}
+		/* pick a free table slot and initialize the XID */
+		MU_LOCK(hlock);
+		rval->obuf.xid = (xidHashSeed++ ^ ((uintptr_t)rval>>10)) & XACT_HASH_MSK;
+		i=j=(rval->obuf.xid & XACT_HASH_MSK);
+		if (msgQ) {
+			/* if there's no message queue, refuse to
+			 * give them transactions; we might be in the process to
+			 * go away...
+			 */
+			do {
+				i=(i+1) & XACT_HASH_MSK; /* cheap modulo */
+				if (!xactHashTbl[i]) {
+#if (DEBUG) & DEBUG_TRACE_XACT
+					fprintf(stderr,"RPCIO: entering index %i, val %x\n",i,rval);
+#endif
+					xactHashTbl[i]=rval;
+					j=-1;
+					break;
+				}
+			} while (i!=j);
+		}
+		MU_UNLOCK(hlock);
+		if (i==j) {
+			XDR_DESTROY(&rval->xdrs);
+			MY_FREE(rval);
+			return 0;
+		}
+		rval->obuf.xid  = xidUpper[i] | i;
+		rval->xdrpos    = XDR_GETPOS(&(rval->xdrs));
+		rval->obufsize  = size;
+	}
+	return rval;
+}
+
+void
+rpcUdpXactDestroy(RpcUdpXact xact)
+{
+int i = xact->obuf.xid & XACT_HASH_MSK;
+
+#if (DEBUG) & DEBUG_TRACE_XACT
+		fprintf(stderr,"RPCIO: removing index %i, val %x\n",i,xact);
+#endif
+
+		ASSERT( xactHashTbl[i]==xact );
+
+		MU_LOCK(hlock);
+		xactHashTbl[i]=0;
+		/* remember XID we used last time so we can avoid
+		 * reusing the same one (incremented by rpcUdpSend routine)
+		 */
+		xidUpper[i]   = xact->obuf.xid;
+		MU_UNLOCK(hlock);
+
+		bufFree(&xact->ibuf);
+
+		XDR_DESTROY(&xact->xdrs);
+		MY_FREE(xact);
+}
+
+
+
+/* Send a transaction, i.e. enqueue it to the
+ * RPC daemon who will actually send it.
+ */
+enum clnt_stat
+rpcUdpSend(
+	RpcUdpXact		xact,
+	RpcUdpServer	srvr,
+	struct timeval	*timeout,
+	u_long			proc,
+	xdrproc_t		xres, caddr_t pres,
+	xdrproc_t		xargs, caddr_t pargs,
+	...
+   )
+{
+register XDR	*xdrs;
+unsigned long	ms;
+va_list			ap;
+
+	va_start(ap,pargs);
+
+	if (!timeout)
+		timeout = RPCIOD_DEFAULT_TIMEOUT;
+
+	ms = 1000 * timeout->tv_sec + timeout->tv_usec/1000;
+
+	/* round lifetime to closest # of ticks */
+	xact->lifetime  = (ms * ticksPerSec + 500) / 1000;
+	if ( 0 == xact->lifetime )
+		xact->lifetime = 1;
+
+#if (DEBUG) & DEBUG_TIMEOUT
+	{
+	static int once=0;
+	if (!once++) {
+		fprintf(stderr,
+				"Initial lifetime: %i (ticks)\n",
+				xact->lifetime);
+	}
+	}
+#endif
+
+	xact->tolive    = xact->lifetime;
+
+	xact->xres      = xres;
+	xact->pres      = pres;
+	xact->server    = srvr;
+
+	xdrs            = &xact->xdrs;
+	xdrs->x_op      = XDR_ENCODE;
+	/* increment transaction ID */
+	xact->obuf.xid += XACT_HASHS;
+	XDR_SETPOS(xdrs, xact->xdrpos);
+	if ( !XDR_PUTLONG(xdrs,(long*)&proc) || !locked_marshal(srvr, xdrs) ||
+		 !xargs(xdrs, pargs) ) {
+		va_end(ap);
+		return(xact->status.re_status=RPC_CANTENCODEARGS);
+	}
+	while ((xargs=va_arg(ap,xdrproc_t))) {
+		if (!xargs(xdrs, va_arg(ap,caddr_t)))
+		va_end(ap);
+		return(xact->status.re_status=RPC_CANTENCODEARGS);
+	}
+
+	va_end(ap);
+
+	rtems_task_ident(RTEMS_SELF, RTEMS_WHO_AM_I, &xact->requestor);
+	if ( rtems_message_queue_send( msgQ, &xact, sizeof(xact)) ) {
+		return RPC_CANTSEND;
+	}
+	/* wakeup the rpciod */
+	ASSERT( RTEMS_SUCCESSFUL==rtems_event_send(rpciod, RPCIOD_TX_EVENT) );
+
+	return RPC_SUCCESS;
+}
+
+/* Block for the RPC reply to an outstanding
+ * transaction.
+ * The caller is woken by the RPC daemon either
+ * upon reception of the reply or on timeout.
+ */
+enum clnt_stat
+rpcUdpRcv(RpcUdpXact xact)
+{
+int					refresh;
+XDR			reply_xdrs;
+struct rpc_msg		reply_msg;
+rtems_status_code	status;
+rtems_event_set		gotEvents;
+
+	refresh = 0;
+
+	do {
+
+	/* block for the reply */
+	status = rtems_event_receive(
+		RTEMS_RPC_EVENT,
+		RTEMS_WAIT | RTEMS_EVENT_ANY,
+		RTEMS_NO_TIMEOUT,
+		&gotEvents);
+	ASSERT( status == RTEMS_SUCCESSFUL );
+
+	if (xact->status.re_status) {
+#ifdef MBUF_RX
+		/* add paranoia */
+		ASSERT( !xact->ibuf );
+#endif
+		return xact->status.re_status;
+	}
+
+#ifdef MBUF_RX
+	xdrmbuf_create(&reply_xdrs, xact->ibuf, XDR_DECODE);
+#else
+	xdrmem_create(&reply_xdrs, xact->ibuf->buf, xact->ibufsize, XDR_DECODE);
+#endif
+
+	reply_msg.acpted_rply.ar_verf          = _null_auth;
+	reply_msg.acpted_rply.ar_results.where = xact->pres;
+	reply_msg.acpted_rply.ar_results.proc  = xact->xres;
+
+	if (xdr_replymsg(&reply_xdrs, &reply_msg)) {
+		/* OK */
+		_seterr_reply(&reply_msg, &xact->status);
+		if (RPC_SUCCESS == xact->status.re_status) {
+			if ( !locked_validate(xact->server,
+								&reply_msg.acpted_rply.ar_verf) ) {
+				xact->status.re_status = RPC_AUTHERROR;
+				xact->status.re_why    = AUTH_INVALIDRESP;
+			}
+			if (reply_msg.acpted_rply.ar_verf.oa_base) {
+				reply_xdrs.x_op = XDR_FREE;
+				xdr_opaque_auth(&reply_xdrs, &reply_msg.acpted_rply.ar_verf);
+			}
+			refresh = 0;
+		} else {
+			/* should we try to refresh our credentials ? */
+			if ( !refresh ) {
+				/* had never tried before */
+				refresh = RPCIOD_REFRESH;
+			}
+		}
+	} else {
+		reply_xdrs.x_op        = XDR_FREE;
+		xdr_replymsg(&reply_xdrs, &reply_msg);
+		xact->status.re_status = RPC_CANTDECODERES;
+	}
+	XDR_DESTROY(&reply_xdrs);
+
+	bufFree(&xact->ibuf);
+
+#ifndef MBUF_RX
+	xact->ibufsize = 0;
+#endif
+
+	if (refresh && locked_refresh(xact->server)) {
+		rtems_task_ident(RTEMS_SELF, RTEMS_WHO_AM_I, &xact->requestor);
+		if ( rtems_message_queue_send(msgQ, &xact, sizeof(xact)) ) {
+			return RPC_CANTSEND;
+		}
+		/* wakeup the rpciod */
+		fprintf(stderr,"RPCIO INFO: refreshing my AUTH\n");
+		ASSERT( RTEMS_SUCCESSFUL==rtems_event_send(rpciod, RPCIOD_TX_EVENT) );
+	}
+
+	} while ( 0 &&  refresh-- > 0 );
+
+	return xact->status.re_status;
+}
+
+
+/* On RTEMS, I'm told to avoid select(); this seems to
+ * be more efficient
+ */
+static void
+rxWakeupCB(struct socket *sock, void *arg)
+{
+  rtems_id *rpciod = (rtems_id*) arg;
+  rtems_event_send(*rpciod, RPCIOD_RX_EVENT);
+}
+
+void
+rpcSetXIDs(uint32_t xid)
+{
+	uint32_t i;
+
+	xid &= ~XACT_HASH_MSK;
+
+	for (i = 0; i < XACT_HASHS; ++i) {
+		xidUpper[i] = xid | i;
+	}
+}
+
+int
+rpcUdpInit(void)
+{
+int			s;
+rtems_status_code	status;
+int			noblock = 1;
+struct sockwakeup	wkup;
+
+	if (ourSock < 0) {
+    fprintf(stderr,"RTEMS-RPCIOD $Release$, " \
+            "Till Straumann, Stanford/SLAC/SSRL 2002, " \
+            "See LICENSE file for licensing info.\n");
+
+		ourSock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+		if (ourSock>=0) {
+			bindresvport(ourSock,(struct sockaddr_in*)0);
+			s = ioctl(ourSock, FIONBIO, (char*)&noblock);
+			assert( s == 0 );
+			/* assume nobody tampers with the clock !! */
+			ticksPerSec = rtems_clock_get_ticks_per_second();
+			MU_CREAT( &hlock );
+			MU_CREAT( &llock );
+
+			if ( !rpciodPriority ) {
+				/* use configured networking priority */
+				if ( ! (rpciodPriority = rtems_bsdnet_config.network_task_priority) )
+					rpciodPriority = RPCIOD_PRIO;	/* fallback value */
+			}
+
+			status = rtems_task_create(
+											rtems_build_name('R','P','C','d'),
+											rpciodPriority,
+											RPCIOD_STACK,
+											RTEMS_DEFAULT_MODES,
+											/* fprintf saves/restores FP registers on PPC :-( */
+											RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT,
+											&rpciod);
+			assert( status == RTEMS_SUCCESSFUL );
+
+#ifdef RTEMS_SMP
+			if ( rpciodCpuset == 0 ) {
+				rpciodCpuset = rtems_bsdnet_config.network_task_cpuset;
+				rpciodCpusetSize = rtems_bsdnet_config.network_task_cpuset_size;
+			}
+			if ( rpciodCpuset != 0 )
+				rtems_task_set_affinity( rpciod, rpciodCpusetSize, rpciodCpuset );
+#endif
+
+			wkup.sw_pfn = rxWakeupCB;
+			wkup.sw_arg = &rpciod;
+			assert( 0==setsockopt(ourSock, SOL_SOCKET, SO_RCVWAKEUP, &wkup, sizeof(wkup)) );
+			status = rtems_message_queue_create(
+											rtems_build_name('R','P','C','q'),
+											RPCIOD_QDEPTH,
+											sizeof(RpcUdpXact),
+											RTEMS_DEFAULT_ATTRIBUTES,
+											&msgQ);
+			assert( status == RTEMS_SUCCESSFUL );
+			status = rtems_task_start( rpciod, rpcio_daemon, 0 );
+			assert( status == RTEMS_SUCCESSFUL );
+
+		} else {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int
+rpcUdpCleanup(void)
+{
+	rtems_semaphore_create(
+			rtems_build_name('R','P','C','f'),
+			0,
+			RTEMS_DEFAULT_ATTRIBUTES,
+			0,
+			&fini);
+	rtems_event_send(rpciod, RPCIOD_KILL_EVENT);
+	/* synchronize with daemon */
+	rtems_semaphore_obtain(fini, RTEMS_WAIT, 5*ticksPerSec);
+	/* if the message queue is still there, something went wrong */
+	if (!msgQ) {
+		rtems_task_delete(rpciod);
+	}
+	rtems_semaphore_delete(fini);
+	return (msgQ !=0);
+}
+
+/* Another API - simpler but less efficient.
+ * For each RPCall, a server and a Xact
+ * are created and destroyed on the fly.
+ *
+ * This should be used for infrequent calls
+ * (e.g. a NFS mount request).
+ *
+ * This is roughly compatible with the original
+ * clnt_call() etc. API - but it uses our
+ * daemon and is fully reentrant.
+ */
+enum clnt_stat
+rpcUdpClntCreate(
+		struct sockaddr_in	*psaddr,
+		rpcprog_t		prog,
+		rpcvers_t		vers,
+		u_long			uid,
+		u_long			gid,
+		RpcUdpClnt		*pclnt
+)
+{
+RpcUdpXact		x;
+RpcUdpServer	s;
+enum clnt_stat	err;
+
+	if ( RPC_SUCCESS != (err=rpcUdpServerCreate(psaddr, prog, vers, uid, gid, &s)) )
+		return err;
+
+	if ( !(x=rpcUdpXactCreate(prog, vers, UDPMSGSIZE)) ) {
+		rpcUdpServerDestroy(s);
+		return RPC_FAILED;
+	}
+	/* TODO: could maintain a server cache */
+
+	x->server = s;
+
+	*pclnt = x;
+
+	return RPC_SUCCESS;
+}
+
+static void
+rpcUdpClntDestroy(RpcUdpClnt xact)
+{
+	rpcUdpServerDestroy(xact->server);
+	rpcUdpXactDestroy(xact);
+}
+
+enum clnt_stat
+rpcUdpClntCall(
+	RpcUdpClnt		xact,
+	u_long			proc,
+	XdrProcT		xargs,
+	CaddrT			pargs,
+	XdrProcT		xres,
+	CaddrT			pres,
+	struct timeval	*timeout
+	)
+{
+enum clnt_stat	stat;
+
+		if ( (stat = rpcUdpSend(xact, xact->server, timeout, proc,
+								xres, pres,
+								xargs, pargs,
+								0)) ) {
+			fprintf(stderr,"RPCIO Send failed: %i\n",stat);
+			return stat;
+		}
+		return rpcUdpRcv(xact);
+}
+
+/* a yet simpler interface */
+enum clnt_stat
+rpcUdpCallRp(
+	struct sockaddr_in	*psrvr,
+	u_long				prog,
+	u_long				vers,
+	u_long				proc,
+	XdrProcT			xargs,
+	CaddrT				pargs,
+	XdrProcT			xres,
+	CaddrT				pres,
+	u_long				uid,		/* RPCIO_DEFAULT_ID picks default */
+	u_long				gid,		/* RPCIO_DEFAULT_ID picks default */
+	struct timeval		*timeout	/* NULL picks default		*/
+)
+{
+RpcUdpClnt			clp;
+enum clnt_stat		stat;
+
+	stat = rpcUdpClntCreate(
+				psrvr,
+				prog,
+				vers,
+				uid,
+				gid,
+				&clp);
+
+	if ( RPC_SUCCESS != stat )
+		return stat;
+
+	stat = rpcUdpClntCall(
+				clp,
+				proc,
+				xargs, pargs,
+				xres,  pres,
+				timeout);
+
+	rpcUdpClntDestroy(clp);
+
+	return stat;
+}
+
+/* linked list primitives */
+static void
+nodeXtract(ListNode n)
+{
+	if (n->prev)
+		n->prev->next = n->next;
+	if (n->next)
+		n->next->prev = n->prev;
+	n->next = n->prev = 0;
+}
+
+static void
+nodeAppend(ListNode l, ListNode n)
+{
+	if ( (n->next = l->next) )
+		n->next->prev = n;
+	l->next = n;
+	n->prev = l;
+
+}
+
+/* this code does the work */
+static void
+rpcio_daemon(rtems_task_argument arg)
+{
+rtems_status_code stat;
+RpcUdpXact        xact;
+RpcUdpServer      srv;
+rtems_interval    next_retrans, then, unow;
+long			  			now;	/* need to do signed comparison with age! */
+rtems_event_set   events;
+ListNode          newList;
+size_t            size;
+rtems_id          q          =  0;
+ListNodeRec       listHead   = {0, 0};
+unsigned long     epoch      = RPCIOD_EPOCH_SECS * ticksPerSec;
+unsigned long			max_period = RPCIOD_RETX_CAP_S * ticksPerSec;
+rtems_status_code	status;
+
+
+        then = rtems_clock_get_ticks_since_boot();
+
+	for (next_retrans = epoch;;) {
+
+		if ( RTEMS_SUCCESSFUL !=
+			 (stat = rtems_event_receive(
+						RPCIOD_RX_EVENT | RPCIOD_TX_EVENT | RPCIOD_KILL_EVENT,
+						RTEMS_WAIT | RTEMS_EVENT_ANY,
+						next_retrans,
+						&events)) ) {
+			ASSERT( RTEMS_TIMEOUT == stat );
+			events = 0;
+		}
+
+		if (events & RPCIOD_KILL_EVENT) {
+			int i;
+
+#if (DEBUG) & DEBUG_EVENTS
+			fprintf(stderr,"RPCIO: got KILL event\n");
+#endif
+
+			MU_LOCK(hlock);
+			for (i=XACT_HASHS-1; i>=0; i--) {
+				if (xactHashTbl[i]) {
+					break;
+				}
+			}
+			if (i<0) {
+				/* prevent them from creating and enqueueing more messages */
+				q=msgQ;
+				/* messages queued after we executed this assignment will fail */
+				msgQ=0;
+			}
+			MU_UNLOCK(hlock);
+			if (i>=0) {
+				fprintf(stderr,"RPCIO There are still transactions circulating; I refuse to go away\n");
+				fprintf(stderr,"(1st in slot %i)\n",i);
+				rtems_semaphore_release(fini);
+			} else {
+				break;
+			}
+		}
+
+        	unow = rtems_clock_get_ticks_since_boot();
+
+		/* measure everything relative to then to protect against
+		 * rollover
+		 */
+		now = unow - then;
+
+		/* NOTE: we don't lock the hash table while we are operating
+		 * on transactions; the paradigm is that we 'own' a particular
+		 * transaction (and hence it's hash table slot) from the
+		 * time the xact was put into the message queue until we
+		 * wake up the requestor.
+		 */
+
+		if (RPCIOD_RX_EVENT & events) {
+
+#if (DEBUG) & DEBUG_EVENTS
+			fprintf(stderr,"RPCIO: got RX event\n");
+#endif
+
+			while ((xact=sockRcv())) {
+
+				/* extract from the retransmission list */
+				nodeXtract(&xact->node);
+
+				/* change the ID - there might already be
+				 * a retransmission on the way. When it's
+				 * reply arrives we must not find it's ID
+				 * in the hashtable
+				 */
+				xact->obuf.xid        += XACT_HASHS;
+
+				xact->status.re_status = RPC_SUCCESS;
+
+				/* calculate roundtrip ticks */
+				xact->trip             = now - xact->trip;
+
+				srv                    = xact->server;
+
+				/* adjust the server's retry period */
+				{
+					register TimeoutT rtry = srv->retry_period;
+					register TimeoutT trip = xact->trip;
+
+					ASSERT( trip >= 0 );
+
+					if ( 0==trip )
+						trip = 1;
+
+					/* retry_new = 0.75*retry_old + 0.25 * 8 * roundrip */
+					rtry   = (3*rtry + (trip << 3)) >> 2;
+
+					if ( rtry > max_period )
+						rtry = max_period;
+
+					srv->retry_period = rtry;
+				}
+
+				/* wakeup requestor */
+				rtems_event_send(xact->requestor, RTEMS_RPC_EVENT);
+			}
+		}
+
+		if (RPCIOD_TX_EVENT & events) {
+
+#if (DEBUG) & DEBUG_EVENTS
+			fprintf(stderr,"RPCIO: got TX event\n");
+#endif
+
+			while (RTEMS_SUCCESSFUL == rtems_message_queue_receive(
+											msgQ,
+											&xact,
+											&size,
+											RTEMS_NO_WAIT,
+											RTEMS_NO_TIMEOUT)) {
+				/* put to the head of timeout q */
+				nodeAppend(&listHead, &xact->node);
+
+				xact->age  = now;
+				xact->trip = FIRST_ATTEMPT;
+			}
+		}
+
+
+		/* work the timeout q */
+		newList = 0;
+		for ( xact=(RpcUdpXact)listHead.next;
+			  xact && xact->age <= now;
+			  xact=(RpcUdpXact)listHead.next ) {
+
+				/* extract from the list */
+				nodeXtract(&xact->node);
+
+				srv = xact->server;
+
+				if (xact->tolive < 0) {
+					/* this one timed out */
+					xact->status.re_errno  = ETIMEDOUT;
+					xact->status.re_status = RPC_TIMEDOUT;
+
+					srv->timeouts++;
+
+					/* Change the ID - there might still be
+					 * a reply on the way. When it arrives we
+					 * must not find it's ID in the hash table
+					 *
+					 * Thanks to Steven Johnson for hunting this
+					 * one down.
+					 */
+					xact->obuf.xid        += XACT_HASHS;
+
+#if (DEBUG) & DEBUG_TIMEOUT
+					fprintf(stderr,"RPCIO XACT timed out; waking up requestor\n");
+#endif
+					if ( rtems_event_send(xact->requestor, RTEMS_RPC_EVENT) ) {
+						rtems_panic("RPCIO PANIC: requestor id was 0x%08x",
+									xact->requestor);
+					}
+
+				} else {
+					int len;
+
+					len = (int)XDR_GETPOS(&xact->xdrs);
+
+#ifdef MBUF_TX
+					xact->refcnt = 1;	/* sendto itself */
+#endif
+					if ( len != SENDTO( ourSock,
+										xact->obuf.buf,
+										len,
+										0,
+										&srv->addr.sa,
+										sizeof(srv->addr.sin)
+#ifdef MBUF_TX
+										, xact,
+										paranoia_free,
+										paranoia_ref
+#endif
+										) ) {
+
+						xact->status.re_errno  = errno;
+						xact->status.re_status = RPC_CANTSEND;
+						srv->errors++;
+
+						/* wakeup requestor */
+						fprintf(stderr,"RPCIO: SEND failure\n");
+						status = rtems_event_send(xact->requestor, RTEMS_RPC_EVENT);
+						assert( status == RTEMS_SUCCESSFUL );
+
+					} else {
+						/* send successful; calculate retransmission time
+						 * and enqueue to temporary list
+						 */
+						if (FIRST_ATTEMPT != xact->trip) {
+#if (DEBUG) & DEBUG_TIMEOUT
+							fprintf(stderr,
+								"timed out; tolive is %i (ticks), retry period is %i (ticks)\n",
+									xact->tolive,
+									srv->retry_period);
+#endif
+							/* this is a real retry; we backup
+							 * the server's retry interval
+							 */
+							if ( srv->retry_period < max_period ) {
+
+								/* If multiple transactions for this server
+								 * fail (e.g. because it died) this will
+								 * back-off very agressively (doubling
+								 * the retransmission period for every
+								 * timed out transaction up to the CAP limit)
+								 * which is desirable - single packet failure
+								 * is treated more gracefully by this algorithm.
+								 */
+
+								srv->retry_period<<=1;
+#if (DEBUG) & DEBUG_TIMEOUT
+								fprintf(stderr,
+										"adjusted to; retry period %i\n",
+										srv->retry_period);
+#endif
+							} else {
+								/* never wait longer than RPCIOD_RETX_CAP_S seconds */
+								fprintf(stderr,
+										"RPCIO: server '%s' not responding - still trying\n",
+										srv->name);
+							}
+							if ( 0 == ++srv->retrans % 1000) {
+								fprintf(stderr,
+										"RPCIO - statistics: already %li retries to server %s\n",
+										srv->retrans,
+										srv->name);
+							}
+						} else {
+							srv->requests++;
+						}
+						xact->trip      = now;
+						{
+						long capped_period = srv->retry_period;
+							if ( xact->lifetime < capped_period )
+								capped_period = xact->lifetime;
+						xact->age       = now + capped_period;
+						xact->tolive   -= capped_period;
+						}
+						/* enqueue to the list of newly sent transactions */
+						xact->node.next = newList;
+						newList         = &xact->node;
+#if (DEBUG) & DEBUG_TIMEOUT
+						fprintf(stderr,
+								"XACT (0x%08x) age is 0x%x, now: 0x%x\n",
+								xact,
+								xact->age,
+								now);
+#endif
+					}
+				}
+	    }
+
+		/* insert the newly sent transactions into the
+		 * sorted retransmission list
+		 */
+		for (; (xact = (RpcUdpXact)newList); ) {
+			register ListNode p,n;
+			newList = newList->next;
+			for ( p=&listHead; (n=p->next) && xact->age > ((RpcUdpXact)n)->age; p=n )
+				/* nothing else to do */;
+			nodeAppend(p, &xact->node);
+		}
+
+		if (now > epoch) {
+			/* every now and then, readjust the epoch */
+			register ListNode n;
+			then += now;
+			for (n=listHead.next; n; n=n->next) {
+				/* readjust outstanding time intervals subject to the
+				 * condition that the 'absolute' time must remain
+				 * the same. 'age' and 'trip' are measured with
+				 * respect to 'then' - hence:
+				 *
+				 * abs_age == old_age + old_then == new_age + new_then
+				 *
+				 * ==> new_age = old_age + old_then - new_then == old_age - 'now'
+				 */
+				((RpcUdpXact)n)->age  -= now;
+				((RpcUdpXact)n)->trip -= now;
+#if (DEBUG) & DEBUG_TIMEOUT
+				fprintf(stderr,
+						"readjusted XACT (0x%08x); age is 0x%x, trip: 0x%x now: 0x%x\n",
+						(RpcUdpXact)n,
+						((RpcUdpXact)n)->trip,
+						((RpcUdpXact)n)->age,
+						now);
+#endif
+			}
+			now = 0;
+		}
+
+		next_retrans = listHead.next ?
+							((RpcUdpXact)listHead.next)->age - now :
+							epoch;	/* make sure we don't miss updating the epoch */
+#if (DEBUG) & DEBUG_TIMEOUT
+		fprintf(stderr,"RPCIO: next timeout is %x\n",next_retrans);
+#endif
+	}
+	/* close our socket; shut down the receiver */
+	close(ourSock);
+
+#if 0 /* if we get here, no transactions exist, hence there can be none
+	   * in the queue whatsoever
+	   */
+	/* flush the message queue */
+	while (RTEMS_SUCCESSFUL == rtems_message_queue_receive(
+										q,
+										&xact,
+										&size,
+										RTEMS_NO_WAIT,
+										RTEMS_NO_TIMEOUT)) {
+			/* TODO enque xact */
+	}
+
+	/* flush all outstanding transactions */
+
+	for (xact=((RpcUdpXact)listHead.next); xact; xact=((RpcUdpXact)xact->node.next)) {
+			xact->status.re_status = RPC_TIMEDOUT;
+			rtems_event_send(xact->requestor, RTEMS_RPC_EVENT);
+	}
+#endif
+
+	rtems_message_queue_delete(q);
+
+	MU_DESTROY(hlock);
+
+	fprintf(stderr,"RPC daemon exited...\n");
+
+	rtems_semaphore_release(fini);
+	rtems_task_suspend(RTEMS_SELF);
+}
+
+
+/* support for transaction 'pools'. A number of XACT objects
+ * is always kept around. The initial number is 0 but it
+ * is allowed to grow up to a maximum.
+ * If the need grows beyond the maximum, behavior depends:
+ * Users can either block until a transaction becomes available,
+ * they can create a new XACT on the fly or get an error
+ * if no free XACT is available from the pool.
+ */
+
+RpcUdpXactPool
+rpcUdpXactPoolCreate(
+	rpcprog_t prog, 		rpcvers_t version,
+	int xactsize,	int poolsize)
+{
+RpcUdpXactPool	rval = MY_MALLOC(sizeof(*rval));
+rtems_status_code	status;
+
+	ASSERT( rval );
+	status = rtems_message_queue_create(
+					rtems_build_name('R','P','C','p'),
+					poolsize,
+					sizeof(RpcUdpXact),
+					RTEMS_DEFAULT_ATTRIBUTES,
+					&rval->box);
+	assert( status == RTEMS_SUCCESSFUL );
+
+	rval->prog     = prog;
+	rval->version  = version;
+	rval->xactSize = xactsize;
+	return rval;
+}
+
+void
+rpcUdpXactPoolDestroy(RpcUdpXactPool pool)
+{
+RpcUdpXact xact;
+
+	while ((xact = rpcUdpXactPoolGet(pool, XactGetFail))) {
+		rpcUdpXactDestroy(xact);
+	}
+	rtems_message_queue_delete(pool->box);
+	MY_FREE(pool);
+}
+
+RpcUdpXact
+rpcUdpXactPoolGet(RpcUdpXactPool pool, XactPoolGetMode mode)
+{
+RpcUdpXact	 xact = 0;
+size_t           size;
+
+	if (RTEMS_SUCCESSFUL != rtems_message_queue_receive(
+								pool->box,
+								&xact,
+								&size,
+								XactGetWait == mode ?
+									RTEMS_WAIT : RTEMS_NO_WAIT,
+								RTEMS_NO_TIMEOUT)) {
+
+		/* nothing found in box; should we create a new one ? */
+
+		xact = (XactGetCreate == mode) ?
+					rpcUdpXactCreate(
+							pool->prog,
+							pool->version,
+							pool->xactSize) : 0 ;
+		if (xact)
+				xact->pool = pool;
+
+	}
+	return xact;
+}
+
+void
+rpcUdpXactPoolPut(RpcUdpXact xact)
+{
+RpcUdpXactPool pool;
+
+	pool = xact->pool;
+	ASSERT( pool );
+
+	if (RTEMS_SUCCESSFUL != rtems_message_queue_send(
+								pool->box,
+								&xact,
+								sizeof(xact)))
+		rpcUdpXactDestroy(xact);
+}
+
+#ifdef MBUF_RX
+
+/* WORKAROUND: include sys/mbuf.h (or other bsdnet headers) only
+ *             _after_ using malloc()/free() & friends because
+ *             the RTEMS/BSDNET headers redefine those :-(
+ */
+
+#define _KERNEL
+#include <sys/mbuf.h>
+
+static void
+bufFree(struct mbuf **m)
+{
+	if (*m) {
+		rtems_bsdnet_semaphore_obtain();
+		m_freem(*m);
+		rtems_bsdnet_semaphore_release();
+		*m = 0;
+	}
+}
+#endif
+
+#ifdef MBUF_TX
+static void
+paranoia_free(caddr_t closure, u_int size)
+{
+#if (DEBUG)
+RpcUdpXact xact = (RpcUdpXact)closure;
+int        len  = (int)XDR_GETPOS(&xact->xdrs);
+
+	ASSERT( --xact->refcnt >= 0 && size == len );
+#endif
+}
+
+static void
+paranoia_ref (caddr_t closure, u_int size)
+{
+#if (DEBUG)
+RpcUdpXact xact = (RpcUdpXact)closure;
+int        len  = (int)XDR_GETPOS(&xact->xdrs);
+	ASSERT( size == len );
+	xact->refcnt++;
+#endif
+}
+#endif
+
+/* receive from a socket and find
+ * the transaction corresponding to the
+ * transaction ID received in the server
+ * reply.
+ *
+ * The semantics of the 'pibuf' pointer are
+ * as follows:
+ *
+ * MBUF_RX:
+ *
+ */
+
+#define RPCIOD_RXBUFSZ	UDPMSGSIZE
+
+static RpcUdpXact
+sockRcv(void)
+{
+int					len,i;
+uint32_t				xid;
+union {
+	struct sockaddr_in	sin;
+	struct sockaddr     sa;
+}					fromAddr;
+int					fromLen  = sizeof(fromAddr.sin);
+RxBuf				ibuf     = 0;
+RpcUdpXact			xact     = 0;
+
+	do {
+
+	/* rcv_mbuf() and recvfrom() differ in that the
+	 * former allocates buffers and passes them back
+	 * to us whereas the latter requires us to provide
+	 * buffer space.
+	 * Hence, in the first case whe have to make sure
+	 * no old buffer is leaked - in the second case,
+	 * we might well re-use an old buffer but must
+	 * make sure we have one allocated
+	 */
+#ifdef MBUF_RX
+	if (ibuf)
+		bufFree(&ibuf);
+
+	len  = recv_mbuf_from(
+					ourSock,
+					&ibuf,
+					RPCIOD_RXBUFSZ,
+				    &fromAddr.sa,
+				    &fromLen);
+#else
+	if ( !ibuf )
+		ibuf = (RpcBuf)MY_MALLOC(RPCIOD_RXBUFSZ);
+	if ( !ibuf )
+		goto cleanup; /* no memory - drop this message */
+
+	len  = recvfrom(ourSock,
+				    ibuf->buf,
+				    RPCIOD_RXBUFSZ,
+				    0,
+				    &fromAddr.sa,
+					&fromLen);
+#endif
+
+	if (len <= 0) {
+		if (EAGAIN != errno)
+			fprintf(stderr,"RECV failed: %s\n",strerror(errno));
+		goto cleanup;
+	}
+
+#if (DEBUG) & DEBUG_PACKLOSS
+	if ( (unsigned)rand() < DEBUG_PACKLOSS_FRACT ) {
+		/* lose packets once in a while */
+		static int xxx = 0;
+		if ( ++xxx % 16 == 0 )
+			fprintf(stderr,"DEBUG: dropped %i packets, so far...\n",xxx);
+		if ( ibuf )
+			bufFree( &ibuf );
+		continue;
+	}
+#endif
+
+	i = (xid=XID(ibuf)) & XACT_HASH_MSK;
+
+	if ( !(xact=xactHashTbl[i])                                             ||
+		   xact->obuf.xid                     != xid                        ||
+#ifdef REJECT_SERVERIP_MISMATCH
+		   xact->server->addr.sin.sin_addr.s_addr != fromAddr.sin.sin_addr.s_addr	||
+#endif
+		   xact->server->addr.sin.sin_port        != fromAddr.sin.sin_port ) {
+
+		if (xact) {
+			if (
+#ifdef REJECT_SERVERIP_MISMATCH
+			    xact->server->addr.sin.sin_addr.s_addr == fromAddr.sin.sin_addr.s_addr &&
+#endif
+		        xact->server->addr.sin.sin_port        == fromAddr.sin.sin_port        &&
+			    ( xact->obuf.xid                   == xid + XACT_HASHS   ||
+				  xact->obuf.xid                   == xid + 2*XACT_HASHS    )
+				) {
+#ifndef DEBUG /* don't complain if it's just a late arrival of a retry */
+			fprintf(stderr,"RPCIO - FYI sockRcv(): dropping late/redundant retry answer\n");
+#endif
+			} else {
+			fprintf(stderr,"RPCIO WARNING sockRcv(): transaction mismatch\n");
+			fprintf(stderr,"xact: xid  0x%08" PRIx32 "  -- got 0x%08" PRIx32 "\n",
+							xact->obuf.xid, xid);
+			fprintf(stderr,"xact: addr 0x%08" PRIx32 "  -- got 0x%08" PRIx32 "\n",
+							xact->server->addr.sin.sin_addr.s_addr,
+							fromAddr.sin.sin_addr.s_addr);
+			fprintf(stderr,"xact: port 0x%08x  -- got 0x%08x\n",
+							xact->server->addr.sin.sin_port,
+							fromAddr.sin.sin_port);
+			}
+		} else {
+			fprintf(stderr,
+					"RPCIO WARNING sockRcv(): got xid 0x%08" PRIx32 " but its slot is empty\n",
+					xid);
+		}
+		/* forget about this one and try again */
+		xact = 0;
+	}
+
+	} while ( !xact );
+
+	xact->ibuf     = ibuf;
+#ifndef MBUF_RX
+	xact->ibufsize = RPCIOD_RXBUFSZ;
+#endif
+
+	return xact;
+
+cleanup:
+
+	bufFree(&ibuf);
+
+	return 0;
+}
+
+
+#include <rtems/rtems_bsdnet_internal.h>
+/* double check the event configuration; should probably globally
+ * manage system events!!
+ * We do this at the end of the file for the same reason we had
+ * included mbuf.h only a couple of lines above - see comment up
+ * there...
+ */
+#if RTEMS_RPC_EVENT & SOSLEEP_EVENT & SBWAIT_EVENT & NETISR_EVENTS
+#error ILLEGAL EVENT CONFIGURATION
+#endif
Index: rtemsbsd/nfsclient/rpcio.h
===================================================================
--- rtemsbsd/nfsclient/rpcio.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
+++ rtemsbsd/nfsclient/rpcio.h	(revision 446459456783e5638dedca1410dfc599dc65eae3)
@@ -0,0 +1,226 @@
+/**
+ * @file
+ *
+ * @brief A Multithreaded RPC/UDP Multiplexor
+ *
+ * @ingroup rtems-nfsclient
+ */
+
+/*
+ * Author: Till Straumann, <strauman@slac.stanford.edu>, 2002
+ *
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
+ * 	   Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The NFS-2 client implementation for RTEMS was produced by
+ *     the Stanford Linear Accelerator Center, Stanford University,
+ * 	   under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software.  Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#ifndef RPCIO_H
+#define RPCIO_H
+
+/**
+ * @defgroup rtems-nfsclient RPC/UDP Multiplexor
+ *
+ * @ingroup nfsclient
+ * @{
+ */
+
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <stdarg.h>
+
+#include "librtemsNfs.h"
+
+typedef struct RpcUdpServerRec_ 	*RpcUdpServer;
+typedef struct RpcUdpXactRec_		*RpcUdpXact;
+
+typedef RpcUdpXact					RpcUdpClnt;
+
+#define RPCIOD_DEFAULT_ID	0xdef10000
+
+enum clnt_stat
+rpcUdpServerCreate(
+	struct sockaddr_in	*paddr,
+	rpcprog_t		prog,
+	rpcvers_t		vers,
+	u_long			uid,		/* RPCIO_DEFAULT_ID picks default */
+	u_long			gid,		/* RPCIO_DEFAULT_ID picks default */
+	RpcUdpServer		*pclnt		/* new server is returned here    */
+	);
+
+
+void
+rpcUdpServerDestroy(RpcUdpServer s);
+
+/**
+ * @brief Dump statistics to a file (stdout if NULL);
+ * @retval 0 for convenience
+ */
+int
+rpcUdpStats(FILE *f);
+
+enum clnt_stat
+rpcUdpClntCreate(
+	struct sockaddr_in	*psaddr,
+	rpcprog_t		prog,
+	rpcvers_t		vers,
+	u_long			uid,		/* RPCIO_DEFAULT_ID picks default */
+	u_long			gid,		/* RPCIO_DEFAULT_ID picks default */
+	RpcUdpClnt		*pclnt		/* new client is returned here    */
+	);
+
+void
+RpcUdpClntDestroy(RpcUdpClnt clnt);
+
+/**
+ * @brief Mute compiler warnings.
+ */
+typedef void *XdrProcT;
+typedef void *CaddrT;
+
+enum clnt_stat
+rpcUdpClntCall(
+	RpcUdpClnt		clnt,
+	u_long			proc,
+	XdrProcT		xargs,
+	CaddrT			pargs,
+	XdrProcT		xres,
+	CaddrT			pres,
+	struct timeval	*timeout	/* optional timeout; maybe NULL to pick default */
+	);
+
+RpcUdpXact
+rpcUdpXactCreate(
+	u_long	program,
+	u_long	version,
+	u_long	size
+	);
+
+void
+rpcUdpXactDestroy(
+	RpcUdpXact xact
+	);
+
+/**
+ * Send a transaction.
+ */
+enum clnt_stat
+rpcUdpSend(
+	RpcUdpXact		xact,
+	RpcUdpServer	srvr,
+	struct timeval	*timeout,	/* maybe NULL to pick default */
+	u_long			proc,
+	xdrproc_t		xres,
+	caddr_t			pres,
+	xdrproc_t		xargs,
+	caddr_t			pargs,
+	...				/* 0 terminated xdrproc/pobj additional argument list */
+	);
+
+/**
+ * @brief Wait for a transaction to complete.
+ */
+enum clnt_stat
+rpcUdpRcv(RpcUdpXact xact);
+
+/* a yet simpler interface */
+enum clnt_stat
+rpcUdpCallRp(
+	struct sockaddr_in	*pserver_addr,
+	u_long				prog,
+	u_long				vers,
+	u_long				proc,
+	XdrProcT			xargs,
+	CaddrT				pargs,
+	XdrProcT			xres,
+	CaddrT				pres,
+	u_long				uid,		/* RPCIO_DEFAULT_ID picks default */
+	u_long				gid,		/* RPCIO_DEFAULT_ID picks default */
+	struct timeval		*timeout	/* NULL picks default		*/
+);
+
+
+
+/*
+ * @brief Manage pools of transactions.
+ *
+ * A pool of transactions. The idea is not to malloc/free them
+ * all the time but keep a limited number around in a 'pool'.
+ * Users who need a XACT may get it from the pool and put it back
+ * when done.
+ * The pool is implemented by RTEMS message queues who manage
+ * the required task synchronization.
+ * A requestor has different options if the pool is empty:
+ *  - it can wait (block) for a XACT to become available
+ *  - it can get an error status
+ *  - or it can malloc an extra XACT from the heap which
+ *    will eventually be released.
+ */
+
+typedef struct RpcUdpXactPoolRec_  *RpcUdpXactPool;
+
+/* NOTE: the pool is empty initially, must get messages (in
+ *       GetCreate mode
+ */
+RpcUdpXactPool
+rpcUdpXactPoolCreate(
+	rpcprog_t prog, rpcvers_t version,
+	int xactsize,	int poolsize);
+
+void
+rpcUdpXactPoolDestroy(RpcUdpXactPool pool);
+
+typedef enum {
+	XactGetFail,	/* call fails if no transaction available */
+	XactGetWait,	/* call blocks until transaction available */
+	XactGetCreate	/* a new transaction is allocated (and freed when put back to the pool */
+} XactPoolGetMode;
+
+RpcUdpXact
+rpcUdpXactPoolGet(RpcUdpXactPool pool, XactPoolGetMode mode);
+
+void
+rpcUdpXactPoolPut(RpcUdpXact xact);
+
+/** @} */
+#endif
