summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@oarcorp.com>2012-08-03 14:21:13 -0500
committerJoel Sherrill <joel.sherrill@oarcorp.com>2012-08-03 14:21:13 -0500
commit8f59c0aea7467a0dcfcf46138c3d1a422cb339fd (patch)
tree779a3e1b57c502fad517f839148aad4dde849bf2
parentlibrpc: Initial addition (diff)
downloadrtems-libbsd-8f59c0aea7467a0dcfcf46138c3d1a422cb339fd.tar.bz2
nfsclient: Initial addition
Will not compile until librpc is available.
-rw-r--r--services/nfsclient/ChangeLog.slac112
-rw-r--r--services/nfsclient/LICENSE44
-rw-r--r--services/nfsclient/Makefile49
-rw-r--r--services/nfsclient/README548
-rw-r--r--services/nfsclient/cexphelp.c20
-rw-r--r--services/nfsclient/dirutils.c383
-rw-r--r--services/nfsclient/include/librtemsNfs.h179
-rw-r--r--services/nfsclient/include/rpcio.h208
-rw-r--r--services/nfsclient/nfs.c3051
-rw-r--r--services/nfsclient/nfs.modini.c31
-rw-r--r--services/nfsclient/nfsTest.c379
-rw-r--r--services/nfsclient/proto/mount_prot.h144
-rw-r--r--services/nfsclient/proto/mount_prot.x161
-rw-r--r--services/nfsclient/proto/mount_prot_xdr.c104
-rw-r--r--services/nfsclient/proto/nfs_prot.h453
-rw-r--r--services/nfsclient/proto/nfs_prot.x1268
-rw-r--r--services/nfsclient/proto/nfs_prot_xdr.c621
-rw-r--r--services/nfsclient/rfc1094.txt1258
-rw-r--r--services/nfsclient/rpcio.c1790
-rw-r--r--services/nfsclient/rpcio.modini.c19
-rw-r--r--services/nfsclient/sock_mbuf.c281
-rw-r--r--services/nfsclient/xdr_mbuf.c537
22 files changed, 11640 insertions, 0 deletions
diff --git a/services/nfsclient/ChangeLog.slac b/services/nfsclient/ChangeLog.slac
new file mode 100644
index 00000000..94dea067
--- /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@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 00000000..4687f9a2
--- /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@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 00000000..a7fbfe0a
--- /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 00000000..944b830e
--- /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@slac.stanford.edu>, 2002
+
+Copyright 2002, Stanford University and
+ Till Straumann <strauman@slac.stanford.edu>
+
+Stanford Notice
+***************
+
+Acknowledgement of sponsorship
+* * * * * * * * * * * * * * * *
+This software was produced by the Stanford Linear Accelerator Center,
+Stanford University, under Contract DE-AC03-76SFO0515 with the Department
+of Energy.
+
+
+Contents
+--------
+I Overview
+ 1) Performance
+ 2) Reference Platform / Test Environment
+
+II Usage
+ 1) Initialization
+ 2) Mounting Remote Server Filesystems
+ 3) Unmounting
+ 4) Unloading
+ 5) Dumping Information / Statistics
+
+III Implementation Details
+ 1) RPCIOD
+ 2) NFS
+ 3) RTEMS Resources Used By NFS/RPCIOD
+ 4) Caveats & Bugs
+
+IV Licensing & Disclaimers
+
+I Overview
+-----------
+
+This package implements a simple non-caching NFS
+client for RTEMS. Most of the system calls are
+supported with the exception of 'mount', i.e. it
+is not possible to mount another FS on top of NFS
+(mostly because of the difficulty that arises when
+mount points are deleted on the server). It
+shouldn't be hard to do, though.
+
+Note: this client supports NFS vers. 2 / MOUNT vers. 1;
+ NFS Version 3 or higher are NOT supported.
+
+The package consists of two modules: RPCIOD and NFS
+itself.
+
+ - RPCIOD is a UDP/RPC multiplexor daemon. It takes
+ RPC requests from multiple local client threads,
+ funnels them through a single socket to multiple
+ servers and dispatches the replies back to the
+ (blocked) requestor threads.
+ RPCIOD does packet retransmission and handles
+ timeouts etc.
+ Note however, that it does NOT do any XDR
+ marshalling - it is up to the requestor threads
+ to do the XDR encoding/decoding. RPCIOD _is_ RPC
+ specific, though, because its message dispatching
+ is based on the RPC transaction ID.
+
+ - The NFS package maps RTEMS filesystem calls
+ to proper RPCs, it does the XDR work and
+ hands marshalled RPC requests to RPCIOD.
+ All of the calls are synchronous, i.e. they
+ block until they get a reply.
+
+1) Performance
+- - - - - - - -
+Performance sucks (due to the lack of
+readahead/delayed write and caching). On a fast
+(100Mb/s) ethernet, it takes about 20s to copy a
+10MB file from NFS to NFS. I found, however, that
+vxWorks' NFS client doesn't seem to be any
+faster...
+
+Since there is no buffer cache with read-ahead
+implemented, all NFS reads are synchronous RPC
+calls. Every read operation involves sending a
+request and waiting for the reply. As long as the
+overhead (sending request + processing it on the
+server) is significant compared to the time it
+takes to transferring the actual data, increasing
+the amount of data per request results in better
+throughput. The UDP packet size limit imposes a
+limit of 8k per RPC call, hence reading from NFS
+in chunks of 8k is better than chunks of 1k [but
+chunks >8k are not possible, i.e., simply not
+honoured: read(a_nfs_fd, buf, 20000) returns
+8192]. This is similar to the old linux days
+(mount with rsize=8k). You can let stdio take
+care of the buffering or use 8k buffers with
+explicit read(2) operations. Note that stdio
+honours the file-system's st_blksize field
+if newlib is compiled with HAVE_BLKSIZE defined.
+In this case, stdio uses 8k buffers for files
+on NFS transparently. The blocksize NFS
+reports can be tuned with a global variable
+setting (see nfs.c for details).
+
+Further increase of throughput can be achieved
+with read-ahead (issuing RPC calls in parallel
+[send out request for block n+1 while you are
+waiting for data of block n to arrive]). Since
+this is not handled by the file system itself, you
+would have to code this yourself e.g., using
+parallel threads to read from a single file from
+interleaved offsets.
+
+Another obvious improvement can be achieved if
+processing the data takes a significant amount of
+time. Then, having a pipeline of threads for
+reading data and processing them makes sense
+[thread b processes chunk n while thread a blocks
+in read(chunk n+1)].
+
+Some performance figures:
+Software: src/nfsTest.c:nfsReadTest() [data not
+ processed in any way].
+Hardware: MVME6100
+Network: 100baseT-FD
+Server: Linux-2.6/RHEL4-smp [dell precision 420]
+File: 10MB
+
+Results:
+Single threaded ('normal') NFS read, 1k buffers: 3.46s (2.89MB/s)
+Single threaded ('normal') NFS read, 8k buffers: 1.31s (7.63MB/s)
+Multi threaded; 2 readers, 8k buffers/xfers: 1.12s (8.9 MB/s)
+Multi threaded; 3 readers, 8k buffers/xfers: 1.04s (9.6 MB/s)
+
+2) Reference Platform
+- - - - - - - - - - -
+RTEMS-NFS was developed and tested on
+
+ o RTEMS-ss20020301 (local patches applied)
+ o PowerPC G3, G4 on Synergy SVGM series board
+ (custom 'SVGM' BSP, to be released soon)
+ o PowerPC 604 on MVME23xx
+ (powerpc/shared/motorola-powerpc BSP)
+ o Test Environment:
+ - RTEMS executable running CEXP
+ - rpciod/nfs dynamically loaded from TFTPfs
+ - EPICS application dynamically loaded from NFS;
+ the executing IOC accesses all of its files
+ on NFS.
+
+II Usage
+---------
+
+After linking into the system and proper initialization
+(rtems-NFS supports 'magic' module initialization when
+loaded into a running system with the CEXP loader),
+you are ready for mounting NFSes from a server
+(I avoid the term NFS filesystem because NFS already
+stands for 'Network File System').
+
+You should also read the
+
+ - "RTEMS Resources Used By NFS/RPCIOD"
+ - "CAVEATS & BUGS"
+
+below.
+
+1) Initialization
+- - - - - - - - -
+NFS consists of two modules who must be initialized:
+
+ a) the RPCIO daemon package; by calling
+
+ rpcUdpInit();
+
+ note that this step must be performed prior to
+ initializing NFS:
+
+ b) NFS is initialized by calling
+
+ nfsInit( smallPoolDepth, bigPoolDepth );
+
+ if you supply 0 (zero) values for the pool
+ depths, the compile-time default configuration
+ is used which should work fine.
+
+NOTE: when using CEXP to load these modules into a
+running system, initialization will be performed
+automagically.
+
+2) Mounting Remote Server Filesystems
+- - - - - - - - - - - - - - - - - - -
+
+There are two interfaces for mounting an NFS:
+
+ - The (non-POSIX) RTEMS 'mount()' call:
+
+ mount( &mount_table_entry_pointer,
+ &filesystem_operations_table_pointer,
+ options,
+ device,
+ mount_point )
+
+ Note that you must specify a 'mount_table_entry_pointer'
+ (use a dummy) - RTEMS' mount() doesn't grok a NULL for
+ the first argument.
+
+ o for the 'filesystem_operations_table_pointer', supply
+
+ &nfs_fs_ops
+
+ o options are constants (see RTEMS headers) for specifying
+ read-only / read-write mounts.
+
+ o the 'device' string specifies the remote filesystem
+ who is to be mounted. NFS expects a string conforming
+ to the following format (EBNF syntax):
+
+ [ <uid> '.' <gid> '@' ] <hostip> ':' <path>
+
+ The first optional part of the string allows you
+ to specify the credentials to be used for all
+ subsequent transactions with this server. If the
+ string is omitted, the EUID/EGID of the executing
+ thread (i.e. the thread performing the 'mount' -
+ NFS will still 'remember' these values and use them
+ for all future communication with this server).
+
+ The <hostip> part denotes the server IP address
+ in standard 'dot' notation. It is followed by
+ a colon and the (absolute) path on the server.
+ Note that no extra characters or whitespace must
+ be present in the string. Example 'device' strings
+ are:
+
+ "300.99@192.168.44.3:/remote/rtems/root"
+
+ "192.168.44.3:/remote/rtems/root"
+
+ o the 'mount_point' string identifies the local
+ directory (most probably on IMFS) where the NFS
+ is to be mounted. Note that the mount point must
+ already exist with proper permissions.
+
+ - Alternate 'mount' interface. NFS offers a more
+ convenient wrapper taking three string arguments:
+
+ nfsMount(uidgid_at_host, server_path, mount_point)
+
+ This interface does DNS lookup (see reentrancy note
+ below) and creates the mount point if necessary.
+
+ o the first argument specifies the server and
+ optionally the uid/gid to be used for authentication.
+ The semantics are exactly as described above:
+
+ [ <uid> '.' <gid> '@' ] <host>
+
+ The <host> part may be either a host _name_ or
+ an IP address in 'dot' notation. In the former
+ case, nfsMount() uses 'gethostbyname()' to do
+ a DNS lookup.
+
+ IMPORTANT NOTE: gethostbyname() is NOT reentrant/
+ thread-safe and 'nfsMount()' (if not provided with an
+ IP/dot address string) is hence subject to race conditions.
+
+ o the 'server_path' and 'mount_point' arguments
+ are described above.
+ NOTE: If the mount point does not exist yet,
+ nfsMount() tries to create it.
+
+ o if nfsMount() is called with a NULL 'uidgid_at_host'
+ argument, it lists all currently mounted NFS
+
+3) Unmounting
+- - - - - - -
+An NFS can be unmounted using RTEMS 'unmount()'
+call (yep, it is unmount() - not umount()):
+
+ unmount(mount_point)
+
+Note that you _must_ supply the mount point (string
+argument). It is _not_ possible to specify the
+'mountee' when unmounting. NFS implements no
+convenience wrapper for this (yet), essentially because
+(although this sounds unbelievable) it is non-trivial
+to lookup the path leading to an RTEMS filesystem
+directory node.
+
+4) Unloading
+- - - - - - -
+After unmounting all NFS from the system, the NFS
+and RPCIOD modules may be stopped and unloaded.
+Just call 'nfsCleanup()' and 'rpcUdpCleanup()'
+in this order. You should evaluate the return value
+of these routines which is non-zero if either
+of them refuses to yield (e.g. because there are
+still mounted filesystems).
+Again, when unloading is done by CEXP this is
+transparently handled.
+
+5) Dumping Information / Statistics
+- - - - - - - - - - - - - - - - - -
+
+Rudimentary RPCIOD statistics are printed
+to a file (stdout when NULL) by
+
+ int rpcUdpStats(FILE *f)
+
+A list of all currently mounted NFS can be
+printed to a file (stdout if NULL) using
+
+ int nfsMountsShow(FILE *f)
+
+For convenience, this routine is also called
+by nfsMount() when supplying NULL arguments.
+
+III Implementation Details
+--------------------------
+
+1) RPCIOD
+- - - - -
+
+RPCIOD was created to
+
+a) avoid non-reentrant librpc calls.
+b) support 'asynchronous' operation over a single
+ socket.
+
+RPCIOD is a daemon thread handling 'transaction objects'
+(XACTs) through an UDP socket. XACTs are marshalled RPC
+calls/replies associated with RPC servers and requestor
+threads.
+
+requestor thread: network:
+
+ XACT packet
+ | |
+ V V
+ | message queue | ( socket )
+ | | ^
+ ----------> <----- | |
+ RPCIOD |
+ / --------------
+ timeout/ (re) transmission
+
+
+A requestor thread drops a transaction into
+the message queue and goes to sleep. The XACT is
+picked up by rpciod who is listening for events from
+three sources:
+
+ o the request queue
+ o packet arrival at the socket
+ o timeouts
+
+RPCIOD sends the XACT to its destination server and
+enqueues the pending XACT into an ordered list of
+outstanding transactions.
+
+When a packet arrives, RPCIOD (based on the RPC transaction
+ID) looks up the matching XACT and wakes up the requestor
+who can then XDR-decode the RPC results found in the XACT
+object's buffer.
+
+When a timeout expires, RPCIOD examines the outstanding
+XACT that is responsible for the timeout. If its lifetime
+has not expired yet, RPCIOD resends the request. Otherwise,
+the XACT's error status is set and the requestor is woken up.
+
+RPCIOD dynamically adjusts the retransmission intervals
+based on the average round-trip time measured (on a per-server
+basis).
+
+Having the requestors event driven (rather than blocking
+e.g. on a semaphore) is geared to having many different
+requestors (one synchronization object per requestor would
+be needed otherwise).
+
+Requestors who want to do asynchronous IO need a different
+interface which will be added in the future.
+
+1.a) Reentrancy
+- - - - - - - -
+RPCIOD does no non-reentrant librpc calls.
+
+1.b) Efficiency
+- - - - - - - -
+We shouldn't bother about efficiency until pipelining (read-ahead/
+delayed write) and caching are implemented. The round-trip delay
+associated with every single RPC transaction clearly is a big
+performance killer.
+
+Nevertheless, I could not withstand the temptation to eliminate
+the extra copy step involved with socket IO:
+
+A user data object has to be XDR encoded into a buffer. The
+buffer given to the socket where it is copied into MBUFs.
+(The network chip driver might even do more copying).
+
+Likewise, on reception 'recvfrom' copies MBUFS into a user
+buffer which is XDR decoded into the final user data object.
+
+Eliminating the copying into (possibly multiple) MBUFS by
+'sendto()' is actually a piece of cake. RPCIOD uses the
+'sosend()' routine [properly wrapped] supplying a single
+MBUF header who directly points to the marshalled buffer
+:-)
+
+Getting rid of the extra copy on reception was (only a little)
+harder: I derived a 'XDR-mbuf' stream from SUN's xdr_mem which
+allows for XDR-decoding out of a MBUF chain who is obtained by
+soreceive().
+
+2) NFS
+- - - -
+The actual NFS implementation is straightforward and essentially
+'passive' (no threads created). Any RTEMS task executing a
+filesystem call dispatched to NFS (such as 'opendir()', 'lseek()'
+or 'unlink()') ends up XDR encoding arguments, dropping a
+XACT into RPCIOD's message queue and going to sleep.
+When woken up by RPCIOD, the XACT is decoded (using the XDR-mbuf
+stream mentioned above) and the properly cooked-up results are
+returned.
+
+3) RTEMS Resources Used By NFS/RPCIOD
+- - - - - - - - - - - - - - - - - - -
+
+The RPCIOD/NFS package uses the following resources. Some
+parameters are compile-time configurable - consult the
+source files for details.
+
+RPCIOD:
+ o 1 task
+ o 1 message queue
+ o 1 socket/filedescriptor
+ o 2 semaphores (a third one is temporarily created during
+ rpcUdpCleanup()).
+ o 1 RTEMS EVENT (by default RTEMS_EVENT_30).
+ IMPORTANT: this event is used by _every_ thread executing
+ NFS system calls and hence is RESERVED.
+ o 3 events only used by RPCIOD itself, i.e. these must not
+ be sent to RPCIOD by no other thread (except for the intended
+ use, of course). The events involved are 1,2,3.
+ o preemption disabled sections: NONE
+ o sections with interrupts disabled: NONE
+ o NO 'timers' are used (timer code would run in IRQ context)
+ o memory usage: n.a
+
+NFS:
+ o 2 message queues
+ o 2 semaphores
+ o 1 semaphore per mounted NFS
+ o 1 slot in driver entry table (for major number)
+ o preemption disabled sections: NONE
+ o sections with interrupts disabled: NONE
+ o 1 task + 1 semaphore temporarily created when
+ listing mounted filesystems (rtems_filesystem_resolve_location())
+
+4) CAVEATS & BUGS
+- - - - - - - - -
+Unfortunately, some bugs crawl around in the filesystem generics.
+(Some of them might already be fixed in versions later than
+rtems-ss-20020301).
+I recommend to use the patch distributed with RTEMS-NFS.
+
+ o RTEMS uses/used (Joel said it has been fixed already) a 'short'
+ ino_t which is not enough for NFS.
+ The driver detects this problem and enables a workaround. In rare
+ situations (mainly involving 'getcwd()' improper inode comparison
+ may result (due to the restricted size, stat() returns st_ino modulo
+ 2^16). In most cases, however, st_dev is compared along with st_ino
+ which will give correct results (different files may yield identical
+ st_ino but they will have different st_dev). However, there is
+ code (in getcwd(), for example) who assumes that files residing
+ in one directory must be hosted by the same device and hence omits
+ the st_dev comparison. In such a case, the workaround will fail.
+
+ NOTE: changing the size (sys/types.h) of ino_t from 'short' to 'long'
+ is strongly recommended. It is NOT included in the patch, however
+ as this is a major change requiring ALL of your sources to
+ be recompiled.
+
+ THE ino_t SIZE IS FIXED IN GCC-3.2/NEWLIB-1.10.0-2 DISTRIBUTED BY
+ OAR.
+
+ o You may work around most filesystem bugs by observing the following
+ rules:
+
+ * never use chroot() (fixed by the patch)
+ * never use getpwent(), getgrent() & friends - they are NOT THREAD
+ safe (fixed by the patch)
+ * NEVER use rtems_libio_share_private_env() - not even with the
+ patch applied. Just DONT - it is broken by design.
+ * All threads who have their own userenv (who have called
+ rtems_libio_set_private_env()) SHOULD 'chdir("/")' before
+ terminating. Otherwise, (i.e. if their cwd is on NFS), it will
+ be impossible to unmount the NFS involved.
+
+ o The patch slightly changes the semantics of 'getpwent()' and
+ 'getgrent()' & friends (to what is IMHO correct anyways - the patch is
+ also needed to fix another problem, however): with the patch applied,
+ the passwd and group files are always accessed from the 'current' user
+ environment, i.e. a thread who has changed its 'root' or 'uid' might
+ not be able to access these files anymore.
+
+ o NOTE: RTEMS 'mount()' / 'unmount()' are NOT THREAD SAFE.
+
+ o The NFS protocol has no 'append' or 'seek_end' primitive. The client
+ must query the current file size (this client uses cached info) and
+ change the local file pointer accordingly (in 'O_APPEND' mode).
+ Obviously, this involves a race condition and hence multiple clients
+ writing the same file may lead to corruption.
+
+IV Licensing & Disclaimers
+--------------------------
+
+NFS is distributed under the SLAC License - consult the
+separate 'LICENSE' file.
+
+Government disclaimer of liability
+- - - - - - - - - - - - - - - - -
+Neither the United States nor the United States Department of Energy,
+nor any of their employees, makes any warranty, express or implied,
+or assumes any legal liability or responsibility for the accuracy,
+completeness, or usefulness of any data, apparatus, product, or process
+disclosed, or represents that its use would not infringe privately
+owned rights.
+
+Stanford disclaimer of liability
+- - - - - - - - - - - - - - - - -
+Stanford University makes no representations or warranties, express or
+implied, nor assumes any liability for the use of this software.
+
+Maintenance of notice
+- - - - - - - - - - -
+In the interest of clarity regarding the origin and status of this
+software, Stanford University requests that any recipient of it maintain
+this notice affixed to any distribution by the recipient that contains a
+copy or derivative of this software.
diff --git a/services/nfsclient/cexphelp.c b/services/nfsclient/cexphelp.c
new file mode 100644
index 00000000..d0406ad3
--- /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 00000000..357aab96
--- /dev/null
+++ b/services/nfsclient/dirutils.c
@@ -0,0 +1,383 @@
+/* very crude and basic fs utilities for testing the NFS */
+
+/* Till Straumann, <strauman@slac.stanford.edu>, 10/2002 */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The NFS-2 client implementation for RTEMS was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#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 00000000..fc480287
--- /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@slac.stanford.edu> 2002-2003 */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The NFS-2 client implementation for RTEMS was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#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 00000000..80782096
--- /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@slac.stanford.edu>, 2002 */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The NFS-2 client implementation for RTEMS was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#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 00000000..bbec6b80
--- /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@slac.stanford.edu> 2002 */
+
+/*
+ * Hacked on by others.
+ *
+ * Modifications to support reference counting in the file system are
+ * Copyright (c) 2012 embedded brains GmbH.
+ */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The NFS-2 client implementation for RTEMS was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/libio_.h>
+#include <rtems/seterr.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <nfs_prot.h>
+#include <mount_prot.h>
+
+#include "rpcio.h"
+#include "librtemsNfs.h"
+
+/* Configurable parameters */
+
+/* Estimated average length of a filename (including terminating 0).
+ * This was calculated by doing
+ *
+ * find <some root> -print -exec basename '{}' \; > feil
+ * wc feil
+ *
+ * AVG_NAMLEN = (num_chars + num_lines)/num_lines
+ */
+#define CONFIG_AVG_NAMLEN 10
+
+#define CONFIG_NFS_SMALL_XACT_SIZE 800 /* size of RPC arguments for non-write ops */
+/* lifetime of NFS attributes in a NfsNode;
+ * the time is in seconds and the lifetime is
+ * infinite if the symbol is #undef
+ */
+#define CONFIG_ATTR_LIFETIME 10/*secs*/
+
+/*
+ * The 'st_blksize' (stat(2)) value this nfs
+ * client should report. If set to zero then the server's fattr data
+ * is passed throught which is not necessary optimal.
+ * Newlib's stdio uses 'st_blksize' (if built with HAVE_BLKSIZE defined)
+ * to size the default buffer.
+ * Due to the overhead of NFS it is probably better to use the maximum
+ * size of an NFS read request (8k) rather than the optimal block
+ * size on the server.
+ * This value can be overridden at run-time by setting the global
+ * variable 'nfsStBlksize'.
+ * Thanks to Steven Johnson <sjohnson@sakuraindustries.com> for helping
+ * working on this issue.
+ */
+#define DEFAULT_NFS_ST_BLKSIZE NFS_MAXDATA
+
+/* dont change this without changing the maximal write size */
+#define CONFIG_NFS_BIG_XACT_SIZE UDPMSGSIZE /* dont change this */
+
+/* The real values for these are specified further down */
+#define NFSCALL_TIMEOUT (&_nfscalltimeout)
+#define MNTCALL_TIMEOUT (&_nfscalltimeout)
+static struct timeval _nfscalltimeout = { 10, 0 }; /* {secs, us } */
+
+/* More or less fixed constants; in particular, NFS3 is not supported */
+#define DELIM '/'
+#define HOSTDELIM ':'
+#define UPDIR ".."
+#define UIDSEP '@'
+#define NFS_VERSION_2 NFS_VERSION
+
+/* we use a dynamically assigned major number */
+#define NFS_MAJOR (nfsGlob.nfs_major)
+
+
+/* NOTE: RTEMS (ss-20020301) uses a 'short st_ino' type :-( but the
+ * NFS fileid is 32 bit. [Later versions of RTEMS have fixed this;
+ * nfsInit() issues a warning if you run a version with 'short st_ino'.]
+ *
+ * As a workarount, we merge the upper 16bits of the fileid into the
+ * minor device no. Hence, it is still possible to uniquely identify
+ * a file by looking at its device number (major = nfs, minor = part
+ * of the fileid + our 'nfs-id' identifier).
+ *
+ * This has an impact on performance, as e.g. getcwd() stats() all
+ * directory entries when it believes it has crossed a mount point
+ * (a.st_dev != b.st_dev).
+ *
+ * OTOH, it also might cause node comparison failure! E.g. 'getcwd()'
+ * assumes that two nodes residing in the same directory must be located
+ * on the same device and hence compares 'st_ino' only.
+ * If two files in the same directory have the same inode number
+ * modulo 2^16, they will be considered equal (although their device
+ * number doesn't match - getcwd doesn't look at it).
+ *
+ * Other software might or might not be affected.
+ *
+ * The only clean solution to this problem is bumping up the size of
+ * 'ino_t' at least to 'long'.
+ * Note that this requires _all_ software (libraries etc.) to be
+ * recompiled.
+ */
+
+#define NFS_MAKE_DEV_T_INO_HACK(node) \
+ rtems_filesystem_make_dev_t( NFS_MAJOR, \
+ (((rtems_device_minor_number)((node)->nfs->id))<<16) | (((rtems_device_minor_number)SERP_ATTR((node)).fileid) >> 16) )
+
+/* use our 'nfs id' and the server's fsid for the minor device number
+ * this should be fairly unique
+ */
+#define NFS_MAKE_DEV_T(node) \
+ rtems_filesystem_make_dev_t( NFS_MAJOR, \
+ (((rtems_device_minor_number)((node)->nfs->id))<<16) | (SERP_ATTR((node)).fsid & (((rtems_device_minor_number)1<<16)-1)) )
+
+#define DIRENT_HEADER_SIZE ( sizeof(struct dirent) - \
+ sizeof( ((struct dirent *)0)->d_name ) )
+
+
+/* debugging flags */
+#define DEBUG_COUNT_NODES (1<<0)
+#define DEBUG_TRACK_NODES (1<<1)
+#define DEBUG_EVALPATH (1<<2)
+#define DEBUG_READDIR (1<<3)
+#define DEBUG_SYSCALLS (1<<4)
+
+/* #define DEBUG ( DEBUG_SYSCALLS | DEBUG_COUNT_NODES ) */
+
+#ifdef DEBUG
+#define STATIC
+#else
+#define STATIC static
+#endif
+
+#define MUTEX_ATTRIBUTES (RTEMS_LOCAL | \
+ RTEMS_PRIORITY | \
+ RTEMS_INHERIT_PRIORITY | \
+ RTEMS_BINARY_SEMAPHORE)
+
+#define LOCK(s) do { \
+ rtems_semaphore_obtain((s), \
+ RTEMS_WAIT, \
+ RTEMS_NO_TIMEOUT); \
+ } while (0)
+
+#define UNLOCK(s) do { rtems_semaphore_release((s)); \
+ } while (0)
+
+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 00000000..22095cf5
--- /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 00000000..18bd0a3c
--- /dev/null
+++ b/services/nfsclient/nfsTest.c
@@ -0,0 +1,379 @@
+/* Test program for evaluating NFS read throughput */
+
+/* Author: Till Straumann <strauman@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@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 00000000..1cde517a
--- /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 00000000..7e0d7f3a
--- /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 00000000..b439ef34
--- /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 00000000..de812dbf
--- /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 00000000..a40d9a5f
--- /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 00000000..cde005e2
--- /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 00000000..7ad0f737
--- /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@SUN.COM <mailto:nowicki@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 00000000..eeadd53f
--- /dev/null
+++ b/services/nfsclient/rpcio.c
@@ -0,0 +1,1790 @@
+/* RPC multiplexor for a multitasking environment */
+
+/* Author: Till Straumann <strauman@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@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 00000000..7aa802fe
--- /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 00000000..e07c63bc
--- /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@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 00000000..3c736397
--- /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@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);
+}