[rtems-libbsd commit] nfsclient: Initial addition

Joel Sherrill joel at rtems.org
Fri Aug 3 19:17:39 UTC 2012


Module:    rtems-libbsd
Branch:    master
Commit:    8f59c0aea7467a0dcfcf46138c3d1a422cb339fd
Changeset: http://git.rtems.org/rtems-libbsd/commit/?id=8f59c0aea7467a0dcfcf46138c3d1a422cb339fd

Author:    Joel Sherrill <joel.sherrill at oarcorp.com>
Date:      Fri Aug  3 14:21:13 2012 -0500

nfsclient: Initial addition

Will not compile until librpc is available.

---

 services/nfsclient/ChangeLog.slac         |  112 ++
 services/nfsclient/LICENSE                |   44 +
 services/nfsclient/Makefile               |   49 +
 services/nfsclient/README                 |  548 ++++++
 services/nfsclient/cexphelp.c             |   20 +
 services/nfsclient/dirutils.c             |  383 ++++
 services/nfsclient/include/librtemsNfs.h  |  179 ++
 services/nfsclient/include/rpcio.h        |  208 ++
 services/nfsclient/nfs.c                  | 3051 +++++++++++++++++++++++++++++
 services/nfsclient/nfs.modini.c           |   31 +
 services/nfsclient/nfsTest.c              |  379 ++++
 services/nfsclient/proto/mount_prot.h     |  144 ++
 services/nfsclient/proto/mount_prot.x     |  161 ++
 services/nfsclient/proto/mount_prot_xdr.c |  104 +
 services/nfsclient/proto/nfs_prot.h       |  453 +++++
 services/nfsclient/proto/nfs_prot.x       | 1268 ++++++++++++
 services/nfsclient/proto/nfs_prot_xdr.c   |  621 ++++++
 services/nfsclient/rfc1094.txt            | 1258 ++++++++++++
 services/nfsclient/rpcio.c                | 1790 +++++++++++++++++
 services/nfsclient/rpcio.modini.c         |   19 +
 services/nfsclient/sock_mbuf.c            |  281 +++
 services/nfsclient/xdr_mbuf.c             |  537 +++++
 22 files changed, 11640 insertions(+), 0 deletions(-)

