From 8f59c0aea7467a0dcfcf46138c3d1a422cb339fd Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Fri, 3 Aug 2012 14:21:13 -0500 Subject: nfsclient: Initial addition Will not compile until librpc is available. --- services/nfsclient/ChangeLog.slac | 112 ++ services/nfsclient/LICENSE | 44 + services/nfsclient/Makefile | 49 + services/nfsclient/README | 548 ++++++ services/nfsclient/cexphelp.c | 20 + services/nfsclient/dirutils.c | 383 ++++ services/nfsclient/include/librtemsNfs.h | 179 ++ services/nfsclient/include/rpcio.h | 208 ++ services/nfsclient/nfs.c | 3051 +++++++++++++++++++++++++++++ services/nfsclient/nfs.modini.c | 31 + services/nfsclient/nfsTest.c | 379 ++++ services/nfsclient/proto/mount_prot.h | 144 ++ services/nfsclient/proto/mount_prot.x | 161 ++ services/nfsclient/proto/mount_prot_xdr.c | 104 + services/nfsclient/proto/nfs_prot.h | 453 +++++ services/nfsclient/proto/nfs_prot.x | 1268 ++++++++++++ services/nfsclient/proto/nfs_prot_xdr.c | 621 ++++++ services/nfsclient/rfc1094.txt | 1258 ++++++++++++ services/nfsclient/rpcio.c | 1790 +++++++++++++++++ services/nfsclient/rpcio.modini.c | 19 + services/nfsclient/sock_mbuf.c | 281 +++ services/nfsclient/xdr_mbuf.c | 537 +++++ 22 files changed, 11640 insertions(+) create mode 100644 services/nfsclient/ChangeLog.slac create mode 100644 services/nfsclient/LICENSE create mode 100644 services/nfsclient/Makefile create mode 100644 services/nfsclient/README create mode 100644 services/nfsclient/cexphelp.c create mode 100644 services/nfsclient/dirutils.c create mode 100644 services/nfsclient/include/librtemsNfs.h create mode 100644 services/nfsclient/include/rpcio.h create mode 100644 services/nfsclient/nfs.c create mode 100644 services/nfsclient/nfs.modini.c create mode 100644 services/nfsclient/nfsTest.c create mode 100644 services/nfsclient/proto/mount_prot.h create mode 100644 services/nfsclient/proto/mount_prot.x create mode 100644 services/nfsclient/proto/mount_prot_xdr.c create mode 100644 services/nfsclient/proto/nfs_prot.h create mode 100644 services/nfsclient/proto/nfs_prot.x create mode 100644 services/nfsclient/proto/nfs_prot_xdr.c create mode 100644 services/nfsclient/rfc1094.txt create mode 100644 services/nfsclient/rpcio.c create mode 100644 services/nfsclient/rpcio.modini.c create mode 100644 services/nfsclient/sock_mbuf.c create mode 100644 services/nfsclient/xdr_mbuf.c 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 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 , 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 , 2002 + +Copyright 2002, Stanford University and + Till Straumann + +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): + + [ '.' '@' ] ':' + + 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 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: + + [ '.' '@' ] + + The 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 +#include +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, , 10/2002 */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann , 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 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include /* PATH_MAX */ + +#include /* 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 +#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 2002-2003 */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann , 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* RPCIO driver interface. + * If you need RPCIO for other purposes than NFS + * you may want to include +#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, , 2002 */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann , 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 +#endif + +#include +#include +#include +#include +#include + +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 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 , 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "rpcio.h" +#include "librtemsNfs.h" + +/* Configurable parameters */ + +/* Estimated average length of a filename (including terminating 0). + * This was calculated by doing + * + * find -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 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 + * ['.''@']':'" string and let + * pPath point to the 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 + * + * ':' + * + * 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,"\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 ':' */ + 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 , 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 , 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 +#include + +#include +#include +#include + +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= 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 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 + + +#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; + +/* + * The type name is used for arbitrary names (hostnames, groupnames) + */ +typedef string name; + +/* + * 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 + + +#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; +typedef string nfspath; + +/* + * 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; +}; + +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; +}; + +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; +}; + +/* + * 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 , "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 , "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; + + The type "filename" is used for passing file names or pathname + components. + +2.3.8. path + + typedef string path; + + 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 , "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; + + The type "dirpath" is a server pathname of a directory. + +A.4.4. name + + typedef string name; + + 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 + +Comment on RFC 1094 + + + +Comments about this RFC: + + * RFC 1094: I am preparing for doing a project in File System in + Network. can you specify... by Ravik (12/4/2003) + + +Previous: RFC 1093 - NSFNET routing architecture + + + +Next: RFC 1095 - Common Management Information Services and Protocol +over TCP/IP (CMOT) + + + +------------------------------------------------------------------------ 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 , 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 , 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; isin_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 + +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 +/* 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, + * 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 +#include +#include + +#include +#include +#include + +#define _KERNEL +#define __BSD_VISIBLE 1 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 , 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 +#include +#include +#include + +#include + +#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 + +#include + +#if DEBUG & DEBUG_VERB || defined(TODO) +#include +#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; iim_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); +} -- cgit v1.2.3