diff --git a/services/nfsclient/ChangeLog.slac b/services/nfsclient/ChangeLog.slac
new file mode 100644
index 0000000..94dea06
--- /dev/null
+++ b/services/nfsclient/ChangeLog.slac
@@ -0,0 +1,112 @@
+Changes since RTEMS-NFS 1.4:
+LICENSE:
+ - changed license terms; RTEMS-NFS is now released under the more liberal
+   'SLAC license'.
+NFS:
+ - silenced compiler warnings (follow handler declaration changes in libio.h
+   by using 'size_t' instead of 'uint32_t' etc.).
+
+Changes since RTEMS-NFS 1.3:
+RPCIOD:
+ - round timeout to next system clock tick and ensure it is at least
+   1 tick.
+ - cap retransmission timeout to transaction lifetime.
+ - BUGFIX (reported by Steven Johnson, 12/5/06): we must change the XID
+   before sending a timed-out transaction back to the requestor to prevent
+   a late reply from being accepted.
+ - Made task priority run-time configurable (and read from rtems_bsdnet_config
+   by default).
+
+NFS:
+ - added nfsGetTimeout() and nfsSetTimeout() to retrieve and modify,
+   respectively the (global) timeout for NFS/MOUNT operations.
+ - Minor mod. to fix 'type-punned pointer' warning (can avoid pointer cast).
+ - Added global variable 'nfsStBlksize' (defaults to 8k) which is now
+   used to report the file system's 'preferred' blocksize (stat(2)/st_blksize).
+   The old behavior (server's fattr.st_blksize is passed through) can be
+   obtained by setting nfsStBlksize=0.
+   The new feature lets stdio use 8k buffers (only if newlib was built with
+   HAVE_BLKSIZE defined). This enhances NFS (buffered) read performance quite
+   a bit. Thanks to Steven Johnson <sjohnson at sakuraindustries.com> for helping
+   with this.
+ - Updated README (performance section).
+ - Added simple performance test: nfsTest.c
+
+Changes since RTEMS-NFS 1.3_pre2:
+ RPCIOD:
+ - fix a problem with NFS server clusters (reply comes from an IP address
+   different from the destination of the request) by relaxing paranoia checks.
+
+Changes since RTEMS-NFS 1.2:
+ NFS:
+ - replaced inet_aton -> inet_pton
+ - replaced unsigned32 -> uint32_t
+ - added _KERNEL definition for 4.7 compilation
+ - silenced compiler warnings (4.7)
+ - added -Wno-unused-variable in 'proto' -- rpcgen produces a lot of them.
+ - new locking scheme. The 'in-use' counters cannot be protected by a mutex
+   because files might be closed when a thread is deleted from a dispatch-disabled
+   section where mutexes must not be locked. The counters are now protected by
+   disabling interrupts.
+   The only critical race-condition I can see ATM is while the NFS is being
+   unmounted and the mount point is crossed by another thread. It should be the
+   generic FS code's responsibility to handle that (but AFAIK, it doesn't) --
+   it's out of our scope...
+ - ftruncate didn't work. The functionality is achieved by nfs_sattr()
+   setting the file size to 0. However, nfs_sattr() always tried to set
+   all attributes (re-applying the current values to fields we didn't
+   want to change) which failed (EPERM) if we were not the owner.
+   Now, we restrict modifications to the requested fields (in case of
+   ftruncate this is *only* the size), adhering to rfc1094 (which states
+   that unused fields shall be set to -1).
+ - lseek(SEEK_END) didn't work. The underlying RTEMS filesystem code
+   uses an internal file 'size' field to compute the offset whence SEEK_END.
+   Instead of painfully maintaining 'size' across all system calls, we
+   just tweak the offset for SEEK_END and leave 'size' unused.
+ - fix: O_APPEND wasn't honoured. Note that there is no NFS 'append' call - 
+   the client simply uses the currently available idea of the file size
+   to set the write offset. This obviously is subject to race conditions
+   if multiple clients are writing the same file.
+ dirutils:
+ - replaced read/write calls by stdio; In case of copying to stdout, I
+   experienced occasional crashes when write(fileno(stdout),...) -- according
+   to the standard, mixing low-level i/o with stdio might produce undefined
+   results; there we go...
+
+Changes since RTEMS-NFS 1.1:
+ NFS:
+ - unlink() didnt work. The underlying RTEMS filesystem code evaluates
+   a '..' path on a non-directory node to find out the file's parent
+   directory. Workaround to this semantically inelegant RTEMS feature
+   was implemented.
+  
+Changes since RTEMS-NFS 1.0.beta3:
+ NFS:
+ - fixed possible string overrun in nfsMount
+ - nfs_read_dir() must reset the 'eofreached' flag if it skipped
+   dirents present in the xdr but not fitting into the user buffer.
+ - nfsMountsShow() released the wrong lock!
+ RPCIO:
+ - cannot delete locked binary semaphore (authlock) -- must unlock
+   first (authlock was never deleted and hence effectively leaked)
+ - added ASSERT paranoia around mutex primitives
+ - Relaxed paranoia check / ASSERTion failure:
+   paranoia_free() is called more than once on an ext_buf - it must
+   undo calls to paranoia_refcnt() - hence the 0 == --refcnt check
+   is too strict.
+ - Added a DEBUG flag to introduce random packet losses for testing
+   retransmission.
+ xdr_mbuf:
+ - make sure we do a signed comparison
+
+Changes since rtemsNFS-1.0.beta2:
+ - moved 'tar' command to the 'config' area; use
+   predefined 'make-tar' in individual Makefiles
+ - use INSTALL_CHANGE for headers, not INSTALL_VARIANT (probably doesn't
+   matter, though)
+ - use LD not LD_FOR_TARGET (to get absolute path)
+ - fixed assertion failure print format
+ - print requestor id if send_event fails - had just experienced this :-(
+ - hint about fprintf using FP registers is probably PPC specific
+ - provided implementation for xdrmbuf_getlong_aligned(). i386-rtems
+   seems to use it.
diff --git a/services/nfsclient/LICENSE b/services/nfsclient/LICENSE
new file mode 100644
index 0000000..4687f9a
--- /dev/null
+++ b/services/nfsclient/LICENSE
@@ -0,0 +1,44 @@
+/* 
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman at 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
+ */ 
diff --git a/services/nfsclient/Makefile b/services/nfsclient/Makefile
new file mode 100644
index 0000000..a7fbfe0
--- /dev/null
+++ b/services/nfsclient/Makefile
@@ -0,0 +1,49 @@
+include ../../config.inc
+
+include $(RTEMS_MAKEFILE_PATH)/Makefile.inc
+include $(RTEMS_CUSTOM)
+include $(PROJECT_ROOT)/make/leaf.cfg
+
+CFLAGS += -I $(INSTALL_BASE)/include
+
+CFLAGS += -w 
+CFLAGS += -I include 
+CFLAGS += -I proto 
+CFLAGS += -std=gnu99
+CFLAGS += -MT $@ -MD -MP -MF $(basename $@).d
+
+C_FILES =
+C_FILES += nfs.c
+C_FILES += rpcio.c
+C_FILES += sock_mbuf.c
+C_FILES += xdr_mbuf.c
+
+# CEXP additions
+# C_FILES += cexphelp.c dirutils.c nfs.modini.c rpcio.modini.c
+
+# This appears to be a test program which no longer compiles and was in
+# the wrong directory
+C_FILES += nfsTest.c
+
+C_O_FILES = $(C_FILES:%.c=%.o)
+C_D_FILES = $(C_FILES:%.c=%.d)
+
+LIB = libnfs.a
+
+all: $(LIB)
+
+$(LIB): $(C_O_FILES)
+	$(AR) rcu $@ $^
+
+install: $(LIB)
+	install -d $(INSTALL_BASE)/include/rtems
+	install -c -m 644 include/rtems/ftpfs.h $(INSTALL_BASE)/
+	install -c -m 644 $(LIB) $(INSTALL_BASE)
+
+clean:
+	rm -f $(LIB) $(C_O_FILES) $(C_D_FILES) $(GEN_FILES)
+
+-include $(C_D_FILES)
+
+doc: 
+
diff --git a/services/nfsclient/README b/services/nfsclient/README
new file mode 100644
index 0000000..944b830
--- /dev/null
+++ b/services/nfsclient/README
@@ -0,0 +1,548 @@
+RTEMS-NFS
+=========
+
+A NFS-V2 client implementation for the RTEMS real-time
+executive.
+
+Author: Till Straumann <strauman at slac.stanford.edu>, 2002
+
+Copyright 2002, Stanford University and
+                Till Straumann <strauman at 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 at 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.
diff --git a/services/nfsclient/cexphelp.c b/services/nfsclient/cexphelp.c
new file mode 100644
index 0000000..d0406ad
--- /dev/null
+++ b/services/nfsclient/cexphelp.c
@@ -0,0 +1,20 @@
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <librtemsNfs.h>
+#include <cexpHelp.h>
+CEXP_HELP_TAB_BEGIN(rtemsNfs)
+	HELP(
+"Mount a remote filesystem (NFS). The mount point (must not be a NFS dir)\n"
+"is created on the fly if not existing already.\n"
+"uid/gid to use may be specified:\n"
+"   hostspec: [uid.gid@]hostname_or_ipaddr\n"
+	, int, nfsMount, (char *hostspec, char *exportdir, char *mntpoint)
+		),
+	HELP(
+"Print all currently mounted NFS directories to open file handle.\n"
+"Pass f = 0 to print to stdout\n"
+	, int, nfsMountsShow, (FILE *f)
+		),
+CEXP_HELP_TAB_END
diff --git a/services/nfsclient/dirutils.c b/services/nfsclient/dirutils.c
new file mode 100644
index 0000000..357aab9
--- /dev/null
+++ b/services/nfsclient/dirutils.c
@@ -0,0 +1,383 @@
+/* very crude and basic fs utilities for testing the NFS */
+
+/* Till Straumann, <strauman at slac.stanford.edu>, 10/2002 */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman at 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
+
+#ifdef __vxworks
+#include <vxWorks.h>
+#endif
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h> /* PATH_MAX */
+
+#include <inttypes.h> /* PRI* */
+
+#if SIZEOF_MODE_T == 8
+#define PRIomode_t PRIo64
+#elif SIZEOF_MODE_T == 4
+#define PRIomode_t PRIo32
+#else
+#error "unsupport size of mode_t"
+#endif
+
+#if SIZEOF_OFF_T == 8
+#define PRIdoff_t PRIo64
+#elif SIZEOF_OFF_T == 4
+#define PRIdoff_t PRIo32
+#else
+#error "unsupported size of off_t"
+#endif
+
+#ifdef HAVE_CEXP
+#include <cexpHelp.h>
+#endif
+
+#ifndef __vxworks
+int
+pwd(void)
+{
+char buf[PATH_MAX];
+
+	if ( !getcwd(buf,PATH_MAX)) {
+		perror("getcwd");
+		return -1;
+	} else {
+		printf("%s\n",buf);
+	}
+	return 0;
+}
+
+static int
+ls_r(char *path, char *chpt, char *name, struct stat *buf)
+{
+char *t;
+	sprintf(chpt, "/%s", name);
+	if (lstat(path,buf)) {
+		fprintf(stderr,"stat(%s): %s\n", path, strerror(errno));
+		return -1;
+	}
+	switch ( buf->st_mode & S_IFMT ) {
+		case S_IFSOCK:
+		case S_IFIFO:	t = "|"; break;
+
+		default:
+		case S_IFREG:
+		case S_IFBLK:
+		case S_IFCHR:
+						t = "";  break;
+		case S_IFDIR:
+						t = "/"; break;
+		case S_IFLNK:
+						t = "@"; break;
+	}
+
+	printf("%10li, %10" PRIdoff_t "b, %5i.%-5i 0%04" PRIomode_t " %s%s\n",
+				buf->st_ino,
+				buf->st_size,
+				buf->st_uid,
+				buf->st_gid,
+				buf->st_mode & ~S_IFMT,
+				name,
+				t);
+	*chpt = 0;
+	return 0;
+}
+
+int
+ls(char *dir, char *opts)
+{
+struct dirent	*de;
+char			path[PATH_MAX+1];
+char			*chpt;
+DIR				*dp  = 0;
+int				rval = -1;
+struct stat		buf;
+
+	if ( !dir )
+		dir = ".";
+
+	strncpy(path, dir, PATH_MAX);
+	path[PATH_MAX] = 0;
+	chpt = path+strlen(path);
+
+	if ( !(dp=opendir(dir)) ) {
+		perror("opendir");
+		goto cleanup;
+	}
+
+	while ( (de = readdir(dp)) ) {
+		ls_r(path, chpt, de->d_name, &buf);
+	}
+
+	rval = 0;
+
+cleanup:
+	if (dp)
+		closedir(dp);
+	return rval;
+}
+#endif
+
+#if 0
+		fprintf(stderr, "usage: cp(""from"",[""to""[,""-f""]]\n");
+		fprintf(stderr, "          ""to""==NULL -> stdout\n");
+		fprintf(stderr, "          ""-f""       -> overwrite existing file\n");
+#endif
+
+int
+cp(char *from, char *to, char *opts)
+{
+struct stat	st;
+int			rval  = -1;
+int			fd    = -1;
+FILE		*fst  = 0;
+FILE		*tst  = 0;
+int			flags = O_CREAT | O_WRONLY | O_TRUNC | O_EXCL;
+
+	if (from) {
+
+	if ((fd=open(from,O_RDONLY,0)) < 0) {
+		fprintf(stderr,
+				"Opening %s for reading: %s\n",
+				from,
+				strerror(errno));
+		goto cleanup;
+	}
+
+	if (fstat(fd, &st)) {
+		fprintf(stderr,
+				"rstat(%s): %s\n",
+				from,
+				strerror(errno));
+		goto cleanup;
+	}
+
+
+	if (!S_ISREG(st.st_mode)) {
+		fprintf(stderr,"Refuse to copy a non-regular file\n");
+		errno = EINVAL;
+		goto cleanup;
+	}
+	/* Now create a stream -- I experienced occasional weirdness
+	 * when circumventing the streams attached to fildno(stdin)
+	 * by reading/writing to the underlying fd's directly ->
+	 * for now we always go through buffered I/O...
+	 */
+	if ( !(fst=fdopen(fd,"r")) ) {
+		fprintf(stderr,
+				"Opening input stream [fdopen()] failed: %s\n",
+				strerror(errno));
+		goto cleanup;
+	}
+	/* at this point, we have a stream and don't need 'fd' anymore */
+	fd = -1;
+
+	} else {
+		fst			= stdin;
+		st.st_mode	= 0644;
+	}
+
+	if (opts && strchr(opts,'f'))
+		flags &= ~ O_EXCL;
+
+	if (to) {
+		if ( (fd=open(to,flags,st.st_mode)) < 0 ) {
+			fprintf(stderr,
+					"Opening %s for writing: %s\n",
+					to,
+					strerror(errno));
+			goto cleanup;
+		}
+		if ( !(tst=fdopen(fd, "w")) ) {
+			fprintf(stderr,
+					"Opening output stream [fdopen()] failed: %s\n",
+					strerror(errno));
+			goto cleanup;
+		}
+		/* at this point we have a stream and don't need 'fd' anymore */
+		fd = -1;
+	} else {
+		tst = stdout;
+	}
+
+	/* clear old errors */
+	clearerr(fst);
+	clearerr(tst);
+
+	/* use macro versions on register vars; stdio is already buffered,
+	 * there's nothing to be gained by reading/writing blocks into
+	 * a secondary buffer...
+	 */
+	{
+	register int ch;
+	register FILE *f = fst;
+	register FILE *t = tst;
+		while ( EOF != (ch = getc(f)) && EOF != putc(ch, t) )
+			/* nothing else */;
+	}
+
+	if ( ferror(fst) ) {
+		fprintf(stderr,"Read error: %s\n",strerror(errno));
+		goto cleanup;
+	}
+	if ( ferror(tst) ) {
+		fprintf(stderr,"Write error: %s\n",strerror(errno));
+		goto cleanup;
+	}
+
+	rval = 0;
+
+cleanup:
+
+	if ( fd >= 0 )
+		close(fd);
+
+	if ( fst ) {
+		if ( from )
+			fclose(fst);
+		else
+			clearerr(fst);
+	}
+	if ( tst ) {
+		if ( to )
+			fclose(tst);
+		else {
+			/* flush stdout */
+			fflush(tst);
+			clearerr(tst);
+		}
+	}
+
+	return rval;
+}
+
+int
+ln(char *to, char *name, char *opts)
+{
+	if (!to) {
+		fprintf(stderr,"ln: need 'to' argument\n");
+		return -1;
+	}
+	if (!name) {
+		if ( !(name = strrchr(to,'/')) ) {
+			fprintf(stderr,
+					"ln: 'unable to link %s to %s\n",
+					to,to);
+			return -1;
+		}
+		name++;
+	}
+	if (opts || strchr(opts,'s')) {
+		if (symlink(name,to)) {
+			fprintf(stderr,"symlink: %s\n",strerror(errno));
+			return -1;
+		}
+	} else {
+		if (link(name,to)) {
+			fprintf(stderr,"hardlink: %s\n",strerror(errno));
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int
+rm(char *path)
+{
+	return unlink(path);
+}
+
+int
+cd(char *path)
+{
+	return chdir(path);
+}
+
+#ifdef HAVE_CEXP
+static CexpHelpTabRec _cexpHelpTabDirutils[] __attribute__((unused)) = {
+	HELP(
+"copy a file: cp(""from"",[""to""[,""-f""]])\n\
+                 from = NULL <-- stdin\n\
+                 to   = NULL --> stdout\n\
+                 option -f: overwrite existing file\n",
+		int,
+		cp, (char *from, char *to, char *options)
+		),
+	HELP(
+"list a directory: ls([""dir""])\n",
+		int,
+		ls, (char *dir)
+		),
+	HELP(
+"remove a file\n",
+		int,
+		rm, (char *path)
+		),
+	HELP(
+"change the working directory\n",
+		int,
+		cd, (char *path)
+		),
+	HELP(
+"create a link: ln(""to"",""name"",""[-s]""\n\
+                   -s creates a symlink\n",
+		int,
+		ln, (char *to, char *name, char *options)
+		),
+	HELP("",,0,)
+};
+#endif
diff --git a/services/nfsclient/include/librtemsNfs.h b/services/nfsclient/include/librtemsNfs.h
new file mode 100644
index 0000000..fc48028
--- /dev/null
+++ b/services/nfsclient/include/librtemsNfs.h
@@ -0,0 +1,179 @@
+#ifndef LIB_RTEMS_NFS_CLIENT_H
+#define LIB_RTEMS_NFS_CLIENT_H
+
+/* public interface to the NFS client library for RTEMS */
+
+/* Author: Till Straumann <strauman at slac.stanford.edu> 2002-2003 */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman at 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 <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;
+
+/* Initialize the driver.
+ *
+ * Note, called in nfsfs initialise when mount is called.
+ *
+ * RETURNS: 0 on success, -1 on failure
+ */
+int
+rpcUdpInit(void);
+
+/* Cleanup/Stop
+ *
+ * RETURNS: 0 on success, nonzero if still in use
+ */
+int
+rpcUdpCleanup(void);
+
+/* NFS driver interface */
+
+/* Initialize the NFS driver.
+ *
+ * NOTE: 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.
+ */
+void
+nfsInit(int smallPoolDepth, int bigPoolDepth);
+
+/* Driver cleanup code
+ *
+ * RETURNS: 0 on success, nonzero if still in use
+ */
+int
+nfsCleanup(void);
+
+/* Dump a list of the currently mounted NFS to a file
+ * (stdout is used in case f==NULL)
+ */
+int
+nfsMountsShow(FILE *f);
+
+/*
+ * 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);
+
+/* A utility routine to find the path leading to a
+ * rtems_filesystem_location_info_t node.
+ *
+ * This should really be present in libcsupport...
+ *
+ * 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);
+
+/* Set the timeout (initial default: 10s) for NFS and mount calls.
+ *
+ * RETURNS 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
diff --git a/services/nfsclient/include/rpcio.h b/services/nfsclient/include/rpcio.h
new file mode 100644
index 0000000..8078209
--- /dev/null
+++ b/services/nfsclient/include/rpcio.h
@@ -0,0 +1,208 @@
+#ifndef RPCIO_H
+#define RPCIO_H
+
+/* A multihreaded RPC/UDP multiplexor */
+
+/* Author: Till Straumann, <strauman at slac.stanford.edu>, 2002 */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman at 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 __rtems
+#include <rtems.h>
+#endif
+
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <stdarg.h>
+
+typedef struct RpcUdpServerRec_ 	*RpcUdpServer;
+typedef struct RpcUdpXactRec_		*RpcUdpXact;
+
+typedef RpcUdpXact					RpcUdpClnt;
+
+#define RPCIOD_DEFAULT_ID	0xdef10000
+
+int
+rpcUdpInit(void);
+
+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);
+
+/* Dump statistics to a file (stdout if NULL);
+ * returns 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);
+
+/* 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 */
+	);
+
+/* 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		*/
+);
+
+
+/* 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
diff --git a/services/nfsclient/nfs.c b/services/nfsclient/nfs.c
new file mode 100644
index 0000000..bbec6b8
--- /dev/null
+++ b/services/nfsclient/nfs.c
@@ -0,0 +1,3051 @@
+/* NFS client implementation for RTEMS; hooks into the RTEMS filesystem */
+
+/* Author: Till Straumann <strauman at 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 at 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 <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 at 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)
+
+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;
+} nfsGlob = {0, 0,  0, 0, 0, 0};
+
+/*
+ * 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;
+
+/* 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
+ */
+static RpcUdpXactPool smallPool = 0;
+static RpcUdpXactPool bigPool   = 0;
+
+
+/*****************************************
+	Implementation
+ *****************************************/
+
+/* 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));
+unsigned long flags;
+
+#if DEBUG & DEBUG_TRACK_NODES
+	fprintf(stderr,"NFS: creating a node\n");
+#endif
+
+	if (rval) {
+		if (fh)
+			memcpy( &SERP_FILE(rval), fh, sizeof(*fh) );
+		rtems_interrupt_disable(flags);
+			nfs->nodesInUse++;
+		rtems_interrupt_enable(flags);
+		rval->nfs       = nfs;
+		rval->str		= 0;
+	} else {
+		errno = ENOMEM;
+	}
+
+	return rval;
+}
+
+/* destroy a node */
+static void
+nfsNodeDestroy(NfsNode node)
+{
+unsigned long flags;
+
+#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
+
+	rtems_interrupt_disable(flags);
+		node->nfs->nodesInUse--;
+#if DEBUG & DEBUG_COUNT_NODES
+		if (node->str)
+			node->nfs->stringsInUse--;
+#endif
+	rtems_interrupt_enable(flags);
+
+	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
+			{ unsigned long flags;
+			rtems_interrupt_disable(flags);
+				node->nfs->stringsInUse++;
+			rtems_interrupt_enable(flags);
+			}
+#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).
+ */
+void
+nfsInit(int smallPoolDepth, int bigPoolDepth)
+{
+static int initialised = 0;
+entry	dummy;
+rtems_status_code status;
+
+	if (initialised)
+		return;
+
+	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));
+		return;
+	}
+
+	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);
+
+	smallPool = rpcUdpXactPoolCreate(
+		NFS_PROGRAM,
+		NFS_VERSION_2,
+		CONFIG_NFS_SMALL_XACT_SIZE,
+		smallPoolDepth);
+	assert( smallPool );
+
+	bigPool = rpcUdpXactPoolCreate(
+		NFS_PROGRAM,
+		NFS_VERSION_2,
+		CONFIG_NFS_BIG_XACT_SIZE,
+		bigPoolDepth);
+	assert( bigPool );
+
+	status = rtems_semaphore_create(
+		rtems_build_name('N','F','S','l'),
+		1,
+		MUTEX_ATTRIBUTES,
+		0,
+		&nfsGlob.llock);
+	assert( status == RTEMS_SUCCESSFUL );
+	status = rtems_semaphore_create(
+		rtems_build_name('N','F','S','m'),
+		1,
+		MUTEX_ATTRIBUTES,
+		0,
+		&nfsGlob.lock);
+	assert( status == RTEMS_SUCCESSFUL );
+
+	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");
+
+	}
+}
+
+/* Driver cleanup code
+ */
+int
+nfsCleanup(void)
+{
+rtems_id	l;
+int			refuse;
+
+	if (!nfsGlob.llock) {
+		/* registering the driver failed - let them still cleanup */
+		return 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;
+	}
+
+	rtems_semaphore_delete(nfsGlob.lock);
+	nfsGlob.lock = 0;
+
+	/* hold the lock while cleaning up... */
+
+	rpcUdpXactPoolDestroy(smallPool);
+	rpcUdpXactPoolDestroy(bigPool);
+	l = nfsGlob.llock;
+	rtems_io_unregister_driver(nfsGlob.nfs_major);
+
+	rtems_semaphore_delete(l);
+	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 = bigPool;		break;
+		default:	pool = 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)
+{
+
+	if (force
+#ifdef CONFIG_ATTR_LIFETIME
+		|| (nowSeconds() - node->age > CONFIG_ATTR_LIFETIME)
+#endif
+		) {
+		if ( nfscall(node->nfs->server,
+					  NFSPROC_GETATTR,
+					  (xdrproc_t)xdr_nfs_fh,	&SERP_FILE(node),
+					  (xdrproc_t)xdr_attrstat, &node->serporid) )
+		return -1;
+
+		if ( NFS_OK != node->serporid.status ) {
+			errno = node->serporid.status;
+			return -1;
+		}
+
+		node->age = nowSeconds();
+	}
+
+	return 0;
+}
+
+/*
+ * 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)
+{
+	int rv = 0;
+
+	if (dst->str != NULL) {
+#if DEBUG & DEBUG_COUNT_NODES
+		rtems_interrupt_level flags;
+		rtems_interrupt_disable(flags);
+			dst->nfs->stringsInUse--;
+		rtems_interrupt_enable(flags);
+#endif
+		free(dst->str);
+	}
+
+	*dst = *src;
+	dst->str = NULL;
+
+	if (src->args.name != NULL) {
+		dst->str = dst->args.name = strdup(src->args.name);
+		if (dst->str != NULL) {
+#if DEBUG & DEBUG_COUNT_NODES
+			rtems_interrupt_level flags;
+			rtems_interrupt_disable(flags);
+				dst->nfs->stringsInUse++;
+			rtems_interrupt_enable(flags);
+#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);
+			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;
+
+	if ( nfscall(tNode->nfs->server,
+					  NFSPROC_LINK,
+					  (xdrproc_t)xdr_linkargs,	&SERP_FILE(tNode),
+					  (xdrproc_t)xdr_nfsstat,	&status)
+	     || (NFS_OK != (errno = status))
+	   ) {
+#if DEBUG & DEBUG_SYSCALLS
+		perror("nfs_link");
+#endif
+		rv = -1;
+	}
+
+	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
+)
+{
+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
+
+	if ( nfscall(nfs->server,
+				 proc,
+				 (xdrproc_t)xdr_diropargs,	&node->args,
+				 (xdrproc_t)xdr_nfsstat,	&status)
+	     || (NFS_OK != (errno = status))
+	    ) {
+#if DEBUG & DEBUG_SYSCALLS
+		perror(name);
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+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;
+  }
+  
+	nfsInit(0, 0);
+
+#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);
+}
+
+/* OPTIONAL; may be NULL - BUT: CAUTION; mount() doesn't check
+ * for this handler to be present - a fs bug
+ * //NOTE: (10/25/2002) patch submitted and probably applied
+ */
+static rtems_filesystem_node_types_t nfs_node_type(
+  const rtems_filesystem_location_info_t *loc
+)
+{
+NfsNode node = loc->node_access;
+
+	if (updateAttr(node, 0 /* only if old */))
+		return -1;
+
+	switch( SERP_ATTR(node).type ) {
+		default:
+			/* rtems has no value for 'unknown';
+			 */
+		case NFNON:
+		case NFSOCK:
+		case NFBAD:
+		case NFFIFO:
+				break;
+
+
+		case NFREG: return RTEMS_FILESYSTEM_MEMORY_FILE;
+		case NFDIR:	return RTEMS_FILESYSTEM_DIRECTORY;
+
+		case NFBLK:
+		case NFCHR:	return RTEMS_FILESYSTEM_DEVICE;
+
+		case NFLNK: return RTEMS_FILESYSTEM_SYM_LINK;
+	}
+	return -1;
+}
+
+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;
+
+	if ( nfscall( nfs->server,
+						(type == S_IFDIR) ? NFSPROC_MKDIR : NFSPROC_CREATE,
+						(xdrproc_t)xdr_createargs,	&SERP_FILE(node),
+						(xdrproc_t)xdr_diropres,	&res)
+		|| (NFS_OK != (errno = res.status)) ) {
+#if DEBUG & DEBUG_SYSCALLS
+		perror("nfs_mknod");
+#endif
+                rv = -1;
+	}
+
+	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;
+
+	if ( nfscall( nfs->server,
+						NFSPROC_SYMLINK,
+						(xdrproc_t)xdr_symlinkargs,	&SERP_FILE(node),
+						(xdrproc_t)xdr_nfsstat,		&status)
+		|| (NFS_OK != (errno = status)) ) {
+#if DEBUG & DEBUG_SYSCALLS
+		perror("nfs_symlink");
+#endif
+                rv = -1;
+	}
+
+	free(dupname);
+
+	return rv;
+}
+
+static ssize_t nfs_readlink_with_node(
+	NfsNode node,
+	char *buf,
+	size_t len
+)
+{
+	Nfs nfs = node->nfs;
+	readlinkres_strbuf rr;
+
+	rr.strbuf.buf = buf;
+	rr.strbuf.max = len - 1;
+
+	if ( nfscall(nfs->server,
+							NFSPROC_READLINK,
+							(xdrproc_t)xdr_nfs_fh,      		&SERP_FILE(node),
+							(xdrproc_t)xdr_readlinkres_strbuf, &rr)
+		|| (NFS_OK != (errno = rr.status)) ) {
+#if DEBUG & DEBUG_SYSCALLS
+		perror("nfs_readlink_with_node");
+#endif
+		return -1;
+	}
+
+	return (ssize_t) strlen(rr.strbuf.buf);
+}
+
+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 && (errno = status) != NFS_OK) {
+			rv = -1;
+		}
+
+		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,
+	.node_type_h    = nfs_node_type,
+	.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,
+	.fsmount_me_h   = rtems_nfs_initialize,
+	.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
+)
+{
+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;
+
+	if ( nfscall(	nfs->server,
+						NFSPROC_READ,
+						(xdrproc_t)xdr_readargs,	&SERP_FILE(node),
+						(xdrproc_t)xdr_readres,	&rr) ) {
+		return -1;
+	}
+
+
+	if (NFS_OK != rr.status) {
+		rtems_set_errno_and_return_minus_one(rr.status);
+	}
+
+#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 rr.readres_u.reply.data.data_len;
+}
+
+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;
+
+	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
+)
+{
+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
+
+	if ( nfscall(
+					server,
+					NFSPROC_READDIR,
+					(xdrproc_t)xdr_readdirargs, &di->readdirargs,
+					(xdrproc_t)xdr_dir_info,    di) ) {
+		return -1;
+	}
+
+
+	if (NFS_OK != di->status) {
+		rtems_set_errno_and_return_minus_one(di->status);
+	}
+
+	return (char*)di->ptr - (char*)buffer;
+}
+
+static ssize_t nfs_file_write(
+	rtems_libio_t *iop,
+	const void    *buffer,
+	size_t        count
+)
+{
+NfsNode 	node = iop->pathinfo.node_access;
+Nfs			nfs  = node->nfs;
+int			e;
+
+	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;
+		}
+		SERP_ARGS(node).writearg.offset	  	   = SERP_ATTR(node).size;
+	} else {
+		SERP_ARGS(node).writearg.offset	  	   = iop->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
+	 */
+
+	if ( nfscall(	nfs->server,
+						NFSPROC_WRITE,
+						(xdrproc_t)xdr_writeargs,	&SERP_FILE(node),
+						(xdrproc_t)xdr_attrstat,	&node->serporid) ) {
+		return -1;
+	}
+
+
+	if (NFS_OK != (e=node->serporid.status) ) {
+		/* try at least to recover the current attributes */
+		updateAttr(node, 1 /* force */);
+		rtems_set_errno_and_return_minus_one(e);
+	}
+
+	node->age = nowSeconds();
+
+	iop->offset += count;
+
+	return count;
+}
+
+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)
+{
+
+struct timeval				now;
+nfstime					nfsnow, t;
+int						e;
+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;
+
+	if ( nfscall( node->nfs->server,
+						NFSPROC_SETATTR,
+						(xdrproc_t)xdr_sattrargs,	&SERP_FILE(node),
+						(xdrproc_t)xdr_attrstat,	&node->serporid) ) {
+#if DEBUG & DEBUG_SYSCALLS
+		fprintf(stderr,
+				"nfs_sattr (mask 0x%08x): %s",
+				mask,
+				strerror(errno));
+#endif
+		return -1;
+	}
+
+	if (NFS_OK != (e=node->serporid.status) ) {
+#if DEBUG & DEBUG_SYSCALLS
+		fprintf(stderr,"nfs_sattr: %s\n",strerror(e));
+#endif
+		/* try at least to recover the current attributes */
+		updateAttr(node, 1 /* force */);
+		rtems_set_errno_and_return_minus_one(e);
+	}
+
+	node->age = nowSeconds();
+
+	return 0;
+}
+
+/* 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;
+
+	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
+};
+
+/* 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
+};
+
+/* 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
+};
+
+/* 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_level k;
+uint32_t	          s,us;
+
+	if ( timeout_ms > 100000 ) {
+		/* out of range */
+		return -1;
+	}
+
+	s  = timeout_ms/1000;
+	us = (timeout_ms % 1000) * 1000;
+
+	rtems_interrupt_disable(k);
+	_nfscalltimeout.tv_sec  = s;
+	_nfscalltimeout.tv_usec = us;
+	rtems_interrupt_enable(k);
+
+	return 0;
+}
+
+uint32_t
+nfsGetTimeout( void )
+{
+rtems_interrupt_level k;
+uint32_t              s,us;
+	rtems_interrupt_disable(k);
+	s  = _nfscalltimeout.tv_sec;
+	us = _nfscalltimeout.tv_usec;
+	rtems_interrupt_enable(k);
+	return s*1000 + us/1000;
+}
diff --git a/services/nfsclient/nfs.modini.c b/services/nfsclient/nfs.modini.c
new file mode 100644
index 0000000..22095cf
--- /dev/null
+++ b/services/nfsclient/nfs.modini.c
@@ -0,0 +1,31 @@
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "librtemsNfs.h"
+
+/* CEXP dynamic loader support */
+
+void
+_cexpModuleInitialize(void *mod)
+{
+#if defined(DEBUG)
+	/* print load address (in case we crash while initializing) */
+unsigned lr;
+	__asm__ __volatile__(
+			"	    bl     thisis_loaded_at \n"
+			"thisis_loaded_at:              \n"
+			"       mflr   %0               \n"
+			: "=r"(lr) ::"lr");
+	printf("thisis_loaded_at: 0x%08x\n",lr);
+#endif
+	nfsInit(0,0);
+}
+
+int
+_cexpModuleFinalize(void *mod)
+{
+	return nfsCleanup();
+}
+
+
diff --git a/services/nfsclient/nfsTest.c b/services/nfsclient/nfsTest.c
new file mode 100644
index 0000000..18bd0a3
--- /dev/null
+++ b/services/nfsclient/nfsTest.c
@@ -0,0 +1,379 @@
+/* Test program for evaluating NFS read throughput */
+
+/* Author: Till Straumann <strauman at slac.stanford.edu>, 2006 */
+
+/* This test code allows for evaluating NFS read performance
+ * under various scenarios:
+ *  - synchronous reads with various buffer sizes (select
+ *    'num_readers' == 0, see below).
+ *  - pseudo 'read-ahead' using multiple threads that issue
+ *    NFS reads from the same file (but from different offsets)
+ *    in parallel.
+ *    Rationale: each NFS read request is synchronous, i.e., the
+ *    caller sends a request to the server and waits for the
+ *    reply to come back. Performance enhancement can be expected
+ *    by requesting multiple blocks in parallel rather than
+ *    sequentially.
+ *
+ * rtems_interval
+ * nfsTestRead(char *file_name, int chunk_size, int num_readers);
+ *
+ * 1) creates 'num_readers' threads, each opening 'file_name' for
+ *    reading on a separate file descriptor.
+ * 2) creates message queues for communicating with reader threads
+ *
+ * 3) read file using nfsTestReadBigbuf() until EOF is reached
+ *
+ * 4) releases resources.
+ *
+ * RETURNS: Time elapsed during step 3 in ms. This is measured
+ *          using the system clock so make sure the test file
+ *          is big enough.
+ *
+ * nfsTestReadBigbuf() synchronously reads a block of
+ * 'num_readers * chunk_size' (which may be bigger than
+ * the UDP limit of 8k) using 'num_reader' threads to
+ * retrieve the various pieces of the big block in parallel.
+ * This speeds up things since several RPC calls can
+ * be in the works at once.
+ *
+ * NOTES:
+ *  - if 'num_readers' == 0 this corresponds to an 'ordinary'
+ *    NFS read. 'num_readers' == 1 schedules a single reader
+ *    thread (== ordinary NFS read + message passing overhead).
+ *  - no actual processing on the data is done; they are simply
+ *    thrown away. A real, performance-critical application could
+ *    pipeline 'reader' and 'cruncher' threads.
+ *  - read is not completely asynchronous; synchronization is still
+ *    performed at 'big block' boundaries (num_readers * chunk_size).
+ */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman at 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 <rtems.h>
+#include <rtems/error.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+unsigned nfsTestReaderPri = 80;
+
+struct nfsTestReq_ {
+	unsigned offset;	/* file offset */
+	int      size;		/* IN: block size to read (must be < 8192), OUT: bytes actually read */
+	void	 *buf;		/* data buffer address */
+};
+
+/* Queue for sending requests to parallel reader tasks */
+rtems_id nfsTestRQ = 0;
+/* Queue to pickup replies from parallel reader tasks  */
+rtems_id nfsTestAQ = 0;
+
+
+/* Reader task; opens its own file descriptor
+ * and works on requests:
+ *    - obtain request from request queue.
+ *    - lseek to the requested file offset
+ *    - NFS read into buffer
+ *    - queue reply.
+ *
+ * Note that this implementation is very simple
+ *  - no full error checking.
+ *  - file is opened/closed by thread
+ * it's main purpose is running quick tests.
+ */
+static rtems_task
+nfsTestReader(rtems_task_argument arg)
+{
+int                fd = open((char*)arg,O_RDONLY);
+unsigned long      s;
+struct nfsTestReq_ r;
+rtems_status_code  sc;
+
+	if ( fd < 0 ) {
+		perror("nfsReader: opening file");
+		goto cleanup;
+	}
+	do {
+		s  = sizeof(r);
+		sc = rtems_message_queue_receive(nfsTestRQ, &r, &s, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+		if ( RTEMS_SUCCESSFUL != sc ) {
+			rtems_error(sc, "(Error) reading from message queue");
+			goto cleanup;
+		}
+		if ( !r.buf ) {
+			/* They send a NULL buffer as a shutdown request */
+			break;
+		}
+#ifdef DEBUG
+		printf("Reader: reading offset %u, size %i to %p ... ",
+			r.offset, r.size, r.buf);
+#endif
+		/* seek to requested offset */
+		lseek(fd, r.offset, SEEK_SET);
+		r.size = read(fd, r.buf, r.size);
+#ifdef DEBUG
+		printf("got %i\n",r.size);
+#endif
+		rtems_message_queue_send(nfsTestAQ, &r, sizeof(r));
+	} while (1) ;
+
+cleanup:
+	if ( fd >= 0 )
+		close(fd);
+	rtems_task_delete(RTEMS_SELF);
+}
+
+
+/* helper to create and start a reader task */
+static rtems_id
+taskSpawn(char *filenm, int inst)
+{
+rtems_status_code sc;
+rtems_id          tid;
+
+	sc = rtems_task_create(
+			rtems_build_name('n','t','t','0'+inst),
+			nfsTestReaderPri,
+			1400,
+			RTEMS_DEFAULT_MODES,
+			RTEMS_DEFAULT_ATTRIBUTES,
+			&tid);
+	if ( RTEMS_SUCCESSFUL != sc ) {
+		rtems_error(sc,"(Error) Creating nfs reader task %i",inst);
+		return 0;
+	}
+
+	sc = rtems_task_start(tid, nfsTestReader, (rtems_task_argument)filenm);
+	if ( RTEMS_SUCCESSFUL != sc ) {
+		rtems_error(sc,"(Error) Staritng nfs reader task %i",inst);
+		rtems_task_delete(tid);
+		return 0;
+	}
+
+	return tid;
+}
+
+/*
+ * Read nrd*sz bytes into 'buf' from file offset 'off'
+ * using 'nrd' parallel reader tasks to do the job.
+ * This helper routine schedules 'nrd' requests to
+ * the reader tasks and waits for all requests to
+ * finish.
+ *
+ * RETURNS: number of bytes read or -1 (error).
+ *
+ * CAVEATS:
+ *	- assumes read requests always return 'sz' bytes
+ *    unless the end of file is reached.
+ *    THIS ASSUMPTION SHOULD NOT BE MADE WHEN WRITING
+ *    ANY 'REAL' CODE.
+ */
+static int
+nfsTestReadBigbuf(char *buf, int off, int sz, int nrd)
+{
+int i,rval=0;
+struct nfsTestReq_ r;
+	r.buf    = buf;
+	r.size   = sz;
+	r.offset = off;
+	/* send out parallel requests */
+	for (i=0; i<nrd; i++) {
+		rtems_message_queue_send(nfsTestRQ, &r, sizeof(r));
+		r.offset += sz;
+		r.buf    += sz;
+	}
+	/* wait for answers */
+	for (i=0; i<nrd; i++) {
+		unsigned long s = sizeof(r);
+		rtems_message_queue_receive(nfsTestAQ, &r, &s, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+		if ( r.size < 0 ) {
+			fprintf(stderr,"A reader failed\n");
+			rval = -1;
+		} else {
+			/* FIXME sanity checks:
+			 *   - catch case where any-but-last read returns < sz
+			 */
+			if ( rval >= 0 ) {
+				rval += r.size;
+			}
+		}
+	}
+	return rval;
+}
+
+/* Main test routine
+ *
+ * Read file 'fname' usint 'nrd' parallel reader tasks,
+ * each operating on chunks of 'sz' bytes.
+ *
+ * RETURNS: time elapsed in milliseconds. This is measured
+ *          using the system clock. Hence, for the result
+ *          to be meaningful, the file must be big enough.
+ *
+ */
+rtems_interval
+nfsTestRead(char *fnam, int sz, int nrd)
+{
+int               i;
+unsigned          off;
+rtems_interval    now=-1, then, tickspsec;
+rtems_status_code sc;
+int		          fd=-1;
+char	          *buf=0;
+
+	if ( nrd < 0 )
+		nrd = 0;
+
+	if ( sz < 0 || sz > 8192 ) {
+		fprintf(stderr,"\n");
+		return -1;
+	}
+
+	nfsTestRQ = nfsTestAQ = 0;
+
+	/* Allocate buffer */
+	if ( ! (buf=malloc(sz*(nrd ? nrd : 1))) ) {
+		perror("allocating buffer");
+		goto cleanup;
+	}
+
+	/* Don't bother proceeding if we can't open the file for reading */
+	if ( (fd=open(fnam,O_RDONLY)) < 0 ) {
+		perror("opening file");
+		goto cleanup;
+	}
+	if ( nrd ) {
+		close(fd); fd = -1;
+	}
+
+	/* Create request queue */
+	if ( nrd ) {
+		sc = rtems_message_queue_create(
+				rtems_build_name('n','t','r','q'),
+				nrd,
+				sizeof(struct nfsTestReq_),
+				RTEMS_DEFAULT_ATTRIBUTES,
+				& nfsTestRQ );
+
+		if ( RTEMS_SUCCESSFUL != sc ) {
+			rtems_error(sc, "(Error) creating request queue");
+			nfsTestRQ = 0;
+			goto cleanup;
+		}
+
+		/* Spawn reader tasks */
+		for ( i=0; i<nrd; i++ ) {
+			if ( ! taskSpawn(fnam, i) )
+				goto cleanup;
+		}
+
+		/* Create reply queue */
+		sc = rtems_message_queue_create(
+				rtems_build_name('n','t','a','q'),
+				nrd,
+				sizeof(struct nfsTestReq_),
+				RTEMS_DEFAULT_ATTRIBUTES,
+				& nfsTestAQ );
+
+		if ( RTEMS_SUCCESSFUL != sc ) {
+			rtems_error(sc, "(Error) creating reply queue");
+			nfsTestAQ = 0;
+			goto cleanup;
+		}
+	}
+
+	/* Timed main loop */
+        then = rtems_clock_get_ticks_since_boot();
+
+	if ( nrd ) {
+		off = 0;
+		while  ((i = nfsTestReadBigbuf(buf, off, sz, nrd)) > 0 ) {
+#ifdef DEBUG
+			printf("bigbuf got %i\n", i);
+#endif
+			off += i;
+		}
+	} else {
+		while ( (i = read(fd, buf, sz)) > 0 )
+			/* nothing else to do */;
+		if ( i < 0 ) {
+			perror("reading");
+			goto cleanup;
+		}
+	}
+
+        now = rtems_clock_get_ticks_since_boot();
+	now = (now-then)*1000;
+	ticksspec = rtems_clock_get_ticks_per_second();
+	now /= tickspsec; /* time in ms */
+
+cleanup:
+	if ( fd >= 0 )
+		close(fd);
+
+	if ( nfsTestRQ ) {
+		/* request tasks to shutdown by sending NULL buf request  */
+		struct nfsTestReq_ r;
+		r.buf = 0;
+		for ( i=0; i<nrd; i++ ) {
+			rtems_message_queue_send( nfsTestRQ, &r, sizeof(r) );
+		}
+		/* cheat: instead of proper synchronization with shutdown we simply
+		 * delay for a second...
+		 */
+		rtems_task_wake_after( tickspsec );
+		rtems_message_queue_delete( nfsTestRQ );
+	}
+	if ( nfsTestAQ )
+		rtems_message_queue_delete( nfsTestAQ );
+	free(buf);
+	return now;
+}
diff --git a/services/nfsclient/proto/mount_prot.h b/services/nfsclient/proto/mount_prot.h
new file mode 100644
index 0000000..1cde517
--- /dev/null
+++ b/services/nfsclient/proto/mount_prot.h
@@ -0,0 +1,144 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _MOUNT_PROT_H_RPCGEN
+#define _MOUNT_PROT_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+
+#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 */
diff --git a/services/nfsclient/proto/mount_prot.x b/services/nfsclient/proto/mount_prot.x
new file mode 100644
index 0000000..7e0d7f3
--- /dev/null
+++ b/services/nfsclient/proto/mount_prot.x
@@ -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;
diff --git a/services/nfsclient/proto/mount_prot_xdr.c b/services/nfsclient/proto/mount_prot_xdr.c
new file mode 100644
index 0000000..b439ef3
--- /dev/null
+++ b/services/nfsclient/proto/mount_prot_xdr.c
@@ -0,0 +1,104 @@
+/*
+ * 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;
+}
diff --git a/services/nfsclient/proto/nfs_prot.h b/services/nfsclient/proto/nfs_prot.h
new file mode 100644
index 0000000..de812db
--- /dev/null
+++ b/services/nfsclient/proto/nfs_prot.h
@@ -0,0 +1,453 @@
+/*
+ * 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>
+
+
+#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,
+};
+typedef enum nfsstat nfsstat;
+
+enum ftype {
+	NFNON = 0,
+	NFREG = 1,
+	NFDIR = 2,
+	NFBLK = 3,
+	NFCHR = 4,
+	NFLNK = 5,
+	NFSOCK = 6,
+	NFBAD = 7,
+	NFFIFO = 8,
+};
+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 */
diff --git a/services/nfsclient/proto/nfs_prot.x b/services/nfsclient/proto/nfs_prot.x
new file mode 100644
index 0000000..a40d9a5
--- /dev/null
+++ b/services/nfsclient/proto/nfs_prot.x
@@ -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
+
diff --git a/services/nfsclient/proto/nfs_prot_xdr.c b/services/nfsclient/proto/nfs_prot_xdr.c
new file mode 100644
index 0000000..cde005e
--- /dev/null
+++ b/services/nfsclient/proto/nfs_prot_xdr.c
@@ -0,0 +1,621 @@
+/*
+ * 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;
+}
diff --git a/services/nfsclient/rfc1094.txt b/services/nfsclient/rfc1094.txt
new file mode 100644
index 0000000..7ad0f73
--- /dev/null
+++ b/services/nfsclient/rfc1094.txt
@@ -0,0 +1,1258 @@
+
+  RFC 1094 (RFC1094)
+
+Internet RFC/STD/FYI/BCP Archives
+
+
+      RFC 1094 - NFS: Network File System Protocol specification
+
+------------------------------------------------------------------------
+
+
+Network Working Group                             Sun Microsystems, Inc.
+Request for Comments: 1094                                    March 1989
+
+            NFS: Network File System Protocol Specification
+
+STATUS OF THIS MEMO
+
+   This RFC describes a protocol that Sun Microsystems, Inc., and others
+   are using.  A new version of the protocol is under development, but
+   others may benefit from the descriptions of the current protocol, and
+   discussion of some of the design issues.  Distribution of this memo
+   is unlimited.
+
+1. INTRODUCTION
+
+   The Sun Network Filesystem (NFS) protocol provides transparent remote
+   access to shared files across networks.  The NFS protocol is designed
+   to be portable across different machines, operating systems, network
+   architectures, and transport protocols.  This portability is achieved
+   through the use of Remote Procedure Call (RPC) primitives built on
+   top of an eXternal Data Representation (XDR).  Implementations
+   already exist for a variety of machines, from personal computers to
+   supercomputers.
+
+   The supporting mount protocol allows the server to hand out remote
+   access privileges to a restricted set of clients.  It performs the
+   operating system-specific functions that allow, for example, to
+   attach remote directory trees to some local file system.
+
+1.1.  Remote Procedure Call
+
+   Sun's Remote Procedure Call specification provides a procedure-
+   oriented interface to remote services.  Each server supplies a
+   "program" that is a set of procedures.  NFS is one such program.  The
+   combination of host address, program number, and procedure number
+   specifies one remote procedure.  A goal of NFS was to not require any
+   specific level of reliability from its lower levels, so it could
+   potentially be used on many underlying transport protocols, or even
+   another remote procedure call implementation.  For ease of
+   discussion, the rest of this document will assume NFS is implemented
+   on top of Sun RPC, described in  RFC 1057 </rfcs/rfc1057.html>, "RPC: Remote Procedure
+   Call Protocol Specification".
+
+1.2.  External Data Representation
+
+   The eXternal Data Representation (XDR) standard provides a common way
+   of representing a set of data types over a network.  The NFS Protocol
+
+   Specification is written using the RPC data description language.
+   For more information, see RFC 1014 </rfcs/rfc1014.html>, "XDR: External Data
+   Representation Standard".  Although automated RPC/XDR compilers exist
+   to generate server and client "stubs", NFS does not require their
+   use.  Any software that provides equivalent functionality can be
+   used, and if the encoding is exactly the same it can interoperate
+   with other implementations of NFS.
+
+1.3.  Stateless Servers
+
+   The NFS protocol was intended to be as stateless as possible.  That
+   is, a server should not need to maintain any protocol state
+   information about any of its clients in order to function correctly.
+   Stateless servers have a distinct advantage over stateful servers in
+   the event of a failure.  With stateless servers, a client need only
+   retry a request until the server responds; it does not even need to
+   know that the server has crashed, or the network temporarily went
+   down.  The client of a stateful server, on the other hand, needs to
+   either detect a server failure and rebuild the server's state when it
+   comes back up, or cause client operations to fail.
+
+   This may not sound like an important issue, but it affects the
+   protocol in some unexpected ways.  We feel that it may be worth a bit
+   of extra complexity in the protocol to be able to write very simple
+   servers that do not require fancy crash recovery.  Note that even if
+   a so-called "reliable" transport protocol such as TCP is used, the
+   client must still be able to handle interruptions of service by re-
+   opening connections when they time out.  Thus, a stateless protocol
+   may actually simplify the  implementation.
+
+   On the other hand, NFS deals with objects such as files and
+   directories that inherently have state -- what good would a file be
+   if it did not keep its contents intact?  The goal was to not
+   introduce any extra state in the protocol itself.  Inherently
+   stateful operations such as file or record locking, and remote
+   execution,  were implemented as separate services, not described in
+   this document.
+
+   The basic way to simplify recovery was to make operations as
+   "idempotent" as possible (so that they can potentially be repeated).
+   Some operations in this version of the protocol did not attain this
+   goal; luckily most of the operations (such as Read and Write) are
+   idempotent.  Also, most server failures occur between operations, not
+   between the receipt of an operation and the response.  Finally,
+   although actual server failures may be rare, in complex networks,
+   failures of any network, router, or bridge may be indistinguishable
+   from a server failure.
+
+2. NFS PROTOCOL DEFINITION
+
+   Servers change over time, and so can the protocol that they use.  RPC
+   provides a version number with each RPC request.  This RFC describes
+   version two of the NFS protocol.  Even in the second version, there
+   are a few obsolete procedures and parameters, which will be removed
+   in later versions.  An RFC for version three of the NFS protocol is
+   currently under preparation.
+
+2.1.  File System Model
+
+   NFS assumes a file system that is hierarchical, with directories as
+   all but the bottom level of files.  Each entry in a directory (file,
+   directory, device, etc.) has a string name.  Different operating
+   systems may have restrictions on the depth of the tree or the names
+   used, as well as using different syntax to represent the "pathname",
+   which is the concatenation of all the "components" (directory and
+   file names) in the name.  A "file system" is a tree on a single
+   server (usually a single disk or physical partition) with a specified
+   "root".  Some operating systems provide a "mount" operation to make
+   all file systems appear as a single tree, while others maintain a
+   "forest" of file systems.  Files are unstructured streams of
+   uninterpreted bytes.  Version 3 of NFS uses slightly more general
+   file system model.
+
+   NFS looks up one component of a pathname at a time.  It may not be
+   obvious why it does not just take the whole pathname, traipse down
+   the directories, and return a file handle when it is done.  There are
+   several good reasons not to do this.  First, pathnames need
+   separators between the directory components, and different operating
+   systems use different separators.  We could define a Network Standard
+   Pathname Representation, but then every pathname would have to be
+   parsed and converted at each end.  Other issues are discussed in
+   section 3, NFS Implementation Issues.
+
+   Although files and directories are similar objects in many ways,
+   different procedures are used to read directories and files.  This
+   provides a network standard format for representing directories.  The
+   same argument as above could have been used to justify a procedure
+   that returns only one directory entry per call.  The problem is
+   efficiency.  Directories can contain many entries, and a remote call
+   to return each would be just too slow.
+
+2.2.  Server Procedures
+
+   The protocol definition is given as a set of procedures with
+   arguments and results defined using the RPC language (XDR language
+   extended with program, version, and procedure declarations).  A brief
+
+   description of the function of each procedure should provide enough
+   information to allow implementation.  Section 2.3 describes the basic
+   data types in more detail.
+
+   All of the procedures in the NFS protocol are assumed to be
+   synchronous.  When a procedure returns to the client, the client can
+   assume that the operation has completed and any data associated with
+   the request is now on stable storage.  For example, a client WRITE
+   request may cause the server to update data blocks, filesystem
+   information blocks (such as indirect blocks), and file attribute
+   information (size and modify times).  When the WRITE returns to the
+   client, it can assume that the write is safe, even in case of a
+   server crash, and it can discard the data written.  This is a very
+   important part of the statelessness of the server.  If the server
+   waited to flush data from remote requests, the client would have to
+   save those requests so that it could resend them in case of a server
+   crash.
+
+           /*
+            * Remote file service routines
+            */
+           program NFS_PROGRAM {
+                   version NFS_VERSION {
+                           void
+                           NFSPROC_NULL(void)              = 0;
+
+                           attrstat
+                           NFSPROC_GETATTR(fhandle)        = 1;
+
+                           attrstat
+                           NFSPROC_SETATTR(sattrargs)      = 2;
+
+                           void
+                           NFSPROC_ROOT(void)              = 3;
+
+                           diropres
+                           NFSPROC_LOOKUP(diropargs)       = 4;
+
+                           readlinkres
+                           NFSPROC_READLINK(fhandle)       = 5;
+
+                           readres
+                           NFSPROC_READ(readargs)          = 6;
+
+                           void
+                           NFSPROC_WRITECACHE(void)        = 7;
+
+                           attrstat
+                           NFSPROC_WRITE(writeargs)        = 8;
+
+                           diropres
+                           NFSPROC_CREATE(createargs)      = 9;
+
+                           stat
+                           NFSPROC_REMOVE(diropargs)       = 10;
+
+                           stat
+                           NFSPROC_RENAME(renameargs)      = 11;
+
+                           stat
+                           NFSPROC_LINK(linkargs)          = 12;
+
+                           stat
+                           NFSPROC_SYMLINK(symlinkargs)    = 13;
+
+                           diropres
+                           NFSPROC_MKDIR(createargs)       = 14;
+
+                           stat
+                           NFSPROC_RMDIR(diropargs)        = 15;
+
+                           readdirres
+                           NFSPROC_READDIR(readdirargs)    = 16;
+
+                           statfsres
+                           NFSPROC_STATFS(fhandle)         = 17;
+                   } = 2;
+           } = 100003;
+
+2.2.1.  Do Nothing
+
+           void
+           NFSPROC_NULL(void) = 0;
+
+   This procedure does no work.  It is made available in all RPC
+   services to allow server response testing and timing.
+
+2.2.2.  Get File Attributes
+
+           attrstat
+           NFSPROC_GETATTR (fhandle) = 1;
+
+   If the reply status is NFS_OK, then the reply attributes contains the
+   attributes for the file given by the input fhandle.
+
+2.2.3.  Set File Attributes
+
+           struct sattrargs {
+                   fhandle file;
+                   sattr attributes;
+           };
+
+           attrstat
+           NFSPROC_SETATTR (sattrargs) = 2;
+
+   The "attributes" argument contains fields which are either -1 or are
+   the new value for the attributes of "file".  If the reply status is
+   NFS_OK, then the reply attributes have the attributes of the file
+   after the "SETATTR" operation has completed.
+
+   Notes:  The use of -1 to indicate an unused field in "attributes" is
+   changed in the next version of the protocol.
+
+2.2.4.  Get Filesystem Root
+
+           void
+           NFSPROC_ROOT(void) = 3;
+
+   Obsolete.  This procedure is no longer used because finding the root
+   file handle of a filesystem requires moving pathnames between client
+   and server.  To do this right, we would have to define a network
+   standard representation of pathnames.  Instead, the function of
+   looking up the root file handle is done by the MNTPROC_MNT procedure.
+   (See Appendix A, "Mount Protocol Definition", for details).
+
+2.2.5.  Look Up File Name
+
+           diropres
+           NFSPROC_LOOKUP(diropargs) = 4;
+
+   If the reply "status" is NFS_OK, then the reply "file" and reply
+   "attributes" are the file handle and attributes for the file "name"
+   in the directory given by "dir" in the argument.
+
+2.2.6.  Read From Symbolic Link
+
+           union readlinkres switch (stat status) {
+           case NFS_OK:
+               path data;
+           default:
+               void;
+           };
+
+           readlinkres
+           NFSPROC_READLINK(fhandle) = 5;
+
+   If "status" has the value NFS_OK, then the reply "data" is the data
+   in the symbolic link given by the file referred to by the fhandle
+   argument.
+
+   Notes:  Since NFS always parses pathnames on the client, the pathname
+   in a symbolic link may mean something different (or be meaningless)
+   on a different client or on the server if a different pathname syntax
+   is used.
+
+2.2.7.  Read From File
+
+           struct readargs {
+                   fhandle file;
+                   unsigned offset;
+                   unsigned count;
+                   unsigned totalcount;
+           };
+
+           union readres switch (stat status) {
+           case NFS_OK:
+                   fattr attributes;
+                   nfsdata data;
+           default:
+                   void;
+           };
+
+           readres
+           NFSPROC_READ(readargs) = 6;
+
+   Returns up to "count" bytes of "data" from the file given by "file",
+   starting at "offset" bytes from the beginning of the file.  The first
+   byte of the file is at offset zero.  The file attributes after the
+   read takes place are returned in "attributes".
+
+   Notes:  The argument "totalcount" is unused, and is removed in the
+   next protocol revision.
+
+2.2.8.  Write to Cache
+
+           void
+           NFSPROC_WRITECACHE(void) = 7;
+
+   To be used in the next protocol revision.
+
+2.2.9.  Write to File
+
+           struct writeargs {
+                   fhandle file;
+                   unsigned beginoffset;
+                   unsigned offset;
+                   unsigned totalcount;
+                   nfsdata data;
+           };
+
+           attrstat
+           NFSPROC_WRITE(writeargs) = 8;
+
+   Writes "data" beginning "offset" bytes from the beginning of "file".
+   The first byte of the file is at offset zero.  If the reply "status"
+   is NFS_OK, then the reply "attributes" contains the attributes of the
+   file after the write has completed.  The write operation is atomic.
+   Data from this "WRITE" will not be mixed with data from another
+   client's "WRITE".
+
+   Notes:  The arguments "beginoffset" and "totalcount" are ignored and
+   are removed in the next protocol revision.
+
+2.2.10.  Create File
+
+           struct createargs {
+                   diropargs where;
+                   sattr attributes;
+           };
+
+           diropres
+           NFSPROC_CREATE(createargs) = 9;
+
+   The file "name" is created in the directory given by "dir".  The
+   initial attributes of the new file are given by "attributes".  A
+   reply "status" of NFS_OK indicates that the file was created, and
+   reply "file" and reply "attributes" are its file handle and
+   attributes.  Any other reply "status" means that the operation failed
+   and no file was created.
+
+   Notes:  This routine should pass an exclusive create flag, meaning
+   "create the file only if it is not already there".
+
+2.2.11.  Remove File
+
+           stat
+           NFSPROC_REMOVE(diropargs) = 10;
+
+   The file "name" is removed from the directory given by "dir".  A
+   reply of NFS_OK means the directory entry was removed.
+
+   Notes:  possibly non-idempotent operation.
+
+2.2.12.  Rename File
+
+           struct renameargs {
+                   diropargs from;
+                   diropargs to;
+           };
+
+           stat
+           NFSPROC_RENAME(renameargs) = 11;
+
+   The existing file "from.name" in the directory given by "from.dir" is
+   renamed to "to.name" in the directory given by "to.dir".  If the
+   reply is NFS_OK, the file was renamed.  The RENAME operation is
+   atomic on the server; it cannot be interrupted in the middle.
+
+   Notes:  possibly non-idempotent operation.
+
+2.2.13.  Create Link to File
+
+   Procedure 12, Version 2.
+
+           struct linkargs {
+                   fhandle from;
+                   diropargs to;
+           };
+
+           stat
+           NFSPROC_LINK(linkargs) = 12;
+
+   Creates the file "to.name" in the directory given by "to.dir", which
+   is a hard link to the existing file given by "from".  If the return
+   value is NFS_OK, a link was created.  Any other return value
+   indicates an error, and the link was not created.
+
+   A hard link should have the property that changes to either of the
+   linked files are reflected in both files.  When a hard link is made
+   to a file, the attributes for the file should have a value for
+   "nlink" that is one greater than the value before the link.
+
+   Notes:  possibly non-idempotent operation.
+
+2.2.14.  Create Symbolic Link
+
+           struct symlinkargs {
+                   diropargs from;
+                   path to;
+                   sattr attributes;
+           };
+
+           stat
+           NFSPROC_SYMLINK(symlinkargs) = 13;
+
+   Creates the file "from.name" with ftype NFLNK in the directory given
+   by "from.dir".  The new file contains the pathname "to" and has
+   initial attributes given by "attributes".  If the return value is
+   NFS_OK, a link was created.  Any other return value indicates an
+   error, and the link was not created.
+
+   A symbolic link is a pointer to another file.  The name given in "to"
+   is not interpreted by the server, only stored in the newly created
+   file.  When the client references a file that is a symbolic link, the
+   contents of the symbolic link are normally transparently
+   reinterpreted as a pathname to substitute.  A READLINK operation
+   returns the data to the client for interpretation.
+
+   Notes:  On UNIX servers the attributes are never used, since symbolic
+   links always have mode 0777.
+
+2.2.15.  Create Directory
+
+           diropres
+           NFSPROC_MKDIR (createargs) = 14;
+
+   The new directory "where.name" is created in the directory given by
+   "where.dir".  The initial attributes of the new directory are given
+   by "attributes".  A reply "status" of NFS_OK indicates that the new
+   directory was created, and reply "file" and reply "attributes" are
+   its file handle and attributes.  Any other reply "status" means that
+   the operation failed and no directory was created.
+
+   Notes:  possibly non-idempotent operation.
+
+2.2.16.  Remove Directory
+
+           stat
+           NFSPROC_RMDIR(diropargs) = 15;
+
+   The existing empty directory "name" in the directory given by "dir"
+   is removed.  If the reply is NFS_OK, the directory was removed.
+
+   Notes:  possibly non-idempotent operation.
+
+2.2.17.  Read From Directory
+
+           struct readdirargs {
+                   fhandle dir;
+                   nfscookie cookie;
+                   unsigned count;
+           };
+
+           struct entry {
+                   unsigned fileid;
+                   filename name;
+                   nfscookie cookie;
+                   entry *nextentry;
+           };
+
+           union readdirres switch (stat status) {
+           case NFS_OK:
+                   struct {
+                           entry *entries;
+                           bool eof;
+                   } readdirok;
+           default:
+                   void;
+           };
+
+           readdirres
+           NFSPROC_READDIR (readdirargs) = 16;
+
+   Returns a variable number of directory entries, with a total size of
+   up to "count" bytes, from the directory given by "dir".  If the
+   returned value of "status" is NFS_OK, then it is followed by a
+   variable number of "entry"s.  Each "entry" contains a "fileid" which
+   consists of a unique number to identify the file within a filesystem,
+   the "name" of the file, and a "cookie" which is an opaque pointer to
+   the next entry in the directory.  The cookie is used in the next
+   READDIR call to get more entries starting at a given point in the
+   directory.  The special cookie zero (all bits zero) can be used to
+   get the entries starting at the beginning of the directory.  The
+   "fileid" field should be the same number as the "fileid" in the the
+   attributes of the file.  (See section "2.3.5. fattr" under "Basic
+   Data Types".)  The "eof" flag has a value of TRUE if there are no
+   more entries in the directory.
+
+2.2.18.  Get Filesystem Attributes
+
+           union statfsres (stat status) {
+           case NFS_OK:
+               struct {
+                   unsigned tsize;
+                   unsigned bsize;
+                   unsigned blocks;
+                   unsigned bfree;
+                   unsigned bavail;
+               } info;
+           default:
+                   void;
+           };
+
+           statfsres
+           NFSPROC_STATFS(fhandle) = 17;
+
+   If the reply "status" is NFS_OK, then the reply "info" gives the
+   attributes for the filesystem that contains file referred to by the
+   input fhandle.  The attribute fields contain the following values:
+
+      tsize   The optimum transfer size of the server in bytes.  This is
+              the number of bytes the server would like to have in the
+              data part of READ and WRITE requests.
+
+      bsize   The block size in bytes of the filesystem.
+
+      blocks  The total number of "bsize" blocks on the filesystem.
+
+      bfree   The number of free "bsize" blocks on the filesystem.
+
+      bavail  The number of "bsize" blocks available to non-privileged
+              users.
+
+   Notes:  This call does not work well if a filesystem has variable
+   size blocks.
+
+2.3.  Basic Data Types
+
+   The following XDR definitions are basic structures and types used in
+   other structures described further on.
+
+2.3.1.  stat
+
+       enum stat {
+           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
+       };
+
+   The "stat" type is returned with every procedure's results.  A value
+   of NFS_OK indicates that the call completed successfully and the
+   results are valid.  The other values indicate some kind of error
+   occurred on the server side during the servicing of the procedure.
+   The error values are derived from UNIX error numbers.
+
+   NFSERR_PERM
+      Not owner.  The caller does not have correct ownership to perform
+      the requested operation.
+
+   NFSERR_NOENT
+      No such file or directory.  The file or directory specified does
+      not exist.
+
+   NFSERR_IO
+      Some sort of hard error occurred when the operation was in
+      progress.  This could be a disk error, for example.
+
+   NFSERR_NXIO
+      No such device or address.
+
+   NFSERR_ACCES
+      Permission denied.  The caller does not have the correct
+      permission to perform the requested operation.
+
+   NFSERR_EXIST
+      File exists.  The file specified already exists.
+
+   NFSERR_NODEV
+      No such device.
+
+   NFSERR_NOTDIR
+      Not a directory.  The caller specified a non-directory in a
+      directory operation.
+
+   NFSERR_ISDIR
+      Is a directory.  The caller specified a directory in a non-
+      directory operation.
+
+   NFSERR_FBIG
+      File too large.  The operation caused a file to grow beyond the
+      server's limit.
+
+   NFSERR_NOSPC
+      No space left on device.  The operation caused the server's
+      filesystem to reach its limit.
+
+   NFSERR_ROFS
+      Read-only filesystem.  Write attempted on a read-only filesystem.
+
+   NFSERR_NAMETOOLONG
+      File name too long.  The file name in an operation was too long.
+
+   NFSERR_NOTEMPTY
+      Directory not empty.  Attempted to remove a directory that was not
+      empty.
+
+   NFSERR_DQUOT
+      Disk quota exceeded.  The client's disk quota on the server has
+      been exceeded.
+
+   NFSERR_STALE
+      The "fhandle" given in the arguments was invalid.  That is, the
+      file referred to by that file handle no longer exists, or access
+      to it has been revoked.
+
+   NFSERR_WFLUSH
+      The server's write cache used in the "WRITECACHE" call got flushed
+      to disk.
+
+2.3.2.  ftype
+
+          enum ftype {
+              NFNON = 0,
+              NFREG = 1,
+              NFDIR = 2,
+              NFBLK = 3,
+              NFCHR = 4,
+              NFLNK = 5
+          };
+
+      The enumeration "ftype" gives the type of a file.  The type NFNON
+      indicates a non-file, NFREG is a regular file, NFDIR is a
+      directory, NFBLK is a block-special device, NFCHR is a character-
+      special device, and NFLNK is a symbolic link.
+
+2.3.3.  fhandle
+
+          typedef opaque fhandle[FHSIZE];
+
+      The "fhandle" is the file handle passed between the server and the
+      client.  All file operations are done using file handles to refer
+      to a file or directory.  The file handle can contain whatever
+      information the server needs to distinguish an individual file.
+
+2.3.4.  timeval
+
+          struct timeval {
+              unsigned int seconds;
+              unsigned int useconds;
+          };
+
+      The "timeval" structure is the number of seconds and microseconds
+      since midnight January 1, 1970, Greenwich Mean Time.  It is used
+      to pass time and date information.
+
+2.3.5.  fattr
+
+          struct fattr {
+              ftype        type;
+              unsigned int mode;
+              unsigned int nlink;
+              unsigned int uid;
+              unsigned int gid;
+              unsigned int size;
+              unsigned int blocksize;
+              unsigned int rdev;
+              unsigned int blocks;
+
+              unsigned int fsid;
+              unsigned int fileid;
+              timeval      atime;
+              timeval      mtime;
+              timeval      ctime;
+          };
+
+      The "fattr" structure contains the attributes of a file; "type" is
+      the type of the file; "nlink" is the number of hard links to the
+      file (the number of different names for the same file); "uid" is
+      the user identification number of the owner of the file; "gid" is
+      the group identification number of the group of the file; "size"
+      is the size in bytes of the file; "blocksize" is the size in bytes
+      of a block of the file; "rdev" is the device number of the file if
+      it is type NFCHR or NFBLK; "blocks" is the number of blocks the
+      file takes up on disk; "fsid" is the file system identifier for
+      the filesystem containing the file; "fileid" is a number that
+      uniquely identifies the file within its filesystem; "atime" is the
+      time when the file was last accessed for either read or write;
+      "mtime" is the time when the file data was last modified
+      (written); and "ctime" is the time when the status of the file was
+      last changed.  Writing to the file also changes "ctime" if the
+      size of the file changes.
+
+      "Mode" is the access mode encoded as a set of bits.  Notice that
+      the file type is specified both in the mode bits and in the file
+      type.  This is really a bug in the protocol and will be fixed in
+      future versions.  The descriptions given below specify the bit
+      positions using octal numbers.
+
+      0040000 This is a directory; "type" field should be NFDIR.
+      0020000 This is a character special file; "type" field should
+              be NFCHR.
+      0060000 This is a block special file; "type" field should be
+              NFBLK.
+      0100000 This is a regular file; "type" field should be NFREG.
+      0120000 This is a symbolic link file;  "type" field should be
+              NFLNK.
+      0140000 This is a named socket; "type" field should be NFNON.
+      0004000 Set user id on execution.
+      0002000 Set group id on execution.
+      0001000 Save swapped text even after use.
+      0000400 Read permission for owner.
+      0000200 Write permission for owner.
+      0000100 Execute and search permission for owner.
+      0000040 Read permission for group.
+      0000020 Write permission for group.
+      0000010 Execute and search permission for group.
+
+      0000004 Read permission for others.
+      0000002 Write permission for others.
+      0000001 Execute and search permission for others.
+
+      Notes:  The bits are the same as the mode bits returned by the
+      stat(2) system call in UNIX.  The file type is specified both in
+      the mode bits and in the file type.  This is fixed in future
+      versions.
+
+      The "rdev" field in the attributes structure is an operating
+      system specific device specifier.  It will be removed and
+      generalized in the next revision of the protocol.
+
+2.3.6.  sattr
+
+          struct sattr {
+              unsigned int mode;
+              unsigned int uid;
+              unsigned int gid;
+              unsigned int size;
+              timeval      atime;
+              timeval      mtime;
+          };
+
+      The "sattr" structure contains the file attributes which can be
+      set from the client.  The fields are the same as for "fattr"
+      above.  A "size" of zero means the file should be truncated.  A
+      value of -1 indicates a field that should be ignored.
+
+2.3.7.  filename
+
+          typedef string filename<MAXNAMLEN>;
+
+      The type "filename" is used for passing file names or pathname
+      components.
+
+2.3.8.  path
+
+          typedef string path<MAXPATHLEN>;
+
+      The type "path" is a pathname.  The server considers it as a
+      string with no internal structure, but to the client it is the
+      name of a node in a filesystem tree.
+
+2.3.9.  attrstat
+
+          union attrstat switch (stat status) {
+          case NFS_OK:
+
+              fattr attributes;
+          default:
+              void;
+          };
+
+      The "attrstat" structure is a common procedure result.  It
+      contains a "status" and, if the call succeeded, it also contains
+      the attributes of the file on which the operation was done.
+
+2.3.10.  diropargs
+
+          struct diropargs {
+              fhandle  dir;
+              filename name;
+          };
+
+      The "diropargs" structure is used in directory operations.  The
+      "fhandle" "dir" is the directory in which to find the file "name".
+      A directory operation is one in which the directory is affected.
+
+2.3.11.  diropres
+
+          union diropres switch (stat status) {
+          case NFS_OK:
+              struct {
+                  fhandle file;
+                  fattr   attributes;
+              } diropok;
+          default:
+              void;
+          };
+
+      The results of a directory operation are returned in a "diropres"
+      structure.  If the call succeeded, a new file handle "file" and
+      the "attributes" associated with that file are returned along with
+      the "status".
+
+3. NFS IMPLEMENTATION ISSUES
+
+   The NFS protocol was designed to allow different operating systems to
+   share files.  However, since it was designed in a UNIX environment,
+   many operations have semantics similar to the operations of the UNIX
+   file system.  This section discusses some of the implementation-
+   specific details and semantic issues.
+
+3.1.  Server/Client Relationship
+
+   The NFS protocol is designed to allow servers to be as simple and
+
+   general as possible.  Sometimes the simplicity of the server can be a
+   problem, if the client wants to implement complicated filesystem
+   semantics.
+
+   For example, some operating systems allow removal of open files.  A
+   process can open a file and, while it is open, remove it from the
+   directory.  The file can be read and written as long as the process
+   keeps it open, even though the file has no name in the filesystem.
+   It is impossible for a stateless server to implement these semantics.
+   The client can do some tricks such as renaming the file on remove,
+   and only removing it on close.  We believe that the server provides
+   enough functionality to implement most file system semantics on the
+   client.
+
+   Every NFS client can also potentially be a server, and remote and
+   local mounted filesystems can be freely intermixed.  This leads to
+   some interesting problems when a client travels down the directory
+   tree of a remote filesystem and reaches the mount point on the server
+   for another remote filesystem.  Allowing the server to follow the
+   second remote mount would require loop detection, server lookup, and
+   user revalidation.  Instead, we decided not to let clients cross a
+   server's mount point.  When a client does a LOOKUP on a directory on
+   which the server has mounted a filesystem, the client sees the
+   underlying directory instead of the mounted directory.
+
+   For example, if a server has a file system called "/usr" and mounts
+   another file system on  "/usr/src", if a client mounts "/usr", it
+   does NOT see the mounted version of "/usr/src".  A client could do
+   remote mounts that match the server's mount points to maintain the
+   server's view.  In this example, the client would also have to mount
+   "/usr/src" in addition to "/usr", even if they are from the same
+   server.
+
+3.2. Pathname Interpretation
+
+   There are a few complications to the rule that pathnames are always
+   parsed on the client.  For example, symbolic links could have
+   different interpretations on different clients.  Another common
+   problem for non-UNIX implementations is the special interpretation of
+   the pathname ".." to mean the parent of a given directory.  The next
+   revision of the protocol uses an explicit flag to indicate the parent
+   instead.
+
+3.3.  Permission Issues
+
+   The NFS protocol, strictly speaking, does not define the permission
+   checking used by servers.  However, it is expected that a server will
+   do normal operating system permission checking using AUTH_UNIX style
+
+   authentication as the basis of its protection mechanism.  The server
+   gets the client's effective "uid", effective "gid", and groups on
+   each call and uses them to check permission.  There are various
+   problems with this method that can been resolved in interesting ways.
+
+   Using "uid" and "gid" implies that the client and server share the
+   same "uid" list.  Every server and client pair must have the same
+   mapping from user to "uid" and from group to "gid".  Since every
+   client can also be a server, this tends to imply that the whole
+   network shares the same "uid/gid" space.  AUTH_DES (and the next
+   revision of the NFS protocol) uses string names instead of numbers,
+   but there are still complex problems to be solved.
+
+   Another problem arises due to the usually stateful open operation.
+   Most operating systems check permission at open time, and then check
+   that the file is open on each read and write request.  With stateless
+   servers, the server has no idea that the file is open and must do
+   permission checking on each read and write call.  On a local
+   filesystem, a user can open a file and then change the permissions so
+   that no one is allowed to touch it, but will still be able to write
+   to the file because it is open.  On a remote filesystem, by contrast,
+   the write would fail.  To get around this problem, the server's
+   permission checking algorithm should allow the owner of a file to
+   access it regardless of the permission setting.
+
+   A similar problem has to do with paging in from a file over the
+   network.  The operating system usually checks for execute permission
+   before opening a file for demand paging, and then reads blocks from
+   the open file.  The file may not have read permission, but after it
+   is opened it does not matter.  An NFS server can not tell the
+   difference between a normal file read and a demand page-in read.  To
+   make this work, the server allows reading of files if the "uid" given
+   in the call has either execute or read permission on the file.
+
+   In most operating systems, a particular user (on UNIX, the user ID
+   zero) has access to all files no matter what permission and ownership
+   they have.  This "super-user" permission may not be allowed on the
+   server, since anyone who can become super-user on their workstation
+   could gain access to all remote files.  The UNIX server by default
+   maps user id 0 to -2 before doing its access checking.  This works
+   except for NFS root filesystems, where super-user access cannot be
+   avoided.
+
+3.4.  RPC Information
+
+   Authentication
+      The NFS service uses AUTH_UNIX,  AUTH_DES, or AUTH_SHORT style
+      authentication, except in the NULL procedure where AUTH_NONE is
+
+      also allowed.
+
+   Transport Protocols
+      NFS is supported normally on UDP.
+
+   Port Number
+      The NFS protocol currently uses the UDP port number 2049.  This is
+      not an officially assigned port, so later versions of the protocol
+      use the "Portmapping" facility of RPC.
+
+3.5.  Sizes of XDR Structures
+
+   These are the sizes, given in decimal bytes, of various XDR
+   structures used in the protocol:
+
+   /*
+    * The maximum number of bytes of data in a READ or WRITE
+    * request.
+    */
+   const MAXDATA = 8192;
+
+   /* The maximum number of bytes in a pathname argument. */
+   const MAXPATHLEN = 1024;
+
+   /* The maximum number of bytes in a file name argument. */
+   const MAXNAMLEN = 255;
+
+   /* The size in bytes of the opaque "cookie" passed by READDIR. */
+   const COOKIESIZE  = 4;
+
+   /* The size in bytes of the opaque file handle. */
+   const FHSIZE = 32;
+
+3.6. Setting RPC Parameters
+
+   Various file system parameters and options should be set at mount
+   time.  The mount protocol is described in the appendix below.  For
+   example, "Soft" mounts as well as "Hard" mounts are usually both
+   provided.  Soft mounted file systems return errors when RPC
+   operations fail (after a given number of optional retransmissions),
+   while hard mounted file systems continue to retransmit forever.  The
+   maximum transfer sizes are implementation dependent.  For efficient
+   operation over a local network, 8192 bytes of data are normally used.
+   This may result in lower-level fragmentation (such as at the IP
+   level).  Since some network interfaces may not allow such packets,
+   for operation over slower-speed networks or hosts, or through
+   gateways, transfer sizes of 512 or 1024 bytes often provide better
+   results.
+
+   Clients and servers may need to keep caches of recent operations to
+   help avoid problems with non-idempotent operations.  For example, if
+   the transport protocol drops the response for a Remove File
+   operation, upon retransmission the server may return an error code of
+   NFSERR_NOENT instead of NFS_OK.  But if the server keeps around the
+   last operation requested and its result, it could return the proper
+   success code.  Of course, the server could be crashed and rebooted
+   between retransmissions, but a small cache (even a single entry)
+   would solve most problems.
+
+                   Appendix A. MOUNT PROTOCOL DEFINITION
+
+A.1.  Introduction
+
+   The mount protocol is separate from, but related to, the NFS
+   protocol.  It provides operating system specific services to get the
+   NFS off the ground -- looking up server path names, validating user
+   identity, and checking access permissions.  Clients use the mount
+   protocol to get the first file handle, which allows them entry into a
+   remote filesystem.
+
+   The mount protocol is kept separate from the NFS protocol to make it
+   easy to plug in new access checking and validation methods without
+   changing the NFS server protocol.
+
+   Notice that the protocol definition implies stateful servers because
+   the server maintains a list of client's mount requests.  The mount
+   list information is not critical for the correct functioning of
+   either the client or the server.  It is intended for advisory use
+   only, for example, to warn possible clients when a server is going
+   down.
+
+   Version one of the mount protocol is used with version two of the NFS
+   protocol.  The only information communicated between these two
+   protocols is the "fhandle" structure.
+
+A.2.  RPC Information
+
+   Authentication
+      The mount service uses AUTH_UNIX and AUTH_NONE style
+      authentication only.
+
+   Transport Protocols
+      The mount service is supported on both UDP and TCP.
+
+   Port Number
+      Consult the server's portmapper, described in RFC 1057 </rfcs/rfc1057.html>, "RPC:
+      Remote Procedure Call Protocol Specification", to find the port
+      number on which the mount service is registered.
+
+A.3.  Sizes of XDR Structures
+
+   These are the sizes, given in decimal bytes, of various XDR
+   structures used in the protocol:
+
+           /* The maximum number of bytes in a pathname argument. */
+           const MNTPATHLEN = 1024;
+
+           /* The maximum number of bytes in a name argument. */
+           const MNTNAMLEN = 255;
+
+           /* The size in bytes of the opaque file handle. */
+           const FHSIZE = 32;
+
+A.4.  Basic Data Types
+
+   This section presents the data types used by the mount protocol.  In
+   many cases they are similar to the types used in NFS.
+
+A.4.1.  fhandle
+
+       typedef opaque fhandle[FHSIZE];
+
+   The type "fhandle" is the file handle that the server passes to the
+   client.  All file operations are done using file handles to refer to
+   a file or directory.  The file handle can contain whatever
+   information the server needs to distinguish an individual file.
+
+   This is the same as the "fhandle" XDR definition in version 2 of the
+   NFS protocol; see section "2.3.3. fhandle" under "Basic Data Types".
+
+A.4.2.  fhstatus
+
+       union fhstatus switch (unsigned status) {
+       case 0:
+           fhandle directory;
+       default:
+           void;
+       }
+
+   The type "fhstatus" is a union.  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.
+   In this case, the status is a UNIX error number.
+
+A.4.3.  dirpath
+
+       typedef string dirpath<MNTPATHLEN>;
+
+   The type "dirpath" is a server pathname of a directory.
+
+A.4.4.  name
+
+       typedef string name<MNTNAMLEN>;
+
+   The type "name" is an arbitrary string used for various names.
+
+A.5.  Server Procedures
+
+   The following sections define the RPC procedures supplied by a mount
+   server.
+
+           /*
+            * Protocol description for the mount program
+            */
+           program MOUNTPROG {
+                   /*
+                    * Version 1 of the mount protocol used with
+                    * version 2 of the NFS protocol.
+                    */
+                   version MOUNTVERS {
+
+                           void
+                           MOUNTPROC_NULL(void) = 0;
+
+                           fhstatus
+                           MOUNTPROC_MNT(dirpath) = 1;
+
+                           mountlist
+                           MOUNTPROC_DUMP(void) = 2;
+
+                           void
+                           MOUNTPROC_UMNT(dirpath) = 3;
+
+                           void
+                           MOUNTPROC_UMNTALL(void) = 4;
+
+                           exportlist
+                           MOUNTPROC_EXPORT(void)  = 5;
+                   } = 1;
+           } = 100005;
+
+A.5.1.  Do Nothing
+
+           void
+           MNTPROC_NULL(void) = 0;
+
+   This procedure does no work.  It is made available in all RPC
+   services to allow server response testing and timing.
+
+A.5.2.  Add Mount Entry
+
+           fhstatus
+           MNTPROC_MNT(dirpath) = 1;
+
+   If the reply "status" is 0, then the reply "directory" contains the
+   file handle for the directory "dirname".  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 "dirname".
+
+A.5.3.  Return Mount Entries
+
+           struct *mountlist {
+                   name      hostname;
+                   dirpath   directory;
+                   mountlist nextentry;
+           };
+
+           mountlist
+           MNTPROC_DUMP(void) = 2;
+
+   Returns the list of remote mounted filesystems.  The "mountlist"
+   contains one entry for each "hostname" and "directory" pair.
+
+A.5.4.  Remove Mount Entry
+
+           void
+           MNTPROC_UMNT(dirpath) = 3;
+
+   Removes the mount list entry for the input "dirpath".
+
+A.5.5.  Remove All Mount Entries
+
+           void
+           MNTPROC_UMNTALL(void) = 4;
+
+   Removes all of the mount list entries for this client.
+
+A.5.6.  Return Export List
+
+           struct *groups {
+                   name grname;
+                   groups grnext;
+           };
+
+           struct *exportlist {
+                   dirpath filesys;
+                   groups groups;
+                   exportlist next;
+           };
+
+           exportlist
+           MNTPROC_EXPORT(void) = 5;
+
+   Returns a variable number of export list entries.  Each entry
+   contains a filesystem name and a list of groups that are allowed to
+   import it.  The filesystem name is in "filesys", and the group name
+   is in the list "groups".
+
+   Notes:  The exportlist should contain more information about the
+   status of the filesystem, such as a read-only flag.
+
+Author's Address:
+
+   Bill Nowicki
+   Sun Microsystems, Inc.
+   Mail Stop 1-40
+   2550 Garcia Avenue
+   Mountain View, CA 94043
+
+   Phone: (415) 336-7278
+
+   Email: nowicki at SUN.COM <mailto:nowicki at SUN.COM>
+
+Comment on RFC 1094 </rfccomment.php?rfcnum=1094>
+
+ 
+
+Comments about this RFC:
+
+    * RFC 1094: I am preparing for doing a project in File System in
+      Network. can you specify... </qa/rfcc-377.html> by Ravik (12/4/2003)
+
+
+Previous: RFC 1093 - NSFNET routing architecture </rfcs/rfc1093.html>
+
+	 	
+
+Next: RFC 1095 - Common Management Information Services and Protocol
+over TCP/IP (CMOT) </rfcs/rfc1095.html>
+
+ 
+
+------------------------------------------------------------------------
diff --git a/services/nfsclient/rpcio.c b/services/nfsclient/rpcio.c
new file mode 100644
index 0000000..eeadd53
--- /dev/null
+++ b/services/nfsclient/rpcio.c
@@ -0,0 +1,1790 @@
+/* RPC multiplexor for a multitasking environment */
+
+/* Author: Till Straumann <strauman at slac.stanford.edu>, 2002 */
+
+/* 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).
+ */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ *     Till Straumann <strauman at 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 "rpcio.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 *)))
+extern void 				xdrmbuf_create(XDR *, struct mbuf *, enum xdr_op);
+#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;
+
+#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 */
+		rval->obuf.xid = time(0) ^ (uintptr_t)rval;
+		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 & ~XACT_HASH_MSK;
+		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);
+}
+
+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 );
+
+			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;
+}
+
+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 file %s line: %i, requestor id was 0x%08x",
+									__FILE__,
+									__LINE__,
+									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>
+
+ssize_t
+recv_mbuf_from(int s, struct mbuf **ppm, long len, struct sockaddr *fromaddr, int *fromlen);
+
+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
diff --git a/services/nfsclient/rpcio.modini.c b/services/nfsclient/rpcio.modini.c
new file mode 100644
index 0000000..7aa802f
--- /dev/null
+++ b/services/nfsclient/rpcio.modini.c
@@ -0,0 +1,19 @@
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "librtemsNfs.h"
+/* CEXP module support (magic init) */
+void
+_cexpModuleInitialize(void *mod)
+{
+	rpcUdpInit();
+}
+
+int
+_cexpModuleFinalize(void *mod)
+{
+	return rpcUdpCleanup();
+}
+
+
diff --git a/services/nfsclient/sock_mbuf.c b/services/nfsclient/sock_mbuf.c
new file mode 100644
index 0000000..e07c63b
--- /dev/null
+++ b/services/nfsclient/sock_mbuf.c
@@ -0,0 +1,281 @@
+/*
+ *  NOTE:
+ *    This is derived from libnetworking/rtems/rtems_syscall.c
+ *
+ *    RTEMS/libnetworking LICENSING restrictions may apply
+ *
+ *    Author (modifications only):
+ *    Copyright: 2002, Stanford University and
+ *    		 Till Straumann, <strauman at slac.stanford.edu>
+ *    Licensing: 'LICENSE.NET' file in the RTEMS top source directory
+ *               for more information.
+ */
+
+/*
+The RTEMS TCP/IP stack is a port of the FreeBSD TCP/IP stack.  The following
+copyright and licensing information applies to this code.
+
+This code is found under the c/src/libnetworking directory but does not
+constitute the entire contents of that subdirectory.
+
+=============================================================================
+
+Copyright (c) 1980, 1983, 1988, 1993
+	The Regents of the University of California.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+   must display the following acknowledgment:
+	This product includes software developed by the University of
+	California, Berkeley and its contributors.
+4. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-
+Portions Copyright (c) 1993 by Digital Equipment Corporation.
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies, and that
+the name of Digital Equipment Corporation not be used in advertising or
+publicity pertaining to distribution of the document or software without
+specific, written prior permission.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
+CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+=============================================================================
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/error.h>
+
+#define _KERNEL
+#define __BSD_VISIBLE	1
+#include <rtems/rtems_bsdnet.h>
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+struct socket *rtems_bsdnet_fdToSocket(int fd);
+
+/*
+ * Package system call argument into mbuf.
+ *
+ * (unfortunately, the original is not public)
+ */
+static int
+sockaddrtombuf (struct mbuf **mp, const struct sockaddr *buf, int buflen)
+{
+struct mbuf *m;
+struct sockaddr *sa;
+
+	if ((u_int)buflen > MLEN)
+		return (EINVAL);
+
+	rtems_bsdnet_semaphore_obtain();
+	m = m_get(M_WAIT, MT_SONAME);
+	rtems_bsdnet_semaphore_release();
+
+	if (m == NULL)
+		return (ENOBUFS);
+	m->m_len = buflen;
+	memcpy (mtod(m, caddr_t), buf, buflen);
+	*mp = m;
+	sa = mtod(m, struct sockaddr *);
+	sa->sa_len = buflen;
+
+	return 0;
+}
+
+static void
+dummyproc(caddr_t ext_buf, u_int ext_size)
+{
+}
+
+/*
+ * send data by simply allocating an MBUF packet
+ * header and pointing it to our data region.
+ *
+ * Optionally, the caller may supply 'reference'
+ * and 'free' procs. (The latter may call the
+ * user back once the networking stack has
+ * released the buffer).
+ *
+ * The callbacks are provided with the 'closure'
+ * pointer and the 'buflen' argument.
+ */
+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)
+)
+{
+	int           error;
+	struct socket *so;
+	struct mbuf   *to, *m;
+	int           ret = -1;
+
+	rtems_bsdnet_semaphore_obtain ();
+	if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) {
+		rtems_bsdnet_semaphore_release ();
+		return -1;
+	}
+
+	error = sockaddrtombuf (&to, toaddr, tolen);
+	if (error) {
+		errno = error;
+		rtems_bsdnet_semaphore_release ();
+		return -1;
+	}
+
+	MGETHDR(m, M_WAIT, MT_DATA);
+	m->m_pkthdr.len   = 0;
+	m->m_pkthdr.rcvif =  (struct ifnet *) 0;
+
+	m->m_flags       |= M_EXT;
+	m->m_ext.ext_buf  = closure ? closure : (void*)buf;
+	m->m_ext.ext_size = buflen;
+	/* we _must_ supply non-null procs; otherwise,
+	 * the kernel code assumes it's a mbuf cluster
+	 */
+	m->m_ext.ext_free = freeproc ? freeproc : dummyproc;
+	m->m_ext.ext_ref  = refproc  ? refproc  : dummyproc;
+	m->m_pkthdr.len  += buflen;
+	m->m_len          = buflen;
+	m->m_data		  = (void*)buf;
+
+	error = sosend (so, to, NULL, m, NULL, flags);
+	if (error) {
+		if (/*auio.uio_resid != len &&*/ (error == EINTR || error == EWOULDBLOCK))
+			error = 0;
+	}
+	if (error)
+		errno = error;
+	else
+		ret = buflen;
+	if (to)
+		m_freem(to);
+	rtems_bsdnet_semaphore_release ();
+	return (ret);
+}
+
+
+/*
+ * receive data in an 'mbuf chain'.
+ * The chain must be released once the
+ * data has been extracted:
+ *
+ *   rtems_bsdnet_semaphore_obtain();
+ *   	m_freem(chain);
+ *   rtems_bsdnet_semaphore_release();
+ */
+ssize_t
+recv_mbuf_from(int s, struct mbuf **ppm, long len, struct sockaddr *fromaddr, int *fromlen)
+{
+	int ret = -1;
+	int error;
+	struct uio auio;
+	struct socket *so;
+	struct mbuf *from = NULL;
+
+	memset(&auio, 0, sizeof(auio));
+	*ppm = 0;
+
+	rtems_bsdnet_semaphore_obtain ();
+	if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) {
+		rtems_bsdnet_semaphore_release ();
+		return -1;
+	}
+/*	auio.uio_iov = mp->msg_iov;
+	auio.uio_iovcnt = mp->msg_iovlen;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_rw = UIO_READ;
+	auio.uio_offset = 0;
+*/
+	auio.uio_resid = len;
+	error = soreceive (so, &from, &auio, (struct mbuf **) ppm,
+			(struct mbuf **)NULL,
+			NULL);
+	if (error) {
+		if (auio.uio_resid != len && (error == EINTR || error == EWOULDBLOCK))
+			error = 0;
+	}
+	if (error) {
+		errno = error;
+	}
+	else {
+		ret = len - auio.uio_resid;
+		if (fromaddr) {
+			len = *fromlen;
+			if ((len <= 0) || (from == NULL)) {
+				len = 0;
+			}
+			else {
+				if (len > from->m_len)
+					len = from->m_len;
+				memcpy (fromaddr, mtod(from, caddr_t), len);
+			}
+			*fromlen = len;
+		}
+	}
+	if (from)
+		m_freem (from);
+	if (error && *ppm) {
+		m_freem(*ppm);
+		*ppm = 0;
+	}
+	rtems_bsdnet_semaphore_release ();
+	return (ret);
+}
diff --git a/services/nfsclient/xdr_mbuf.c b/services/nfsclient/xdr_mbuf.c
new file mode 100644
index 0000000..3c73639
--- /dev/null
+++ b/services/nfsclient/xdr_mbuf.c
@@ -0,0 +1,537 @@
+/* xdr_mbuf is derived from xdr_mem */
+
+/* Author (mbuf specifica): Till Straumann <strauman at slac.stanford.edu>, 10/2002 */
+
+/*
+ * 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
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)xdr_mem.c	2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/xdr/xdr_mem.c,v 1.8 1999/08/28 00:02:56 peter Exp $";
+#endif
+
+/*
+ * xdr_mbuf,  XDR implementation using mbuf buffers
+ *
+ * derived from:
+ *
+ * xdr_mem.h, XDR implementation using memory buffers.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * The MBUF stream is useful for BSDNET kernel (or RTEMS for that matter)
+ * use.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <netinet/in.h>
+
+#include <stdlib.h>
+
+#define TODO
+
+/* TODO remove: a hack because malloc is redefined */
+#ifdef TODO
+static inline void *
+my_malloc(size_t i)
+{
+	return malloc(i);
+}
+
+static inline void
+my_free(void *p)
+{
+	return free(p);
+}
+#endif
+
+#define DEBUG_ASSERT	(1<<0)
+#define DEBUG_VERB		(1<<1)
+
+#define DEBUG			DEBUG_ASSERT
+
+#define _KERNEL
+#include <sys/mbuf.h>
+
+#include <assert.h>
+
+#if DEBUG & DEBUG_VERB || defined(TODO)
+#include <stdio.h>
+#endif
+
+static bool_t	xdrmbuf_getlong_aligned(XDR *xdrs, long *lp);
+static bool_t	xdrmbuf_putlong_aligned(XDR *xdrs, const long *lp);
+static bool_t	xdrmbuf_getlong_unaligned(XDR *xdrs, long *lp);
+static bool_t	xdrmbuf_putlong_unaligned(XDR *xdrs, const long *lp);
+static bool_t	xdrmbuf_getbytes(XDR *xdrs, caddr_t addr, u_int len);
+static bool_t	xdrmbuf_putbytes(XDR *xdrs, const char *addr, u_int len);
+static u_int	xdrmbuf_getpos(XDR *xdrs); /* XXX w/64-bit pointers, u_int not enough! */
+static bool_t	xdrmbuf_setpos(XDR *xdrs, u_int pos);
+static int32_t *xdrmbuf_inline_aligned(XDR *xdrs, u_int len);
+static int32_t *xdrmbuf_inline_unaligned(XDR *xdrs, u_int len);
+static void	xdrmbuf_destroy(XDR *);
+
+static struct	xdr_ops xdrmbuf_ops_aligned = {
+	xdrmbuf_getlong_aligned,
+	xdrmbuf_putlong_aligned,
+	xdrmbuf_getbytes,
+	xdrmbuf_putbytes,
+	xdrmbuf_getpos,
+	xdrmbuf_setpos,
+	xdrmbuf_inline_aligned,
+	xdrmbuf_destroy
+};
+
+static struct	xdr_ops xdrmbuf_ops_unaligned = {
+	xdrmbuf_getlong_unaligned,
+	xdrmbuf_putlong_unaligned,
+	xdrmbuf_getbytes,
+	xdrmbuf_putbytes,
+	xdrmbuf_getpos,
+	xdrmbuf_setpos,
+	xdrmbuf_inline_unaligned,
+	xdrmbuf_destroy
+};
+
+typedef struct MBPrivateRec_ {
+	struct mbuf		*mchain;
+	struct mbuf		*mcurrent;
+	u_int			pos;		/* number of bytes contained in all MUBFS ahead
+								 * of mcurrent
+								 */
+} MBPrivateRec, *MBPrivate;
+
+/* NOTE: the stream position helper 'pos'
+ *       must be managed by the caller!
+ */
+static inline void
+xdrmbuf_setup(XDR *xdrs, struct mbuf *m)
+{
+MBPrivate	mbp = (MBPrivate)xdrs->x_base;
+
+		mbp->mcurrent    = m;
+		xdrs->x_private  = mtod(m,caddr_t);
+		xdrs->x_handy    = m->m_len;
+		xdrs->x_ops      = ((uintptr_t)xdrs->x_private & (sizeof(int32_t) - 1))
+								? &xdrmbuf_ops_unaligned : &xdrmbuf_ops_aligned;
+}
+
+static struct mbuf *
+xdrmbuf_next(XDR *xdrs)
+{
+struct mbuf		*rval;
+MBPrivate		mbp = (MBPrivate)xdrs->x_base;
+
+	if (mbp->mcurrent) {
+		mbp->pos += mbp->mcurrent->m_len;
+		rval      = mbp->mcurrent->m_next;
+	} else {
+		rval      = 0;
+	}
+
+	if (rval) {
+		xdrmbuf_setup(xdrs, rval);
+	}
+#if DEBUG & DEBUG_VERB
+	else {
+		fprintf(stderr,"xdrmbuf: end of chain\n");
+	}
+#endif
+
+	return rval;
+}
+
+/*
+ * The procedure xdrmbuf_create initializes a stream descriptor for a
+ * memory buffer.
+ */
+void
+xdrmbuf_create(XDR *xdrs, struct mbuf *mbuf, enum xdr_op op)
+{
+MBPrivate mbp;
+
+	xdrs->x_op = op;
+	mbp = (MBPrivate)my_malloc(sizeof(*mbp));
+	assert( mbp );
+	xdrs->x_base  = (caddr_t) mbp;
+
+	mbp->mchain   = mbuf;
+	mbp->pos      = 0;
+
+#if DEBUG & DEBUG_VERB
+	{
+	struct mbuf *mbf;
+	fprintf(stderr,"Dumping chain:\n");
+	for (mbf = mbuf; mbf; mbf=mbf->m_next) {
+		int ii;
+		fprintf(stderr,"MBUF------------");
+		for (ii=0; ii<mbf->m_len; ii++) {
+			fprintf(stderr,"%02x ",mtod(mbf,char*)[ii]);
+			if (ii%16==0)
+				fputc('\n',stderr);
+		}
+		fputc('\n',stderr);
+	}
+	}
+#endif
+
+	xdrmbuf_setup(xdrs, mbuf);
+}
+
+static void
+xdrmbuf_destroy(XDR *xdrs)
+{
+MBPrivate	mbp = (MBPrivate)xdrs->x_base;
+#if 0 /* leave destroying the chain to the user */
+struct mbuf *m = mbp->mchain;
+
+	rtems_bsdnet_semaphore_obtain();
+		m_freem(m);
+	rtems_bsdnet_semaphore_release();
+#endif
+
+	my_free(mbp);
+}
+
+static bool_t
+xdrmbuf_getlong_aligned(register XDR *xdrs, register long *lp)
+{
+	while ( (signed int)(xdrs->x_handy -= sizeof(int32_t)) < 0) {
+		if ((xdrs->x_handy += sizeof(int32_t)) == 0) {
+			/* handy was 0 on entry; request a new buffer.
+			 * Coded this way, so the most frequently executed
+			 * path needs only one comparison...
+			 */
+			if (!xdrmbuf_next(xdrs))
+				return FALSE;
+		} else {
+			/* uh-oh an aligned long spread over two MBUFS ??
+			 * let the unaligned handler deal with this rare
+			 * situation.
+			 */
+			return xdrmbuf_getlong_unaligned(xdrs,lp);
+		}
+	}
+	*lp = ntohl(*(int32_t *)(xdrs->x_private));
+	xdrs->x_private += sizeof(int32_t);
+#if DEBUG & DEBUG_VERB
+	fprintf(stderr,"Got aligned long %x\n",*lp);
+#endif
+	return (TRUE);
+}
+
+static bool_t
+xdrmbuf_putlong_aligned(
+	XDR *xdrs,
+	const long *lp)
+{
+fprintf(stderr,"TODO: xdrmbuf_putlong_aligned() is unimplemented\n");
+	return FALSE;
+#if 0
+	if ((xdrs->x_handy -= sizeof(int32_t)) < 0)
+		return (FALSE);
+	*(int32_t *)xdrs->x_private = htonl(*lp);
+	xdrs->x_private += sizeof(int32_t);
+	return (TRUE);
+#endif
+}
+
+static bool_t
+xdrmbuf_getlong_unaligned(
+	XDR *xdrs,
+	long *lp)
+{
+union {
+	int32_t l;
+	char	c[sizeof(int32_t)];
+}	u;
+
+register int  i,j;
+register char *cp,*sp;
+
+	i = xdrs->x_handy - sizeof(int32_t);
+
+	/* handle the most common case first */
+	if ( i >= 0 ) {
+
+		xdrs->x_handy   = i;
+		sp			    = (char*)xdrs->x_private;
+		xdrs->x_private = sp + sizeof(int32_t);
+
+#ifdef CANDO_UNALIGNED
+		{
+			*lp              = ntohl(*(int32_t *)sp);
+#        if DEBUG & DEBUG_VERB
+			fprintf(stderr,"Got unaligned long %x (%i remaining)\n",*lp, xdrs->x_handy);
+#        endif
+			return TRUE;
+		}
+#else   /* machine can't do unaligned access */
+		{
+			u.c[0]          = *sp;
+			u.c[1]          = *++sp;
+			u.c[2]          = *++sp;
+			u.c[3]          = *++sp;
+
+			goto done;
+		}
+#endif /* CANDO_UNALIGNED */
+	}
+
+	/* here the messy 'crossing buffers' business starts */
+
+
+	j  = sizeof(int32_t);
+
+	cp = u.c-1;
+
+	/* NOTE: on entry to this section,  handy < j holds */
+	do {
+		sp = ((char*)xdrs->x_private)-1;
+
+		if ( (i=xdrs->x_handy) >= j ) {
+			/* more data in the buffer than we need:
+			 * copy everything we need and goto 'done'
+			 */
+			xdrs->x_handy    = i-j;
+			do {
+				*++cp = *++sp;
+			} while (--j > 0);
+			xdrs->x_private  = (caddr_t)++sp;
+
+			goto done;
+
+		} else {
+			/* not enough data - copy as much as possible
+			 * then get retrieve the next MBUF and start
+			 * over
+			 */
+			j-=i;
+			while (i--)
+				*++cp = *++sp;
+			if (!xdrmbuf_next(xdrs))
+				return FALSE;
+#if DEBUG & DEBUG_VERB
+			fprintf(stderr,"getlong_unaligned: crossed mbuf boundary\n");
+#endif
+		}
+	} while (j > 0);
+
+done:
+
+	*lp = ntohl(u.l);
+
+#if DEBUG & DEBUG_VERB
+	fprintf(stderr,"Got unaligned long %x (%i remaining)\n",*lp, xdrs->x_handy);
+#endif
+	return (TRUE);
+}
+
+static bool_t
+xdrmbuf_putlong_unaligned(
+	XDR *xdrs,
+	const long *lp )
+{
+
+	fprintf(stderr,"TODO: xdrmbuf_putlong_unaligned() is unimplemented\n");
+	return FALSE;
+#if 0
+	{
+	int32_t l;
+
+	if ((xdrs->x_handy -= sizeof(int32_t)) < 0)
+		return (FALSE);
+	l = htonl(*lp);
+	memcpy(xdrs->x_private, &l, sizeof(int32_t));
+	xdrs->x_private += sizeof(int32_t);
+	return (TRUE);
+	}
+#endif
+}
+
+static bool_t
+xdrmbuf_getbytes(
+	XDR *xdrs,
+	caddr_t addr,
+	u_int len)
+{
+#if DEBUG & DEBUG_VERB
+int	olen=len,bufs=0;
+#endif
+
+#if DEBUG & DEBUG_VERB
+	fprintf(stderr,"wanting %i bytes (have %i)\n",olen,xdrs->x_handy);
+#endif
+
+	while (len>0) {
+		if (xdrs->x_handy >= len) {
+			memcpy(addr, xdrs->x_private, len);
+			xdrs->x_private += len;
+			xdrs->x_handy   -= len;
+#if 0 /* save a couple of instructions */
+			len = 0;
+#else
+			goto done;
+#endif
+		} else {
+			if (xdrs->x_handy > 0) {
+				memcpy(addr, xdrs->x_private, xdrs->x_handy);
+				len  -= xdrs->x_handy;
+				addr += xdrs->x_handy;
+			}
+			if (!xdrmbuf_next(xdrs))
+				return FALSE;
+#if DEBUG & DEBUG_VERB
+			bufs++;
+#endif
+		}
+	}
+done:
+#if DEBUG & DEBUG_VERB
+	fprintf(stderr,"Got %i bytes (out of %i mbufs)\n",olen,bufs);
+#endif
+	return (TRUE);
+}
+
+static bool_t
+xdrmbuf_putbytes(
+	XDR *xdrs,
+	const char *addr,
+	u_int len )
+{
+
+	fprintf(stderr,"TODO: xdrmbuf_putbytes() is unimplemented\n");
+	return FALSE;
+#if 0
+	if ((xdrs->x_handy -= len) < 0)
+		return (FALSE);
+	memcpy(xdrs->x_private, addr, len);
+	xdrs->x_private += len;
+	return (TRUE);
+#endif
+}
+
+static u_int
+xdrmbuf_getpos(
+	XDR *xdrs)
+{
+#if 1
+MBPrivate	mbp  = (MBPrivate)xdrs->x_base;
+struct mbuf *m   = mbp->mcurrent;
+u_int       rval = mbp->pos;
+
+	if (m) {
+		rval += xdrs->x_private - mtod(m, void*);
+	}
+#else
+struct mbuf *m;
+u_int 		rval = 0;
+MBPrivate	mbp  = (MBPrivate)xdrs->x_base;
+
+	for ( m = mbp->mchain; m && m != mbp->mcurrent; m = m->m_next )
+		rval += m->m_len;
+	if (m) {
+		rval += (u_long)xdrs->x_private - mtod(m, u_long);
+	}
+
+#endif
+	return rval;
+}
+
+static bool_t
+xdrmbuf_setpos(
+	XDR *xdrs,
+	u_int pos)
+{
+struct		mbuf *m;
+MBPrivate	mbp   = (MBPrivate)xdrs->x_base;
+
+	if (pos >= mbp->pos) {
+		pos      -= mbp->pos;
+		m         = mbp->mcurrent;
+	} else {
+		m         = mbp->mchain;
+		mbp->pos  = 0;
+	}
+
+	while ( m && pos >= m->m_len ) {
+		pos      -= m->m_len;
+		mbp->pos += m->m_len;
+		m         = m->m_next;
+	}
+
+	if (m) {
+		xdrmbuf_setup(xdrs, m);
+		xdrs->x_private += pos;
+		return TRUE;
+	}
+
+	return 0 == pos ? TRUE : FALSE;
+}
+
+static int32_t *
+xdrmbuf_inline_aligned(
+	XDR *xdrs,
+	u_int len)
+{
+int32_t	*buf = 0;
+
+	if (xdrs->x_handy == 0 && !xdrmbuf_next(xdrs))
+		return 0;
+
+	if (xdrs->x_handy >= len) {
+		xdrs->x_handy -= len;
+		buf = (int32_t *) xdrs->x_private;
+		xdrs->x_private += len;
+#if DEBUG & DEBUG_VERB
+		fprintf(stderr,"Got %i aligned inline bytes at %x\n", len, buf);
+#endif
+	}
+#if DEBUG & DEBUG_VERB
+	else {
+		fprintf(stderr,"Skipped %i aligned inline bytes\n",len);
+	}
+#endif
+	return (buf);
+}
+
+static int32_t *
+xdrmbuf_inline_unaligned(
+	XDR *xdrs,
+	u_int len )
+{
+	return (0);
+}




More information about the vc mailing list