diff options
Diffstat (limited to 'cpukit/libfs/src/nfsclient/src')
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/cexphelp.c | 20 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/dirutils.c | 385 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/librtemsNfs.h | 180 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/nfs.c | 3354 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/nfs.modini.c | 31 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/nfsTest.c | 381 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/rpcio.c | 1792 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/rpcio.h | 209 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/rpcio.modini.c | 19 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/sock_mbuf.c | 283 | ||||
-rw-r--r-- | cpukit/libfs/src/nfsclient/src/xdr_mbuf.c | 539 |
11 files changed, 7193 insertions, 0 deletions
diff --git a/cpukit/libfs/src/nfsclient/src/cexphelp.c b/cpukit/libfs/src/nfsclient/src/cexphelp.c new file mode 100644 index 0000000000..d0406ad33a --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/cexphelp.c @@ -0,0 +1,20 @@ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <librtemsNfs.h> +#include <cexpHelp.h> +CEXP_HELP_TAB_BEGIN(rtemsNfs) + HELP( +"Mount a remote filesystem (NFS). The mount point (must not be a NFS dir)\n" +"is created on the fly if not existing already.\n" +"uid/gid to use may be specified:\n" +" hostspec: [uid.gid@]hostname_or_ipaddr\n" + , int, nfsMount, (char *hostspec, char *exportdir, char *mntpoint) + ), + HELP( +"Print all currently mounted NFS directories to open file handle.\n" +"Pass f = 0 to print to stdout\n" + , int, nfsMountsShow, (FILE *f) + ), +CEXP_HELP_TAB_END diff --git a/cpukit/libfs/src/nfsclient/src/dirutils.c b/cpukit/libfs/src/nfsclient/src/dirutils.c new file mode 100644 index 0000000000..99c65876f3 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/dirutils.c @@ -0,0 +1,385 @@ +/* $Id$ */ + +/* very crude and basic fs utilities for testing the NFS */ + +/* Till Straumann, <strauman@slac.stanford.edu>, 10/2002 */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __vxworks +#include <vxWorks.h> +#endif +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <limits.h> /* PATH_MAX */ + +#include <inttypes.h> /* PRI* */ + +#if SIZEOF_MODE_T == 8 +#define PRIomode_t PRIo64 +#elif SIZEOF_MODE_T == 4 +#define PRIomode_t PRIo32 +#else +#error "unsupport size of mode_t" +#endif + +#if SIZEOF_OFF_T == 8 +#define PRIdoff_t PRIo64 +#elif SIZEOF_OFF_T == 4 +#define PRIdoff_t PRIo32 +#else +#error "unsupported size of off_t" +#endif + +#ifdef HAVE_CEXP +#include <cexpHelp.h> +#endif + +#ifndef __vxworks +int +pwd(void) +{ +char buf[PATH_MAX]; + + if ( !getcwd(buf,PATH_MAX)) { + perror("getcwd"); + return -1; + } else { + printf("%s\n",buf); + } + return 0; +} + +static int +ls_r(char *path, char *chpt, char *name, struct stat *buf) +{ +char *t; + sprintf(chpt, "/%s", name); + if (lstat(path,buf)) { + fprintf(stderr,"stat(%s): %s\n", path, strerror(errno)); + return -1; + } + switch ( buf->st_mode & S_IFMT ) { + case S_IFSOCK: + case S_IFIFO: t = "|"; break; + + default: + case S_IFREG: + case S_IFBLK: + case S_IFCHR: + t = ""; break; + case S_IFDIR: + t = "/"; break; + case S_IFLNK: + t = "@"; break; + } + + printf("%10li, %10" PRIdoff_t "b, %5i.%-5i 0%04" PRIomode_t " %s%s\n", + buf->st_ino, + buf->st_size, + buf->st_uid, + buf->st_gid, + buf->st_mode & ~S_IFMT, + name, + t); + *chpt = 0; + return 0; +} + +int +ls(char *dir, char *opts) +{ +struct dirent *de; +char path[PATH_MAX+1]; +char *chpt; +DIR *dp = 0; +int rval = -1; +struct stat buf; + + if ( !dir ) + dir = "."; + + strncpy(path, dir, PATH_MAX); + path[PATH_MAX] = 0; + chpt = path+strlen(path); + + if ( !(dp=opendir(dir)) ) { + perror("opendir"); + goto cleanup; + } + + while ( (de = readdir(dp)) ) { + ls_r(path, chpt, de->d_name, &buf); + } + + rval = 0; + +cleanup: + if (dp) + closedir(dp); + return rval; +} +#endif + +#if 0 + fprintf(stderr, "usage: cp(""from"",[""to""[,""-f""]]\n"); + fprintf(stderr, " ""to""==NULL -> stdout\n"); + fprintf(stderr, " ""-f"" -> overwrite existing file\n"); +#endif + +int +cp(char *from, char *to, char *opts) +{ +struct stat st; +int rval = -1; +int fd = -1; +FILE *fst = 0; +FILE *tst = 0; +int flags = O_CREAT | O_WRONLY | O_TRUNC | O_EXCL; + + if (from) { + + if ((fd=open(from,O_RDONLY,0)) < 0) { + fprintf(stderr, + "Opening %s for reading: %s\n", + from, + strerror(errno)); + goto cleanup; + } + + if (fstat(fd, &st)) { + fprintf(stderr, + "rstat(%s): %s\n", + from, + strerror(errno)); + goto cleanup; + } + + + if (!S_ISREG(st.st_mode)) { + fprintf(stderr,"Refuse to copy a non-regular file\n"); + errno = EINVAL; + goto cleanup; + } + /* Now create a stream -- I experienced occasional weirdness + * when circumventing the streams attached to fildno(stdin) + * by reading/writing to the underlying fd's directly -> + * for now we always go through buffered I/O... + */ + if ( !(fst=fdopen(fd,"r")) ) { + fprintf(stderr, + "Opening input stream [fdopen()] failed: %s\n", + strerror(errno)); + goto cleanup; + } + /* at this point, we have a stream and don't need 'fd' anymore */ + fd = -1; + + } else { + fst = stdin; + st.st_mode = 0644; + } + + if (opts && strchr(opts,'f')) + flags &= ~ O_EXCL; + + if (to) { + if ( (fd=open(to,flags,st.st_mode)) < 0 ) { + fprintf(stderr, + "Opening %s for writing: %s\n", + to, + strerror(errno)); + goto cleanup; + } + if ( !(tst=fdopen(fd, "w")) ) { + fprintf(stderr, + "Opening output stream [fdopen()] failed: %s\n", + strerror(errno)); + goto cleanup; + } + /* at this point we have a stream and don't need 'fd' anymore */ + fd = -1; + } else { + tst = stdout; + } + + /* clear old errors */ + clearerr(fst); + clearerr(tst); + + /* use macro versions on register vars; stdio is already buffered, + * there's nothing to be gained by reading/writing blocks into + * a secondary buffer... + */ + { + register int ch; + register FILE *f = fst; + register FILE *t = tst; + while ( EOF != (ch = getc(f)) && EOF != putc(ch, t) ) + /* nothing else */; + } + + if ( ferror(fst) ) { + fprintf(stderr,"Read error: %s\n",strerror(errno)); + goto cleanup; + } + if ( ferror(tst) ) { + fprintf(stderr,"Write error: %s\n",strerror(errno)); + goto cleanup; + } + + rval = 0; + +cleanup: + + if ( fd >= 0 ) + close(fd); + + if ( fst ) { + if ( from ) + fclose(fst); + else + clearerr(fst); + } + if ( tst ) { + if ( to ) + fclose(tst); + else { + /* flush stdout */ + fflush(tst); + clearerr(tst); + } + } + + return rval; +} + +int +ln(char *to, char *name, char *opts) +{ + if (!to) { + fprintf(stderr,"ln: need 'to' argument\n"); + return -1; + } + if (!name) { + if ( !(name = strrchr(to,'/')) ) { + fprintf(stderr, + "ln: 'unable to link %s to %s\n", + to,to); + return -1; + } + name++; + } + if (opts || strchr(opts,'s')) { + if (symlink(name,to)) { + fprintf(stderr,"symlink: %s\n",strerror(errno)); + return -1; + } + } else { + if (link(name,to)) { + fprintf(stderr,"hardlink: %s\n",strerror(errno)); + return -1; + } + } + return 0; +} + +int +rm(char *path) +{ + return unlink(path); +} + +int +cd(char *path) +{ + return chdir(path); +} + +#ifdef HAVE_CEXP +static CexpHelpTabRec _cexpHelpTabDirutils[] __attribute__((unused)) = { + HELP( +"copy a file: cp(""from"",[""to""[,""-f""]])\n\ + from = NULL <-- stdin\n\ + to = NULL --> stdout\n\ + option -f: overwrite existing file\n", + int, + cp, (char *from, char *to, char *options) + ), + HELP( +"list a directory: ls([""dir""])\n", + int, + ls, (char *dir) + ), + HELP( +"remove a file\n", + int, + rm, (char *path) + ), + HELP( +"change the working directory\n", + int, + cd, (char *path) + ), + HELP( +"create a link: ln(""to"",""name"",""[-s]""\n\ + -s creates a symlink\n", + int, + ln, (char *to, char *name, char *options) + ), + HELP("",,0,) +}; +#endif diff --git a/cpukit/libfs/src/nfsclient/src/librtemsNfs.h b/cpukit/libfs/src/nfsclient/src/librtemsNfs.h new file mode 100644 index 0000000000..fc17d1de7f --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/librtemsNfs.h @@ -0,0 +1,180 @@ +#ifndef LIB_RTEMS_NFS_CLIENT_H +#define LIB_RTEMS_NFS_CLIENT_H +/* $Id$ */ + +/* public interface to the NFS client library for RTEMS */ + +/* Author: Till Straumann <strauman@slac.stanford.edu> 2002-2003 */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/stat.h> +#include <dirent.h> +#include <netdb.h> +#include <ctype.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* RPCIO driver interface. + * If you need RPCIO for other purposes than NFS + * you may want to include <rpcio.h> +#include "rpcio.h" + */ + +/* Priority of daemon; may be setup prior to calling rpcUdpInit(); + * otherwise the network task priority from the rtems_bsdnet_config + * is used... + */ +extern rtems_task_priority rpciodPriority; + +/* Initialize the driver. + * + * Note, called in nfsfs initialise when mount is called. + * + * RETURNS: 0 on success, -1 on failure + */ +int +rpcUdpInit(void); + +/* Cleanup/Stop + * + * RETURNS: 0 on success, nonzero if still in use + */ +int +rpcUdpCleanup(void); + +/* NFS driver interface */ + +/* Initialize the NFS driver. + * + * NOTE: The RPCIO driver must have been initialized prior to + * calling this. + * + * Note, called in nfsfs initialise when mount is called with defaults. + * + * ARGS: depth of the small and big + * transaction pools, i.e. how + * many transactions (buffers) + * should always be kept around. + * + * (If more transactions are needed, + * they are created and destroyed + * on the fly). + * + * Supply zero values to have the + * driver chose reasonable defaults. + */ +int +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/cpukit/libfs/src/nfsclient/src/nfs.c b/cpukit/libfs/src/nfsclient/src/nfs.c new file mode 100644 index 0000000000..b8a7ffdb57 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/nfs.c @@ -0,0 +1,3354 @@ +/* $Id$ */ + +/* NFS client implementation for RTEMS; hooks into the RTEMS filesystem */ + +/* Author: Till Straumann <strauman@slac.stanford.edu> 2002 */ + +/* Hacked on by others. */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/stat.h> +#include <dirent.h> +#include <netdb.h> +#include <ctype.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <nfs_prot.h> +#include <mount_prot.h> + +#include "rpcio.h" + +/* Configurable parameters */ + +/* Estimated average length of a filename (including terminating 0). + * This was calculated by doing + * + * find <some root> -print -exec basename '{}' \; > feil + * wc feil + * + * AVG_NAMLEN = (num_chars + num_lines)/num_lines + */ +#define CONFIG_AVG_NAMLEN 10 + +#define CONFIG_NFS_SMALL_XACT_SIZE 800 /* size of RPC arguments for non-write ops */ +/* lifetime of NFS attributes in a NfsNode; + * the time is in seconds and the lifetime is + * infinite if the symbol is #undef + */ +#define CONFIG_ATTR_LIFETIME 10/*secs*/ + +/* + * The 'st_blksize' (stat(2)) value this nfs + * client should report. If set to zero then the server's fattr data + * is passed throught which is not necessary optimal. + * Newlib's stdio uses 'st_blksize' (if built with HAVE_BLKSIZE defined) + * to size the default buffer. + * Due to the overhead of NFS it is probably better to use the maximum + * size of an NFS read request (8k) rather than the optimal block + * size on the server. + * This value can be overridden at run-time by setting the global + * variable 'nfsStBlksize'. + * Thanks to Steven Johnson <sjohnson@sakuraindustries.com> for helping + * working on this issue. + */ +#define DEFAULT_NFS_ST_BLKSIZE NFS_MAXDATA + +/* dont change this without changing the maximal write size */ +#define CONFIG_NFS_BIG_XACT_SIZE UDPMSGSIZE /* dont change this */ + +/* The real values for these are specified further down */ +#define NFSCALL_TIMEOUT (&_nfscalltimeout) +#define MNTCALL_TIMEOUT (&_nfscalltimeout) +static struct timeval _nfscalltimeout = { 10, 0 }; /* {secs, us } */ + +/* More or less fixed constants; in particular, NFS3 is not supported */ +#define DELIM '/' +#define HOSTDELIM ':' +#define UPDIR ".." +#define UIDSEP '@' +#define NFS_VERSION_2 NFS_VERSION + +/* we use a dynamically assigned major number */ +#define NFS_MAJOR (nfsGlob.nfs_major) + + +/* NOTE: RTEMS (ss-20020301) uses a 'short st_ino' type :-( but the + * NFS fileid is 32 bit. [Later versions of RTEMS have fixed this; + * nfsInit() issues a warning if you run a version with 'short st_ino'.] + * + * As a workarount, we merge the upper 16bits of the fileid into the + * minor device no. Hence, it is still possible to uniquely identify + * a file by looking at its device number (major = nfs, minor = part + * of the fileid + our 'nfs-id' identifier). + * + * This has an impact on performance, as e.g. getcwd() stats() all + * directory entries when it believes it has crossed a mount point + * (a.st_dev != b.st_dev). + * + * OTOH, it also might cause node comparison failure! E.g. 'getcwd()' + * assumes that two nodes residing in the same directory must be located + * on the same device and hence compares 'st_ino' only. + * If two files in the same directory have the same inode number + * modulo 2^16, they will be considered equal (although their device + * number doesn't match - getcwd doesn't look at it). + * + * Other software might or might not be affected. + * + * The only clean solution to this problem is bumping up the size of + * 'ino_t' at least to 'long'. + * Note that this requires _all_ software (libraries etc.) to be + * recompiled. + */ + +#define NFS_MAKE_DEV_T_INO_HACK(node) \ + rtems_filesystem_make_dev_t( NFS_MAJOR, \ + (((rtems_device_minor_number)((node)->nfs->id))<<16) | (((rtems_device_minor_number)SERP_ATTR((node)).fileid) >> 16) ) + +/* use our 'nfs id' and the server's fsid for the minor device number + * this should be fairly unique + */ +#define NFS_MAKE_DEV_T(node) \ + rtems_filesystem_make_dev_t( NFS_MAJOR, \ + (((rtems_device_minor_number)((node)->nfs->id))<<16) | (SERP_ATTR((node)).fsid & (((rtems_device_minor_number)1<<16)-1)) ) + +#define DIRENT_HEADER_SIZE ( sizeof(struct dirent) - \ + sizeof( ((struct dirent *)0)->d_name ) ) + + +/* debugging flags */ +#define DEBUG_COUNT_NODES (1<<0) +#define DEBUG_TRACK_NODES (1<<1) +#define DEBUG_EVALPATH (1<<2) +#define DEBUG_READDIR (1<<3) +#define DEBUG_SYSCALLS (1<<4) + +/* #define DEBUG ( DEBUG_SYSCALLS | DEBUG_COUNT_NODES ) */ + +#ifdef DEBUG +#define STATIC +#else +#define STATIC static +#endif + +#define MUTEX_ATTRIBUTES (RTEMS_LOCAL | \ + RTEMS_PRIORITY | \ + RTEMS_INHERIT_PRIORITY | \ + RTEMS_BINARY_SEMAPHORE) + +#define LOCK(s) do { \ + rtems_semaphore_obtain((s), \ + RTEMS_WAIT, \ + RTEMS_NO_TIMEOUT); \ + } while (0) + +#define UNLOCK(s) do { rtems_semaphore_release((s)); \ + } while (0) + +/***************************************** + 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; + +static bool_t +xdr_strbuf(XDR *xdrs, strbuf *obj) +{ + return xdr_string(xdrs, &obj->buf, obj->max); +} + +/* 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( + rtems_filesystem_location_info_t *loc, /* IN */ + char *buf, /* OUT */ + 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 struct _rtems_filesystem_operations_table nfs_fs_ops; +static struct _rtems_filesystem_file_handlers_r nfs_file_file_handlers; +static struct _rtems_filesystem_file_handlers_r nfs_dir_file_handlers; +static 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); + + +/***************************************** + Inline Routines + *****************************************/ + + +/* * * * * * * * * * * * * * * * * * + Trivial Operations on a NfsNode + * * * * * * * * * * * * * * * * * */ + +/* determine if a location 'l' is an NFS root node */ +static inline int +locIsRoot(rtems_filesystem_location_info_t *l) +{ +NfsNode me = (NfsNode) l->node_access; +NfsNode r; + r = (NfsNode)l->mt_entry->mt_fs_root.node_access; + return SERP_ATTR(r).fileid == SERP_ATTR(me).fileid && + SERP_ATTR(r).fsid == SERP_ATTR(me).fsid; +} + +/* determine if a location 'l' is an NFS node */ +static inline int +locIsNfs(rtems_filesystem_location_info_t *l) +{ + return l->ops == &nfs_fs_ops; +} + +/* determine if two locations refer to the + * same entity. We know that 'nfsloc' is a + * location inside nfs. However, we needn't + * know anything about 'anyloc'. + */ +static inline int +locAreEqual( + rtems_filesystem_location_info_t *nfsloc, + rtems_filesystem_location_info_t *anyloc +) +{ +NfsNode na = (NfsNode) nfsloc->node_access; +NfsNode nb; + + if (!locIsNfs(anyloc)) + return 0; + + nb = (NfsNode) anyloc->node_access; + + if (na->nfs != nb->nfs) + return 0; + + updateAttr(nb, 0); + + return SERP_ATTR(na).fileid == SERP_ATTR(nb).fileid && + SERP_ATTR(na).fsid == SERP_ATTR(nb).fsid; +} + + + +/***************************************** + Global Variables + *****************************************/ + +/* These are (except for MAXNAMLEN/MAXPATHLEN) copied from IMFS */ + +static rtems_filesystem_limits_and_options_t +nfs_limits_and_options = { + 5, /* link_max */ + 6, /* max_canon */ + 7, /* max_input */ + NFS_MAXNAMLEN, /* name_max */ + NFS_MAXPATHLEN, /* path_max */ + 2, /* pipe_buf */ + 1, /* posix_async_io */ + 2, /* posix_chown_restrictions */ + 3, /* posix_no_trunc */ + 4, /* posix_prio_io */ + 5, /* posix_sync_io */ + 6 /* posix_vdisable */ +}; + +/* size of an encoded 'entry' object */ +static int dirres_entry_size; + +/* Global stuff and statistics */ +static struct nfsstats { + /* A lock for protecting the + * linked ist of mounted NFS + * and the num_mounted_fs field + */ + rtems_id llock; + /* A lock for protecting misc + * stuff within the driver. + * The lock must only be held + * for short periods of time. + */ + rtems_id lock; + /* Our major number as assigned + * by RTEMS + */ + rtems_device_major_number nfs_major; + /* The number of currently + * mounted NFS + */ + int num_mounted_fs; + /* A list of the currently + * mounted NFS + */ + struct NfsRec_ *mounted_fs; + /* A counter for allocating + * unique IDs to each mounted + * NFS. + * Assume we are not going to + * do more than 16k mounts + * during the system lifetime + */ + u_short fs_ids; +} nfsGlob = {0, 0, 0, 0, 0, 0}; + +/* + * Global variable to tune the 'st_blksize' (stat(2)) value this nfs + * client should report. + * size on the server. + */ +#ifndef DEFAULT_NFS_ST_BLKSIZE +#define DEFAULT_NFS_ST_BLKSIZE NFS_MAXDATA +#endif +int nfsStBlksize = DEFAULT_NFS_ST_BLKSIZE; + +/* Two pools of RPC transactions; + * One with small send buffers + * the other with a big one. + * The actual size of the small + * buffer is configurable (see top). + * + * Note: The RX buffers are always + * big + */ +static RpcUdpXactPool smallPool = 0; +static RpcUdpXactPool bigPool = 0; + + +/***************************************** + Implementation + *****************************************/ + +/* Create a Nfs object. This is + * per-mounted NFS information. + * + * ARGS: The Nfs server handle. + * + * RETURNS: Nfs on success, + * NULL on failure with + * errno set + * + * NOTE: The submitted server + * object is 'owned' by + * this Nfs and will be + * destroyed by nfsDestroy() + */ +static Nfs +nfsCreate(RpcUdpServer server) +{ +Nfs rval = calloc(1,sizeof(*rval)); + + if (rval) { + rval->server = server; + LOCK(nfsGlob.llock); + rval->next = nfsGlob.mounted_fs; + nfsGlob.mounted_fs = rval; + UNLOCK(nfsGlob.llock); + } else { + errno = ENOMEM; + } + return rval; +} + +/* Destroy an Nfs object and + * its associated server + */ +static void +nfsDestroy(Nfs nfs) +{ +register Nfs prev; + if (!nfs) + return; + + LOCK(nfsGlob.llock); + if (nfs == nfsGlob.mounted_fs) + nfsGlob.mounted_fs = nfs->next; + else { + for (prev = nfsGlob.mounted_fs; + prev && prev->next != nfs; + prev = prev->next) + /* nothing else to do */; + assert( prev ); + prev->next = nfs->next; + } + UNLOCK(nfsGlob.llock); + + nfs->next = 0; /* paranoia */ + rpcUdpServerDestroy(nfs->server); + free(nfs); +} + +/* + * Create a Node. The node will + * be associated with a particular + * mounted NFS identified by 'nfs' + * Optionally, a NFS file handle + * may be copied into this node. + * + * ARGS: nfs of the NFS this node + * belongs to. + * NFS file handle identifying + * this node. + * RETURNS: node on success, + * NULL on failure with errno + * set. + * + * NOTE: The caller of this routine + * is responsible for copying + * a NFS file handle if she + * choses to pass a NULL fh. + * + * The driver code assumes the + * a node always has a valid + * NFS filehandle and file + * attributes (unless the latter + * are aged). + */ +static NfsNode +nfsNodeCreate(Nfs nfs, fhandle *fh) +{ +NfsNode rval = malloc(sizeof(*rval)); +unsigned long flags; + +#if DEBUG & DEBUG_TRACK_NODES + fprintf(stderr,"NFS: creating a node\n"); +#endif + + if (rval) { + if (fh) + memcpy( &SERP_FILE(rval), fh, sizeof(*fh) ); + rtems_interrupt_disable(flags); + nfs->nodesInUse++; + rtems_interrupt_enable(flags); + rval->nfs = nfs; + rval->str = 0; + } else { + errno = ENOMEM; + } + + return rval; +} + +/* destroy a node */ +static void +nfsNodeDestroy(NfsNode node) +{ +unsigned long flags; + +#if DEBUG & DEBUG_TRACK_NODES + fprintf(stderr,"NFS: destroying a node\n"); +#endif +#if 0 + if (!node) + return; + /* this probably does nothing... */ + xdr_free(xdr_serporid, &node->serporid); +#endif + + rtems_interrupt_disable(flags); + node->nfs->nodesInUse--; +#if DEBUG & DEBUG_COUNT_NODES + if (node->str) + node->nfs->stringsInUse--; +#endif + rtems_interrupt_enable(flags); + + if (node->str) + free(node->str); + + free(node); +} + +/* Clone a given node (AKA copy constructor), + * i.e. create an exact copy. + * + * ARGS: node to clone + * RETURNS: new node on success + * NULL on failure with errno set. + * + * NOTE: a string attached to 'str' + * is cloned as well. Outdated + * attributes (of the new copy + * only) will be refreshed + * (if unsuccessful, this could + * be a reason for failure to + * clone a node). + */ +static NfsNode +nfsNodeClone(NfsNode node) +{ +NfsNode rval = nfsNodeCreate(node->nfs, 0); + + if (rval) { + *rval = *node; + + /* must clone the string also */ + if (node->str) { + rval->args.name = rval->str = strdup(node->str); + if (!rval->str) { + errno = ENOMEM; + nfsNodeDestroy(rval); + return 0; + } +#if DEBUG & DEBUG_COUNT_NODES + { unsigned long flags; + rtems_interrupt_disable(flags); + node->nfs->stringsInUse++; + rtems_interrupt_enable(flags); + } +#endif + } + + /* possibly update the stats */ + if (updateAttr(rval, 0 /* only if necessary */)) { + nfsNodeDestroy(rval); + return 0; + } + } + return rval; +} + +/* Initialize the driver. + * + * ARGS: depth of the small and big + * transaction pools, i.e. how + * many transactions (buffers) + * should always be kept around. + * + * (If more transactions are needed, + * they are created and destroyed + * on the fly). + */ +void +nfsInit(int smallPoolDepth, int bigPoolDepth) +{ +static int initialised = 0; +entry dummy; +rtems_status_code status; + + if (initialised) + return; + + initialised = 1; + + fprintf(stderr, + "RTEMS-NFS $Release$, " \ + "Till Straumann, Stanford/SLAC/SSRL 2002, " \ + "See LICENSE file for licensing info.\n"); + + /* Get a major number */ + + if (RTEMS_SUCCESSFUL != rtems_io_register_driver(0, &drvNfs, &nfsGlob.nfs_major)) { + fprintf(stderr,"Registering NFS driver failed - %s\n", strerror(errno)); + return; + } + + if (0==smallPoolDepth) + smallPoolDepth = 20; + if (0==bigPoolDepth) + bigPoolDepth = 10; + + /* it's crucial to zero out the 'next' pointer + * because it terminates the xdr_entry recursion + * + * we also must make the filename some non-zero + * char pointer! + */ + + memset(&dummy, 0, sizeof(dummy)); + + dummy.nextentry = 0; + dummy.name = "somename"; /* guess average length of a filename */ + dirres_entry_size = xdr_sizeof((xdrproc_t)xdr_entry, &dummy); + + smallPool = rpcUdpXactPoolCreate( + NFS_PROGRAM, + NFS_VERSION_2, + CONFIG_NFS_SMALL_XACT_SIZE, + smallPoolDepth); + assert( smallPool ); + + bigPool = rpcUdpXactPoolCreate( + NFS_PROGRAM, + NFS_VERSION_2, + CONFIG_NFS_BIG_XACT_SIZE, + bigPoolDepth); + assert( bigPool ); + + status = rtems_semaphore_create( + rtems_build_name('N','F','S','l'), + 1, + MUTEX_ATTRIBUTES, + 0, + &nfsGlob.llock); + assert( status == RTEMS_SUCCESSFUL ); + status = rtems_semaphore_create( + rtems_build_name('N','F','S','m'), + 1, + MUTEX_ATTRIBUTES, + 0, + &nfsGlob.lock); + assert( status == RTEMS_SUCCESSFUL ); + + if (sizeof(ino_t) < sizeof(u_int)) { + fprintf(stderr, + "WARNING: Using 'short st_ino' hits performance and may fail to access/find correct files\n"); + fprintf(stderr, + "you should fix newlib's sys/stat.h - for now I'll enable a hack...\n"); + + } +} + +/* Driver cleanup code + */ +int +nfsCleanup(void) +{ +rtems_id l; +int refuse; + + if (!nfsGlob.llock) { + /* registering the driver failed - let them still cleanup */ + return 0; + } + + LOCK(nfsGlob.llock); + if ( (refuse = nfsGlob.num_mounted_fs) ) { + fprintf(stderr,"Refuse to unload NFS; %i filesystems still mounted.\n", + refuse); + nfsMountsShow(stderr); + /* yes, printing is slow - but since you try to unload the driver, + * you assume nobody is using NFS, so what if they have to wait? + */ + UNLOCK(nfsGlob.llock); + return -1; + } + + rtems_semaphore_delete(nfsGlob.lock); + nfsGlob.lock = 0; + + /* hold the lock while cleaning up... */ + + rpcUdpXactPoolDestroy(smallPool); + rpcUdpXactPoolDestroy(bigPool); + l = nfsGlob.llock; + rtems_io_unregister_driver(nfsGlob.nfs_major); + + rtems_semaphore_delete(l); + nfsGlob.llock = 0; + return 0; +} + +/* NFS RPC wrapper. + * + * ARGS: srvr the NFS server we want to call + * proc the NFSPROC_xx we want to invoke + * xargs xdr routine to wrap the arguments + * pargs pointer to the argument object + * xres xdr routine to unwrap the results + * pres pointer to the result object + * + * RETURNS: 0 on success, -1 on error with errno set. + * + * NOTE: the caller assumes that errno is set to + * a nonzero value if this routine returns + * an error (nonzero return value). + * + * This routine prints RPC error messages to + * stderr. + */ +STATIC int +nfscall( + RpcUdpServer srvr, + int proc, + xdrproc_t xargs, + void * pargs, + xdrproc_t xres, + void * pres) +{ +RpcUdpXact xact; +enum clnt_stat stat; +RpcUdpXactPool pool; +int rval = -1; + + + switch (proc) { + case NFSPROC_SYMLINK: + case NFSPROC_WRITE: + pool = bigPool; break; + default: pool = smallPool; break; + } + + xact = rpcUdpXactPoolGet(pool, XactGetCreate); + + if ( !xact ) { + errno = ENOMEM; + return -1; + } + + if ( RPC_SUCCESS != (stat=rpcUdpSend( + xact, + srvr, + NFSCALL_TIMEOUT, + proc, + xres, + pres, + xargs, + pargs, + 0)) || + RPC_SUCCESS != (stat=rpcUdpRcv(xact)) ) { + + fprintf(stderr, + "NFS (proc %i) - %s\n", + proc, + clnt_sperrno(stat)); + + switch (stat) { + /* TODO: this is probably not complete and/or fully accurate */ + case RPC_CANTENCODEARGS : errno = EINVAL; break; + case RPC_AUTHERROR : errno = EPERM; break; + + case RPC_CANTSEND : + case RPC_CANTRECV : /* hope they have errno set */ + case RPC_SYSTEMERROR : break; + + default : errno = EIO; break; + } + } else { + rval = 0; + } + + /* release the transaction back into the pool */ + rpcUdpXactPoolPut(xact); + + if (rval && !errno) + errno = EIO; + + return rval; +} + +/* Check the 'age' of a node's stats + * and read the attributes from the server + * if necessary. + * + * ARGS: node node to update + * force enforce updating ignoring + * the timestamp/age + * + * RETURNS: 0 on success, + * -1 on failure with errno set + */ + +static int +updateAttr(NfsNode node, int force) +{ + + if (force +#ifdef CONFIG_ATTR_LIFETIME + || (nowSeconds() - node->age > CONFIG_ATTR_LIFETIME) +#endif + ) { + if ( nfscall(node->nfs->server, + NFSPROC_GETATTR, + (xdrproc_t)xdr_nfs_fh, &SERP_FILE(node), + (xdrproc_t)xdr_attrstat, &node->serporid) ) + return -1; + + if ( NFS_OK != node->serporid.status ) { + errno = node->serporid.status; + return -1; + } + + node->age = nowSeconds(); + } + + return 0; +} + +/* + * IP address helper. + * + * initialize a sockaddr_in from a + * [<uid>'.'<gid>'@']<host>':'<path>" string and let + * pPath point to the <path> part; retrieve the optional + * uid/gids + * + * ARGS: see description above + * + * RETURNS: 0 on success, + * -1 on failure with errno set + */ +static int +buildIpAddr(u_long *puid, u_long *pgid, + char **pHost, struct sockaddr_in *psa, + char **pPath) +{ +struct hostent *h; +char host[64]; +char *chpt = *pPath; +char *path; +int len; + + if ( !chpt ) { + errno = EINVAL; + return -1; + } + + /* look for the optional uid/gid */ + if ( (chpt = strchr(chpt, UIDSEP)) ) { + if ( 2 != sscanf(*pPath,"%li.%li",puid,pgid) ) { + errno = EINVAL; + return -1; + } + chpt++; + } else { + *puid = RPCIOD_DEFAULT_ID; + *pgid = RPCIOD_DEFAULT_ID; + chpt = *pPath; + } + if ( pHost ) + *pHost = chpt; + + /* split the device name which is in the form + * + * <host> ':' <path> + * + * into its components using a local buffer + */ + + if ( !(path = strchr(chpt, HOSTDELIM)) || + (len = path - chpt) >= sizeof(host) - 1 ) { + errno = EINVAL; + return -1; + } + /* point to path beyond ':' */ + path++; + + strncpy(host, chpt, len); + host[len]=0; + + /* BEGIN OF NON-THREAD SAFE REGION */ + + h = gethostbyname(host); + + if ( !h ) { + errno = EINVAL; + return -1; + } + + memcpy(&psa->sin_addr, h->h_addr, sizeof (struct in_addr)); + + /* END OF NON-THREAD SAFE REGION */ + + psa->sin_family = AF_INET; + psa->sin_port = 0; + *pPath = path; + return 0; +} + +/* wrapper similar to nfscall. + * However, since it is not used + * very often, the simpler and less + * efficient rpcUdpCallRp API is used. + * + * ARGS: see 'nfscall()' above + * + * RETURNS: RPC status + */ +static enum clnt_stat +mntcall( + struct sockaddr_in *psrvr, + int proc, + xdrproc_t xargs, + void * pargs, + xdrproc_t xres, + void * pres, + u_long uid, + u_long gid) +{ +#ifdef MOUNT_V1_PORT +int retry; +#endif +enum clnt_stat stat = RPC_FAILED; + +#ifdef MOUNT_V1_PORT + /* if the portmapper fails, retry a fixed port */ + for (retry = 1, psrvr->sin_port = 0, stat = RPC_FAILED; + retry >= 0 && stat; + stat && (psrvr->sin_port = htons(MOUNT_V1_PORT)), retry-- ) +#endif + stat = rpcUdpCallRp( + psrvr, + MOUNTPROG, + MOUNTVERS, + proc, + xargs, + pargs, + xres, + pres, + uid, + gid, + MNTCALL_TIMEOUT + ); + return stat; +} + +/***************************************** + RTEMS File System Operations for NFS + *****************************************/ + +#if 0 /* for reference */ + +struct rtems_filesystem_location_info_tt +{ + void *node_access; + rtems_filesystem_file_handlers_r *handlers; + rtems_filesystem_operations_table *ops; + rtems_filesystem_mount_table_entry_t *mt_entry; +}; + +#endif + +/* + * Evaluate a path letting 'pathloc' travel along. + * + * The important semantics of this operation are: + * + * If this routine returns -1, the caller assumes + * pathloc to be _invalid_ and hence it will not + * invoke rtems_filesystem_freenode() on it. + * + * OTOH, if evalpath returns 0, + * rtems_filesystem_freenode() will eventually be + * called which results in our freeing the associated + * NfsNode attached to node_access. + * + * Therefore, this routine will _always_ allocate + * a NfsNode and pass it out to *pathloc (provided + * that the evaluation succeeds). + * + * However, if the evaluation finds that it has to + * step across FS boundaries (mount point or a symlink + * pointing outside), the NfsNode is destroyed + * before passing control to the new FS' evalpath_h() + * + */ + +union nfs_evalpath_arg { + int i; + const char **c; + }; + +STATIC int nfs_do_evalpath( + const char *pathname, /* IN */ + int pathnamelen, /* IN */ + union nfs_evalpath_arg *arg, + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + int forMake +) +{ +char *del = 0, *part; +int e = 0; +NfsNode node = pathloc->node_access; +char *p = malloc(MAXPATHLEN+1); +Nfs nfs = (Nfs)pathloc->mt_entry->fs_info; +RpcUdpServer server = nfs->server; +unsigned long flags; +#if DEBUG & DEBUG_COUNT_NODES +unsigned long niu,siu; +#endif + + if ( !p ) { + e = ENOMEM; + goto cleanup; + } + memset(p, 0, MAXPATHLEN+1); + memcpy(p, pathname, pathnamelen); + + LOCK(nfsGlob.lock); + node = nfsNodeClone(node); + UNLOCK(nfsGlob.lock); + + /* from here on, the NFS is protected from being unmounted + * since the node refcount is > 1 + */ + + /* clone the node */ + if ( !node ) { + /* nodeClone sets errno */ + pathloc->node_access = 0; + if ( ! (e = errno) ) { + /* if we have no node, e must not be zero! */ + e = ENOMEM; + } + goto cleanup; + } + + pathloc->node_access = node; + + /* Special case: the RTEMS filesystem code + * may emit '..' on a regular file node to + * find the parent directory :-(. + * (eval.c: rtems_filesystem_evaluate_parent()) + * Try to catch this case here: + */ + if ( NFDIR != SERP_ATTR(node).type && '.'==*p && '.'==*(p+1) ) { + for ( part = p+2; '/'==*part; part++ ) + /* skip trailing '/' */; + if ( !*part ) { + /* this is it; back out dir and let them look up the dir itself... */ + memcpy( &SERP_FILE(node), + &node->args.dir, + sizeof(node->args.dir)); + *(p+1)=0; + } + } + + for (part=p; part && *part; part=del) { + + if ( NFLNK == SERP_ATTR(node).type ) { + /* follow midpath link */ + char *b = malloc(NFS_MAXPATHLEN+1); + int l; + + if (!b) { + e = ENOMEM; + goto cleanup; + } + if (nfs_readlink(pathloc, b, NFS_MAXPATHLEN+1)) { + free(b); + e = errno; + goto cleanup; + } + + /* prepend the link value to the rest of the path */ + if ( (l=strlen(b)) + strlen(part) + 1 > NFS_MAXPATHLEN ) { + free(b); + e = EINVAL; + goto cleanup; + } + /* swap string buffers and reset delimiter */ + b[l++] = DELIM; + strcpy(b+l,part); + part = b; + b = p; + p = del = part; + + free(b); + + /* back up the directory filehandle (only necessary + * if we don't back out to the root + */ + if (! (DELIM == *part) ) { + memcpy( &SERP_FILE(node), + &node->args.dir, + sizeof(node->args.dir)); + + if (updateAttr(node, 1 /* force */)) { + e = errno; + goto cleanup; + } + } + } + + /* find delimiter and eat /// sequences + * (only if we don't restart at the root) + */ + if ( DELIM != *part && (del = strchr(part, DELIM))) { + do { + *del++=0; + } while (DELIM==*del); + } + + /* refuse to backup over the root */ + if ( 0==strcmp(part,UPDIR) + && locAreEqual(pathloc, &rtems_filesystem_root) ) { + part++; + } + + /* cross mountpoint upwards */ + if ( (0==strcmp(part,UPDIR) && locIsRoot(pathloc)) /* cross mountpoint up */ + || DELIM == *part /* link starts at root */ + ) { + int rval; + +#if DEBUG & DEBUG_EVALPATH + fprintf(stderr, + "Crossing mountpoint upwards\n"); +#endif + + if (DELIM == *part) { + *pathloc = rtems_filesystem_root; + } else { + *pathloc = pathloc->mt_entry->mt_point_node; + /* re-append the rest of the path */ + if (del) + while ( 0 == *--del ) + *del = DELIM; + } + + nfsNodeDestroy(node); + +#if DEBUG & DEBUG_EVALPATH + fprintf(stderr, + "Re-evaluating '%s'\n", + part); +#endif + + if (forMake) + rval = pathloc->ops->evalformake_h(part, pathloc, arg->c); + else + rval = pathloc->ops->evalpath_h(part, strlen(part), arg->i, pathloc); + + free(p); + return rval; + } + + /* lookup one element */ + SERP_ARGS(node).diroparg.name = part; + + /* remember args / directory fh */ + memcpy( &node->args, &SERP_FILE(node), sizeof(node->args)); + + /* don't lookup the item we want to create */ + if ( forMake && (!del || !*del) ) + break; + +#if DEBUG & DEBUG_EVALPATH + fprintf(stderr,"Looking up '%s'\n",part); +#endif + + if ( nfscall(server, + NFSPROC_LOOKUP, + (xdrproc_t)xdr_diropargs, &SERP_FILE(node), + (xdrproc_t)xdr_serporid, &node->serporid) || + NFS_OK != (errno=node->serporid.status) ) { + e = errno; + goto cleanup; + } + node->age = nowSeconds(); + +#if DEBUG & DEBUG_EVALPATH + if (NFLNK == SERP_ATTR(node).type && del) { + fprintf(stderr, + "Following midpath link '%s'\n", + part); + } +#endif + + } + + if (forMake) { + /* remember the name - do this _before_ copying + * the name to local storage; the caller expects a + * pointer into pathloc + */ + assert( node->args.name ); + + *(const char**)arg = pathname + (node->args.name - p); + +#if 0 + /* restore the directory node */ + + memcpy( &SERP_FILE(node), + &node->args.dir, + sizeof(node->args.dir)); + + if ( (nfscall(nfs->server, + NFSPROC_GETATTR, + (xdrproc_t)xdr_nfs_fh, &SERP_FILE(node), + (xdrproc_t)xdr_attrstat, &node->serporid) && !errno && (errno = EIO)) || + (NFS_OK != (errno=node->serporid.status) ) ) { + goto cleanup; + } +#endif + } + + if (locIsRoot(pathloc)) { + + /* stupid filesystem code has no 'op' for comparing nodes + * but just compares the 'node_access' pointers. + * Luckily, this is only done for comparing the root nodes. + * Hence, we never give them a copy of the root but always + * the root itself. + */ + pathloc->node_access = pathloc->mt_entry->mt_fs_root.node_access; + /* increment the 'in use' counter since we return one more + * reference to the root node + */ + rtems_interrupt_disable(flags); + nfs->nodesInUse++; + rtems_interrupt_enable(flags); + nfsNodeDestroy(node); + + + } else { + switch (SERP_ATTR(node).type) { + case NFDIR: pathloc->handlers = &nfs_dir_file_handlers; break; + case NFREG: pathloc->handlers = &nfs_file_file_handlers; break; + case NFLNK: pathloc->handlers = &nfs_link_file_handlers; break; + default: pathloc->handlers = &rtems_filesystem_handlers_default; break; + } + pathloc->node_access = node; + + /* remember the name of this directory entry */ + + if (node->args.name) { + if (node->str) { +#if DEBUG & DEBUG_COUNT_NODES + rtems_interrupt_disable(flags); + nfs->stringsInUse--; + rtems_interrupt_enable(flags); +#endif + free(node->str); + } + node->args.name = node->str = strdup(node->args.name); + if (!node->str) { + e = ENOMEM; + goto cleanup; + } + +#if DEBUG & DEBUG_COUNT_NODES + rtems_interrupt_disable(flags); + nfs->stringsInUse++; + rtems_interrupt_enable(flags); +#endif + } + + } + node = 0; + + e = 0; + +cleanup: + free(p); +#if DEBUG & DEBUG_COUNT_NODES + /* cache counters; nfs may be unmounted by other thread after the last + * node is destroyed + */ + niu = nfs->nodesInUse; + siu = nfs->stringsInUse; +#endif + if (node) { + nfsNodeDestroy(node); + pathloc->node_access = 0; + } +#if DEBUG & DEBUG_COUNT_NODES + fprintf(stderr, + "leaving evalpath, in use count is %i nodes, %i strings\n", + niu,siu); +#endif + if (e) { +#if DEBUG & DEBUG_EVALPATH + perror("Evalpath"); +#endif + rtems_set_errno_and_return_minus_one(e); + } else { + return 0; + } +} + +/* MANDATORY; may set errno=ENOSYS and return -1 */ +static int nfs_evalformake( + const char *path, /* IN */ + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + const char **pname /* OUT */ +) +{ + union nfs_evalpath_arg args; + args.c = pname; + + return nfs_do_evalpath(path, strlen(path), &args, pathloc, 1 /*forMake*/); +} + +static int nfs_evalpath( + const char *path, /* IN */ + size_t pathlen, /* IN */ + int flags, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +) +{ + union nfs_evalpath_arg args; + args.i = flags; + return nfs_do_evalpath(path, pathlen, &args, pathloc, 0 /*not forMake*/); +} + + +/* create a hard link */ + +static int nfs_link( + rtems_filesystem_location_info_t *to_loc, /* IN */ + rtems_filesystem_location_info_t *parent_loc, /* IN */ + const char *name /* IN */ +) +{ +NfsNode pNode; +nfsstat status; +NfsNode tNode = to_loc->node_access; + +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr,"Creating link '%s'\n",name); +#endif + + if ( !locIsNfs(parent_loc) ) { + errno = EXDEV; + return -1; + } + + pNode = parent_loc->node_access; + if ( tNode->nfs != pNode->nfs ) { + errno = EXDEV; + return -1; + } + memcpy(&SERP_ARGS(tNode).linkarg.to.dir, + &SERP_FILE(pNode), + sizeof(SERP_FILE(pNode))); + + SERP_ARGS(tNode).linkarg.to.name = (filename)name; + + 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 + return -1; + } + + return 0; + +} + +static int nfs_do_unlink( + rtems_filesystem_location_info_t *parent_loc,/* IN */ + rtems_filesystem_location_info_t *loc, /* IN */ + 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_unlink( + rtems_filesystem_location_info_t *parent_loc, /* IN */ + rtems_filesystem_location_info_t *loc /* IN */ +) +{ + return nfs_do_unlink(parent_loc, loc, NFSPROC_REMOVE); +} + +static int nfs_chown( + 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); + +} + +/* Cleanup the FS private info attached to pathloc->node_access */ +static int nfs_freenode( + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ +Nfs nfs = ((NfsNode)pathloc->node_access)->nfs; + +#if DEBUG & DEBUG_COUNT_NODES + /* 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 + + /* never destroy the root node; it is released by the unmount + * code + */ + if (locIsRoot(pathloc)) { + unsigned long flags; + /* just adjust the references to the root node but + * don't really release it + */ + rtems_interrupt_disable(flags); + nfs->nodesInUse--; + rtems_interrupt_enable(flags); + } else { + nfsNodeDestroy(pathloc->node_access); + pathloc->node_access = 0; + } + return 0; +} + +/* 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.node_access = rootNode; + + rootNode = 0; + + mt_entry->mt_fs_root.ops = &nfs_fs_ops; + mt_entry->mt_fs_root.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 int 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_set_errno_and_return_minus_one(EBUSY); + } + + 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)); + errno = EIO; + return -1; + } + + nfsNodeDestroy(mt_entry->mt_fs_root.node_access); + mt_entry->mt_fs_root.node_access = 0; + + nfsDestroy(mt_entry->fs_info); + mt_entry->fs_info = 0; + + nfsGlob.num_mounted_fs--; +UNLOCK(nfsGlob.llock); + + return 0; +} + +/* 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( + rtems_filesystem_location_info_t *pathloc /* in */ +) +{ +NfsNode node = pathloc->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 char *path, /* IN */ + mode_t mode, /* IN */ + dev_t dev, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +) +{ + +struct timeval now; +diropres res; +NfsNode node = pathloc->node_access; +mode_t type = S_IFMT & mode; + + if (type != S_IFDIR && type != S_IFREG) + rtems_set_errno_and_return_minus_one(ENOTSUP); + +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr,"nfs_mknod: creating %s\n", path); +#endif + + rtems_clock_get_tod_timeval(&now); + + SERP_ARGS(node).createarg.name = (filename)path; + SERP_ARGS(node).createarg.attributes.mode = mode; + /* TODO: either use our uid or use the Nfs credentials */ + SERP_ARGS(node).createarg.attributes.uid = 0; + SERP_ARGS(node).createarg.attributes.gid = 0; + 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( node->nfs->server, + 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 + return -1; + } + + return 0; +} + +static int nfs_utime( + 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( + rtems_filesystem_location_info_t *loc, /* IN */ + const char *link_name, /* IN */ + const char *node_name +) +{ +struct timeval now; +nfsstat status; +NfsNode node = loc->node_access; + + +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr,"nfs_symlink: creating %s -> %s\n", link_name, node_name); +#endif + + rtems_clock_get_tod_timeval(&now); + + SERP_ARGS(node).symlinkarg.name = (filename)link_name; + SERP_ARGS(node).symlinkarg.to = (nfspath) node_name; + + SERP_ARGS(node).symlinkarg.attributes.mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; + /* TODO */ + SERP_ARGS(node).symlinkarg.attributes.uid = 0; + SERP_ARGS(node).symlinkarg.attributes.gid = 0; + 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( node->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 + return -1; + } + + return 0; +} + +static int nfs_do_readlink( + rtems_filesystem_location_info_t *loc, /* IN */ + strbuf *psbuf /* IN/OUT */ +) +{ +NfsNode node = loc->node_access; +Nfs nfs = node->nfs; +readlinkres_strbuf rr; +int wasAlloced; +int rval; + + rr.strbuf = *psbuf; + + wasAlloced = (0 == psbuf->buf); + + if ( (rval = nfscall(nfs->server, + NFSPROC_READLINK, + (xdrproc_t)xdr_nfs_fh, &SERP_FILE(node), + (xdrproc_t)xdr_readlinkres_strbuf, &rr)) ) { + if (wasAlloced) + xdr_free( (xdrproc_t)xdr_strbuf, (caddr_t)&rr.strbuf ); + } + + + if (NFS_OK != rr.status) { + if (wasAlloced) + xdr_free( (xdrproc_t)xdr_strbuf, (caddr_t)&rr.strbuf ); + rtems_set_errno_and_return_minus_one(rr.status); + } + + *psbuf = rr.strbuf; + + return 0; +} + +static ssize_t nfs_readlink( + rtems_filesystem_location_info_t *loc, /* IN */ + char *buf, /* OUT */ + size_t len +) +{ +strbuf sbuf; + sbuf.buf = buf; + sbuf.max = len; + + return nfs_do_readlink(loc, &sbuf); +} + +/* The semantics of this routine are: + * + * The caller submits a valid pathloc, i.e. it has + * an NfsNode attached to node_access. + * On return, pathloc points to the target node which + * may or may not be an NFS node. + * Hence, the original NFS node is released in either + * case: + * - link evaluation fails; pathloc points to no valid node + * - link evaluation success; pathloc points to a new valid + * node. If it's an NFS node, a new NfsNode will be attached + * to node_access... + */ + +#define LINKVAL_BUFLEN (MAXPATHLEN+1) +#define RVAL_ERR_BUT_DONT_FREENODE (-1) +#define RVAL_ERR_AND_DO_FREENODE ( 1) +#define RVAL_OK ( 0) + +static int nfs_eval_link( + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + int flags /* IN */ +) +{ +rtems_filesystem_node_types_t type; +char *buf = malloc(LINKVAL_BUFLEN); +int rval = RVAL_ERR_AND_DO_FREENODE; + + if (!buf) { + errno = ENOMEM; + goto cleanup; + } + + /* in this loop, we must not use NFS specific ops as we might + * step out of our FS during the process... + * This algorithm should actually be performed by the + * generic's evaluate_path routine :-( + * + * Unfortunately, there is no way of finding the + * directory node who contains 'pathloc', however :-( + */ + do { + /* assume the generics have verified 'pathloc' to be + * a link... + */ + if ( !pathloc->ops->readlink_h ) { + errno = ENOTSUP; + goto cleanup; + } + + if ( pathloc->ops->readlink_h(pathloc, buf, LINKVAL_BUFLEN) ) { + goto cleanup; + } + +#if DEBUG & DEBUG_EVALPATH + fprintf(stderr, "link value is '%s'\n", buf); +#endif + + /* is the link value an absolute path ? */ + if ( DELIM != *buf ) { + /* NO; a relative path */ + + /* we must backup to the link's directory - we + * know only how to do that for NFS, however. + * In this special case, we can avoid recursion. + * Otherwise (i.e. if the link is on another FS), + * we must step into its eval_link_h(). + */ + if (locIsNfs(pathloc)) { + NfsNode node = pathloc->node_access; + int err; + + memcpy( &SERP_FILE(node), + &node->args.dir, + sizeof(node->args.dir) ); + + if (updateAttr(node, 1 /* force */)) + goto cleanup; + + if (SERP_ATTR(node).type != NFDIR) { + errno = ENOTDIR; + goto cleanup; + } + + pathloc->handlers = &nfs_dir_file_handlers; + + err = nfs_evalpath(buf, strlen(buf), flags, pathloc); + + /* according to its semantics, + * nfs_evalpath cloned the node attached + * to pathloc. Hence we have to + * release the old one (referring to + * the link; the new clone has been + * updated and refers to the link _value_). + */ + nfsNodeDestroy(node); + + if (err) { + /* nfs_evalpath has set errno; + * pathloc->node_access has no + * valid node attached in this case + */ + rval = RVAL_ERR_BUT_DONT_FREENODE; + goto cleanup; + } + + } else { + if ( ! pathloc->ops->eval_link_h ) { + errno = ENOTSUP; + goto cleanup; + } + if (!pathloc->ops->eval_link_h(pathloc, flags)) { + /* FS is responsible for freeing its pathloc->node_access + * if necessary + */ + rval = RVAL_ERR_BUT_DONT_FREENODE; + goto cleanup; + } + } + } else { + /* link points to an absolute path '/xxx' */ + + /* release this node; filesystem_evaluate_path() will + * lookup a new one. + */ + rtems_filesystem_freenode(pathloc); + + if (rtems_filesystem_evaluate_path(buf, strlen(buf), flags, pathloc, 1)) { + /* If evalpath fails then there is no valid node + * attached to pathloc; hence we must not attempt + * to free the node + */ + rval = RVAL_ERR_BUT_DONT_FREENODE; + goto cleanup; + } + } + + if ( !pathloc->ops->node_type_h ) { + errno = ENOTSUP; + goto cleanup; + } + + type = pathloc->ops->node_type_h(pathloc); + + + /* I dont know what to do about hard links */ + } while ( RTEMS_FILESYSTEM_SYM_LINK == type ); + + rval = RVAL_OK; + +cleanup: + + free(buf); + + if (RVAL_ERR_AND_DO_FREENODE == rval) { + rtems_filesystem_freenode(pathloc); + return -1; + } + + return rval; +} + + +struct _rtems_filesystem_operations_table nfs_fs_ops = { + nfs_evalpath, /* MANDATORY */ + nfs_evalformake, /* MANDATORY; may set errno=ENOSYS and return -1 */ + nfs_link, /* OPTIONAL; may be defaulted */ + nfs_unlink, /* OPTIONAL; may be defaulted */ + nfs_node_type, /* OPTIONAL; may be defaulted; BUG in mount - no test!! */ + nfs_mknod, /* OPTIONAL; may be defaulted */ + nfs_chown, /* OPTIONAL; may be defaulted */ + nfs_freenode, /* OPTIONAL; may be defaulted; (release node_access) */ + rtems_filesystem_default_mount, + rtems_nfs_initialize, /* OPTIONAL; may be defaulted -- not used anymore */ + rtems_filesystem_default_unmount, + nfs_fsunmount_me, /* OPTIONAL; may be defaulted */ + nfs_utime, /* OPTIONAL; may be defaulted */ + nfs_eval_link, /* OPTIONAL; may be defaulted */ + nfs_symlink, /* OPTIONAL; may be defaulted */ + nfs_readlink, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_rename, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_statvfs /* OPTIONAL; may be defaulted */ + +}; + +/***************************************** + 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, + uint32_t flag, + uint32_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, + uint32_t flag, + uint32_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( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ +readres rr; +NfsNode node = iop->pathinfo.node_access; +Nfs nfs = node->nfs; + + if (count > NFS_MAXDATA) + count = NFS_MAXDATA; + + SERP_ARGS(node).readarg.offset = iop->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; +} + +/* 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(); + + return count; +} + +static rtems_off64_t nfs_file_lseek( + rtems_libio_t *iop, + rtems_off64_t length, + int whence +) +{ +#if DEBUG & DEBUG_SYSCALLS + fprintf(stderr, + "lseek to %i (length %i, whence %i)\n", + iop->offset, + length, + whence); +#endif + if ( SEEK_END == whence ) { + /* rtems (4.6.2) libcsupport code 'lseek' uses iop->size to + * compute the offset. We don't want to track the file size + * by updating 'iop->size' constantly. + * Since lseek is the only place using iop->size, we work + * around this by tweaking the offset here... + */ + NfsNode node = iop->pathinfo.node_access; + fattr *fa = &SERP_ATTR(node); + + if (updateAttr(node, 0 /* only if old */)) { + return -1; + } + iop->offset = fa->size; + } + + /* this is particularly easy :-) */ + return iop->offset; +} + +static rtems_off64_t nfs_dir_lseek( + rtems_libio_t *iop, + rtems_off64_t length, + int whence +) +{ +DirInfo di = iop->pathinfo.node_access_2; + + /* we don't support anything other than + * rewinding + */ + if (SEEK_SET != whence || 0 != length) { + errno = ENOTSUP; + return -1; + } + + /* rewind cookie */ + memset( &di->readdirargs.cookie, + 0, + sizeof(di->readdirargs.cookie) ); + + di->eofreached = FALSE; + + return iop->offset; +} + +#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; + rtems_off64_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( + 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; +} + + +/* common for file/dir/link */ +static int nfs_fchmod( + rtems_filesystem_location_info_t *loc, + mode_t mode +) +{ +sattr arg; + + arg.mode = mode; + return nfs_sattr(loc->node_access, &arg, SATTR_MODE); + +} + +/* just set the size attribute to 'length' + * the server will take care of the rest :-) + */ +static int nfs_file_ftruncate( + rtems_libio_t *iop, + rtems_off64_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); +} + +/* files and symlinks are removed + * by the common nfs_unlink() routine. + * NFS has a different NFSPROC_RMDIR + * call, though... + */ +static int nfs_dir_rmnod( + rtems_filesystem_location_info_t *parentpathloc, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + return nfs_do_unlink(parentpathloc, pathloc, NFSPROC_RMDIR); +} + +/* the file handlers table */ +static +struct _rtems_filesystem_file_handlers_r nfs_file_file_handlers = { + nfs_file_open, /* OPTIONAL; may be defaulted */ + nfs_file_close, /* OPTIONAL; may be defaulted */ + nfs_file_read, /* OPTIONAL; may be defaulted */ + nfs_file_write, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_ioctl, + nfs_file_lseek, /* OPTIONAL; may be defaulted */ + nfs_fstat, /* OPTIONAL; may be defaulted */ + nfs_fchmod, /* OPTIONAL; may be defaulted */ + nfs_file_ftruncate, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + nfs_unlink, /* OPTIONAL; may be defaulted */ +}; + +/* the directory handlers table */ +static +struct _rtems_filesystem_file_handlers_r nfs_dir_file_handlers = { + nfs_dir_open, /* OPTIONAL; may be defaulted */ + nfs_dir_close, /* OPTIONAL; may be defaulted */ + nfs_dir_read, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_write, + rtems_filesystem_default_ioctl, + nfs_dir_lseek, /* OPTIONAL; may be defaulted */ + nfs_fstat, /* OPTIONAL; may be defaulted */ + nfs_fchmod, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + nfs_dir_rmnod, /* OPTIONAL; may be defaulted */ +}; + +/* the link handlers table */ +static +struct _rtems_filesystem_file_handlers_r nfs_link_file_handlers = { + rtems_filesystem_default_open, + rtems_filesystem_default_close, + rtems_filesystem_default_read, + rtems_filesystem_default_write, + rtems_filesystem_default_ioctl, + rtems_filesystem_default_lseek, + nfs_fstat, /* OPTIONAL; may be defaulted */ + nfs_fchmod, /* OPTIONAL; may be defaulted */ + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + nfs_unlink, /* OPTIONAL; may be defaulted */ +}; + +/* 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)) + fprintf(f,"<UNABLE TO LOOKUP MOUNTPOINT>\n"); + else + fprintf(f,"%s\n",mntpt); + } + + UNLOCK(nfsGlob.llock); + + free(mntpt); + return 0; +} + +#if 0 +CCJ_REMOVE_MOUNT +/* convenience wrapper + * + * NOTE: this routine calls NON-REENTRANT + * gethostbyname() if the host is + * not in 'dot' notation. + */ +int +nfsMount(char *uidhost, char *path, char *mntpoint) +{ +struct stat st; +int devl; +char *host; +int rval = -1; +char *dev = 0; + + if (!uidhost || !path || !mntpoint) { + fprintf(stderr,"usage: nfsMount(""[uid.gid@]host"",""path"",""mountpoint"")\n"); + nfsMountsShow(stderr); + return -1; + } + + if ( !(dev = malloc((devl=strlen(uidhost) + 20 + strlen(path)+1))) ) { + fprintf(stderr,"nfsMount: out of memory\n"); + return -1; + } + + /* Try to create the mount point if nonexistent */ + if (stat(mntpoint, &st)) { + if (ENOENT != errno) { + perror("nfsMount trying to create mount point - stat failed"); + goto cleanup; + } else if (mkdir(mntpoint,0777)) { + perror("nfsMount trying to create mount point"); + goto cleanup; + } + } + + if ( !(host=strchr(uidhost,UIDSEP)) ) { + host = uidhost; + } else { + host++; + } + + if (isdigit((unsigned char)*host)) { + /* avoid using gethostbyname */ + sprintf(dev,"%s:%s",uidhost,path); + } else { + struct hostent *h; + + /* copy the uid part (hostname will be + * overwritten) + */ + strcpy(dev, uidhost); + + /* NOTE NOTE NOTE: gethostbyname is NOT + * thread safe. This is UGLY + */ + +/* BEGIN OF NON-THREAD SAFE REGION */ + + h = gethostbyname(host); + + if ( !h || + !inet_ntop( AF_INET, + (struct in_addr*)h->h_addr_list[0], + dev + (host - uidhost), + devl - (host - uidhost) ) + ) { + fprintf(stderr,"nfsMount: host '%s' not found\n",host); + goto cleanup; + } + +/* END OF NON-THREAD SAFE REGION */ + + /* append ':<path>' */ + strcat(dev,":"); + strcat(dev,path); + } + + printf("Trying to mount %s on %s\n",dev,mntpoint); + + if (mount(dev, + mntpoint, + "nfs", + RTEMS_FILESYSTEM_READ_WRITE, + NULL)) { + perror("nfsMount - mount"); + goto cleanup; + } + + rval = 0; + +cleanup: + free(dev); + return rval; +} +#endif + +/* HERE COMES A REALLY UGLY HACK */ + +/* This is stupid; it is _very_ hard to find the path + * leading to a rtems_filesystem_location_info_t node :-( + * The only easy way is making the location the current + * directory and issue a getcwd(). + * However, since we don't want to tamper with the + * current directory, we must create a separate + * task to do the job for us - sigh. + */ + +typedef struct ResolvePathArgRec_ { + rtems_filesystem_location_info_t *loc; /* IN: location to resolve */ + char *buf; /* IN/OUT: buffer where to put the path */ + int len; /* IN: buffer length */ + rtems_id sync; /* IN: synchronization */ + rtems_status_code status; /* OUT: result */ +} ResolvePathArgRec, *ResolvePathArg; + +static void +resolve_path(rtems_task_argument arg) +{ +ResolvePathArg rpa = (ResolvePathArg)arg; +rtems_filesystem_location_info_t old; + + /* IMPORTANT: let the helper task have its own libio environment (i.e. cwd) */ + if (RTEMS_SUCCESSFUL == (rpa->status = rtems_libio_set_private_env())) { + + old = rtems_filesystem_current; + + rtems_filesystem_current = *(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 = 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/cpukit/libfs/src/nfsclient/src/nfs.modini.c b/cpukit/libfs/src/nfsclient/src/nfs.modini.c new file mode 100644 index 0000000000..22095cf53f --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/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/cpukit/libfs/src/nfsclient/src/nfsTest.c b/cpukit/libfs/src/nfsclient/src/nfsTest.c new file mode 100644 index 0000000000..a372a7511c --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/nfsTest.c @@ -0,0 +1,381 @@ +/* $Id$ */ + +/* Test program for evaluating NFS read throughput */ + +/* Author: Till Straumann <strauman@slac.stanford.edu>, 2006 */ + +/* This test code allows for evaluating NFS read performance + * under various scenarios: + * - synchronous reads with various buffer sizes (select + * 'num_readers' == 0, see below). + * - pseudo 'read-ahead' using multiple threads that issue + * NFS reads from the same file (but from different offsets) + * in parallel. + * Rationale: each NFS read request is synchronous, i.e., the + * caller sends a request to the server and waits for the + * reply to come back. Performance enhancement can be expected + * by requesting multiple blocks in parallel rather than + * sequentially. + * + * rtems_interval + * nfsTestRead(char *file_name, int chunk_size, int num_readers); + * + * 1) creates 'num_readers' threads, each opening 'file_name' for + * reading on a separate file descriptor. + * 2) creates message queues for communicating with reader threads + * + * 3) read file using nfsTestReadBigbuf() until EOF is reached + * + * 4) releases resources. + * + * RETURNS: Time elapsed during step 3 in ms. This is measured + * using the system clock so make sure the test file + * is big enough. + * + * nfsTestReadBigbuf() synchronously reads a block of + * 'num_readers * chunk_size' (which may be bigger than + * the UDP limit of 8k) using 'num_reader' threads to + * retrieve the various pieces of the big block in parallel. + * This speeds up things since several RPC calls can + * be in the works at once. + * + * NOTES: + * - if 'num_readers' == 0 this corresponds to an 'ordinary' + * NFS read. 'num_readers' == 1 schedules a single reader + * thread (== ordinary NFS read + message passing overhead). + * - no actual processing on the data is done; they are simply + * thrown away. A real, performance-critical application could + * pipeline 'reader' and 'cruncher' threads. + * - read is not completely asynchronous; synchronization is still + * performed at 'big block' boundaries (num_readers * chunk_size). + */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/error.h> + +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> + +unsigned nfsTestReaderPri = 80; + +struct nfsTestReq_ { + unsigned offset; /* file offset */ + int size; /* IN: block size to read (must be < 8192), OUT: bytes actually read */ + void *buf; /* data buffer address */ +}; + +/* Queue for sending requests to parallel reader tasks */ +rtems_id nfsTestRQ = 0; +/* Queue to pickup replies from parallel reader tasks */ +rtems_id nfsTestAQ = 0; + + +/* Reader task; opens its own file descriptor + * and works on requests: + * - obtain request from request queue. + * - lseek to the requested file offset + * - NFS read into buffer + * - queue reply. + * + * Note that this implementation is very simple + * - no full error checking. + * - file is opened/closed by thread + * it's main purpose is running quick tests. + */ +static rtems_task +nfsTestReader(rtems_task_argument arg) +{ +int fd = open((char*)arg,O_RDONLY); +unsigned long s; +struct nfsTestReq_ r; +rtems_status_code sc; + + if ( fd < 0 ) { + perror("nfsReader: opening file"); + goto cleanup; + } + do { + s = sizeof(r); + sc = rtems_message_queue_receive(nfsTestRQ, &r, &s, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "(Error) reading from message queue"); + goto cleanup; + } + if ( !r.buf ) { + /* They send a NULL buffer as a shutdown request */ + break; + } +#ifdef DEBUG + printf("Reader: reading offset %u, size %i to %p ... ", + r.offset, r.size, r.buf); +#endif + /* seek to requested offset */ + lseek(fd, r.offset, SEEK_SET); + r.size = read(fd, r.buf, r.size); +#ifdef DEBUG + printf("got %i\n",r.size); +#endif + rtems_message_queue_send(nfsTestAQ, &r, sizeof(r)); + } while (1) ; + +cleanup: + if ( fd >= 0 ) + close(fd); + rtems_task_delete(RTEMS_SELF); +} + + +/* helper to create and start a reader task */ +static rtems_id +taskSpawn(char *filenm, int inst) +{ +rtems_status_code sc; +rtems_id tid; + + sc = rtems_task_create( + rtems_build_name('n','t','t','0'+inst), + nfsTestReaderPri, + 1400, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &tid); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc,"(Error) Creating nfs reader task %i",inst); + return 0; + } + + sc = rtems_task_start(tid, nfsTestReader, (rtems_task_argument)filenm); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc,"(Error) Staritng nfs reader task %i",inst); + rtems_task_delete(tid); + return 0; + } + + return tid; +} + +/* + * Read nrd*sz bytes into 'buf' from file offset 'off' + * using 'nrd' parallel reader tasks to do the job. + * This helper routine schedules 'nrd' requests to + * the reader tasks and waits for all requests to + * finish. + * + * RETURNS: number of bytes read or -1 (error). + * + * CAVEATS: + * - assumes read requests always return 'sz' bytes + * unless the end of file is reached. + * THIS ASSUMPTION SHOULD NOT BE MADE WHEN WRITING + * ANY 'REAL' CODE. + */ +static int +nfsTestReadBigbuf(char *buf, int off, int sz, int nrd) +{ +int i,rval=0; +struct nfsTestReq_ r; + r.buf = buf; + r.size = sz; + r.offset = off; + /* send out parallel requests */ + for (i=0; i<nrd; i++) { + rtems_message_queue_send(nfsTestRQ, &r, sizeof(r)); + r.offset += sz; + r.buf += sz; + } + /* wait for answers */ + for (i=0; i<nrd; i++) { + unsigned long s = sizeof(r); + rtems_message_queue_receive(nfsTestAQ, &r, &s, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if ( r.size < 0 ) { + fprintf(stderr,"A reader failed\n"); + rval = -1; + } else { + /* FIXME sanity checks: + * - catch case where any-but-last read returns < sz + */ + if ( rval >= 0 ) { + rval += r.size; + } + } + } + return rval; +} + +/* Main test routine + * + * Read file 'fname' usint 'nrd' parallel reader tasks, + * each operating on chunks of 'sz' bytes. + * + * RETURNS: time elapsed in milliseconds. This is measured + * using the system clock. Hence, for the result + * to be meaningful, the file must be big enough. + * + */ +rtems_interval +nfsTestRead(char *fnam, int sz, int nrd) +{ +int i; +unsigned off; +rtems_interval now=-1, then, tickspsec; +rtems_status_code sc; +int fd=-1; +char *buf=0; + + if ( nrd < 0 ) + nrd = 0; + + if ( sz < 0 || sz > 8192 ) { + fprintf(stderr,"\n"); + return -1; + } + + nfsTestRQ = nfsTestAQ = 0; + + /* Allocate buffer */ + if ( ! (buf=malloc(sz*(nrd ? nrd : 1))) ) { + perror("allocating buffer"); + goto cleanup; + } + + /* Don't bother proceeding if we can't open the file for reading */ + if ( (fd=open(fnam,O_RDONLY)) < 0 ) { + perror("opening file"); + goto cleanup; + } + if ( nrd ) { + close(fd); fd = -1; + } + + /* Create request queue */ + if ( nrd ) { + sc = rtems_message_queue_create( + rtems_build_name('n','t','r','q'), + nrd, + sizeof(struct nfsTestReq_), + RTEMS_DEFAULT_ATTRIBUTES, + & nfsTestRQ ); + + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "(Error) creating request queue"); + nfsTestRQ = 0; + goto cleanup; + } + + /* Spawn reader tasks */ + for ( i=0; i<nrd; i++ ) { + if ( ! taskSpawn(fnam, i) ) + goto cleanup; + } + + /* Create reply queue */ + sc = rtems_message_queue_create( + rtems_build_name('n','t','a','q'), + nrd, + sizeof(struct nfsTestReq_), + RTEMS_DEFAULT_ATTRIBUTES, + & nfsTestAQ ); + + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "(Error) creating reply queue"); + nfsTestAQ = 0; + goto cleanup; + } + } + + /* Timed main loop */ + then = rtems_clock_get_ticks_since_boot(); + + if ( nrd ) { + off = 0; + while ((i = nfsTestReadBigbuf(buf, off, sz, nrd)) > 0 ) { +#ifdef DEBUG + printf("bigbuf got %i\n", i); +#endif + off += i; + } + } else { + while ( (i = read(fd, buf, sz)) > 0 ) + /* nothing else to do */; + if ( i < 0 ) { + perror("reading"); + goto cleanup; + } + } + + now = rtems_clock_get_ticks_since_boot(); + now = (now-then)*1000; + ticksspec = rtems_clock_get_ticks_per_second(); + now /= tickspsec; /* time in ms */ + +cleanup: + if ( fd >= 0 ) + close(fd); + + if ( nfsTestRQ ) { + /* request tasks to shutdown by sending NULL buf request */ + struct nfsTestReq_ r; + r.buf = 0; + for ( i=0; i<nrd; i++ ) { + rtems_message_queue_send( nfsTestRQ, &r, sizeof(r) ); + } + /* cheat: instead of proper synchronization with shutdown we simply + * delay for a second... + */ + rtems_task_wake_after( tickspsec ); + rtems_message_queue_delete( nfsTestRQ ); + } + if ( nfsTestAQ ) + rtems_message_queue_delete( nfsTestAQ ); + free(buf); + return now; +} diff --git a/cpukit/libfs/src/nfsclient/src/rpcio.c b/cpukit/libfs/src/nfsclient/src/rpcio.c new file mode 100644 index 0000000000..3316cccb5f --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/rpcio.c @@ -0,0 +1,1792 @@ +/* $Id$ */ + +/* RPC multiplexor for a multitasking environment */ + +/* Author: Till Straumann <strauman@slac.stanford.edu>, 2002 */ + +/* This code funnels arbitrary task's UDP/RPC requests + * through one socket to arbitrary servers. + * The replies are gathered and dispatched to the + * requestors. + * One task handles all the sending and receiving + * work including retries. + * It is up to the requestor, however, to do + * the XDR encoding of the arguments / decoding + * of the results (except for the RPC header which + * is handled by the daemon). + */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> +#include <stdlib.h> +#include <time.h> +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <assert.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "rpcio.h" + +/****************************************************************/ +/* CONFIGURABLE PARAMETERS */ +/****************************************************************/ + +#define MBUF_RX /* If defined: use mbuf XDR stream for + * decoding directly out of mbufs + * Otherwise, the regular 'recvfrom()' + * interface will be used involving an + * extra buffer allocation + copy step. + */ + +#define MBUF_TX /* If defined: avoid copying data when + * sending. Instead, use a wrapper to + * 'sosend()' which will point an MBUF + * directly to our buffer space. + * Note that the BSD stack does not copy + * data when fragmenting packets - it + * merely uses an mbuf chain pointing + * into different areas of the data. + * + * If undefined, the regular 'sendto()' + * interface is used. + */ + +#undef REJECT_SERVERIP_MISMATCH + /* If defined, RPC replies must come from the server + * that was queried. Eric Norum has reported problems + * with clustered NFS servers. So we disable this + * reducing paranoia... + */ + +/* daemon task parameters */ +#define RPCIOD_STACK 10000 +#define RPCIOD_PRIO 100 /* *fallback* priority */ + +/* depth of the message queue for sending + * RPC requests to the daemon + */ +#define RPCIOD_QDEPTH 20 + +/* Maximum retry limit for retransmission */ +#define RPCIOD_RETX_CAP_S 3 /* seconds */ + +/* Default timeout for RPC calls */ +#define RPCIOD_DEFAULT_TIMEOUT (&_rpc_default_timeout) +static struct timeval _rpc_default_timeout = { 10 /* secs */, 0 /* usecs */ }; + +/* how many times should we try to resend a failed + * transaction with refreshed AUTHs + */ +#define RPCIOD_REFRESH 2 + +/* Events we are using; the RPC_EVENT + * MUST NOT be used by any application + * thread doing RPC IO (e.g. NFS) + */ +#define RTEMS_RPC_EVENT RTEMS_EVENT_30 /* THE event used by RPCIO. Every task doing + * RPC IO will receive this - hence it is + * RESERVED + */ +#define RPCIOD_RX_EVENT RTEMS_EVENT_1 /* Events the RPCIOD is using/waiting for */ +#define RPCIOD_TX_EVENT RTEMS_EVENT_2 +#define RPCIOD_KILL_EVENT RTEMS_EVENT_3 /* send to the daemon to kill it */ + +#define LD_XACT_HASH 8 /* ld of the size of the transaction hash table */ + + +/* Debugging Flags */ + +/* NOTE: defining DEBUG 0 leaves some 'assert()' paranoia checks + * but produces no output + */ + +#define DEBUG_TRACE_XACT (1<<0) +#define DEBUG_EVENTS (1<<1) +#define DEBUG_MALLOC (1<<2) +#define DEBUG_TIMEOUT (1<<3) +#define DEBUG_PACKLOSS (1<<4) /* This introduces random, artificial packet losses to test retransmission */ + +#define DEBUG_PACKLOSS_FRACT (0xffffffff/10) + +/* USE PARENTHESIS WHEN 'or'ing MULTIPLE FLAGS: (DEBUG_XX | DEBUG_YY) */ +#define DEBUG (0) + +/****************************************************************/ +/* END OF CONFIGURABLE SECTION */ +/****************************************************************/ + +/* prevent rollover of our timers by readjusting the epoch on the fly */ +#if (DEBUG) & DEBUG_TIMEOUT +#define RPCIOD_EPOCH_SECS 10 +#else +#define RPCIOD_EPOCH_SECS 10000 +#endif + +#ifdef DEBUG +#define ASSERT(arg) assert(arg) +#else +#define ASSERT(arg) if (arg) +#endif + +/****************************************************************/ +/* MACROS */ +/****************************************************************/ + + +#define XACT_HASHS (1<<(LD_XACT_HASH)) /* the hash table size derived from the ld */ +#define XACT_HASH_MSK ((XACT_HASHS)-1) /* mask to extract the hash index from a RPC-XID */ + + +#define MU_LOCK(mutex) do { \ + assert( \ + RTEMS_SUCCESSFUL == \ + rtems_semaphore_obtain( \ + (mutex), \ + RTEMS_WAIT, \ + RTEMS_NO_TIMEOUT \ + ) ); \ + } while(0) + +#define MU_UNLOCK(mutex) do { \ + assert( \ + RTEMS_SUCCESSFUL == \ + rtems_semaphore_release( \ + (mutex) \ + ) ); \ + } while(0) + +#define MU_CREAT(pmutex) do { \ + assert( \ + RTEMS_SUCCESSFUL == \ + rtems_semaphore_create( \ + rtems_build_name( \ + 'R','P','C','l' \ + ), \ + 1, \ + MUTEX_ATTRIBUTES, \ + 0, \ + (pmutex)) ); \ + } while (0) + + +#define MU_DESTROY(mutex) do { \ + assert( \ + RTEMS_SUCCESSFUL == \ + rtems_semaphore_delete( \ + mutex \ + ) ); \ + } while (0) + +#define MUTEX_ATTRIBUTES (RTEMS_LOCAL | \ + RTEMS_PRIORITY | \ + RTEMS_INHERIT_PRIORITY | \ + RTEMS_BINARY_SEMAPHORE) + +#define FIRST_ATTEMPT 0x88888888 /* some time that is never reached */ + +/****************************************************************/ +/* TYPE DEFINITIONS */ +/****************************************************************/ + +typedef rtems_interval TimeoutT; + +/* 100000th implementation of a doubly linked list; + * since only one thread is looking at these, + * we need no locking + */ +typedef struct ListNodeRec_ { + struct ListNodeRec_ *next, *prev; +} ListNodeRec, *ListNode; + + +/* Structure representing an RPC server */ +typedef struct RpcUdpServerRec_ { + RpcUdpServer next; /* linked list of all servers; protected by hlock */ + union { + struct sockaddr_in sin; + struct sockaddr sa; + } addr; + AUTH *auth; + rtems_id authlock; /* must MUTEX the auth object - it's not clear + * what is better: + * 1 having one (MUTEXed) auth per server + * who is shared among all transactions + * using that server + * 2 maintaining an AUTH per transaction + * (there are then other options: manage + * XACT pools on a per-server basis instead + * of associating a server with a XACT when + * sending) + * experience will show if the current (1) + * approach has to be changed. + */ + TimeoutT retry_period; /* dynamically adjusted retry period + * (based on packet roundtrip time) + */ + /* STATISTICS */ + unsigned long retrans; /* how many retries were issued by this server */ + unsigned long requests; /* how many requests have been sent */ + unsigned long timeouts; /* how many requests have timed out */ + unsigned long errors; /* how many errors have occurred (other than timeouts) */ + char name[20]; /* server's address in IP 'dot' notation */ +} RpcUdpServerRec; + +typedef union RpcBufU_ { + uint32_t xid; + char buf[1]; +} RpcBufU, *RpcBuf; + +/* RX Buffer implementation; this is either + * an MBUF chain (MBUF_RX configuration) + * or a buffer allocated from the heap + * where recvfrom copies the (encoded) reply + * to. The XDR routines the copy/decode + * it into the user's data structures. + */ +#ifdef MBUF_RX +typedef struct mbuf * RxBuf; /* an MBUF chain */ +static void bufFree(struct mbuf **m); +#define XID(ibuf) (*(mtod((ibuf), u_long *))) +extern void xdrmbuf_create(XDR *, struct mbuf *, enum xdr_op); +#else +typedef RpcBuf RxBuf; +#define bufFree(b) do { MY_FREE(*(b)); *(b)=0; } while(0) +#define XID(ibuf) ((ibuf)->xid) +#endif + +/* A RPC 'transaction' consisting + * of server and requestor information, + * buffer space and an XDR object + * (for encoding arguments). + */ +typedef struct RpcUdpXactRec_ { + ListNodeRec node; /* so we can put XACTs on a list */ + RpcUdpServer server; /* server this XACT goes to */ + long lifetime; /* during the lifetime, retry attempts are made */ + long tolive; /* lifetime timer */ + struct rpc_err status; /* RPC reply error status */ + long age; /* age info; needed to manage retransmission */ + long trip; /* record round trip time in ticks */ + rtems_id requestor; /* the task waiting for this XACT to complete */ + RpcUdpXactPool pool; /* if this XACT belong to a pool, this is it */ + XDR xdrs; /* argument encoder stream */ + int xdrpos; /* stream position after the (permanent) header */ + xdrproc_t xres; /* reply decoder proc - TODO needn't be here */ + caddr_t pres; /* reply decoded obj - TODO needn't be here */ +#ifndef MBUF_RX + int ibufsize; /* size of the ibuf (bytes) */ +#endif +#ifdef MBUF_TX + int refcnt; /* mbuf external storage reference count */ +#endif + int obufsize; /* size of the obuf (bytes) */ + RxBuf ibuf; /* pointer to input buffer assigned by daemon */ + RpcBufU obuf; /* output buffer (encoded args) APPENDED HERE */ +} RpcUdpXactRec; + +typedef struct RpcUdpXactPoolRec_ { + rtems_id box; + int prog; + int version; + int xactSize; +} RpcUdpXactPoolRec; + +/* a global hash table where all 'living' transaction + * objects are registered. + * A number of bits in a transaction's XID maps 1:1 to + * an index in this table. Hence, the XACT matching + * an RPC/UDP reply packet can quickly be found + * The size of this table imposes a hard limit on the + * number of all created transactions in the system. + */ +static RpcUdpXact xactHashTbl[XACT_HASHS]={0}; +static u_long xidUpper [XACT_HASHS]={0}; +static unsigned xidHashSeed = 0 ; + +/* forward declarations */ +static RpcUdpXact +sockRcv(void); + +static void +rpcio_daemon(rtems_task_argument); + +#ifdef MBUF_TX +ssize_t +sendto_nocpy ( + int s, + const void *buf, size_t buflen, + int flags, + const struct sockaddr *toaddr, int tolen, + void *closure, + void (*freeproc)(caddr_t, u_int), + void (*refproc)(caddr_t, u_int) +); +static void paranoia_free(caddr_t closure, u_int size); +static void paranoia_ref (caddr_t closure, u_int size); +#define SENDTO sendto_nocpy +#else +#define SENDTO sendto +#endif + +static RpcUdpServer rpcUdpServers = 0; /* linked list of all servers; protected by llock */ + +static int ourSock = -1; /* the socket we are using for communication */ +static rtems_id rpciod = 0; /* task id of the RPC daemon */ +static rtems_id msgQ = 0; /* message queue where the daemon picks up + * requests + */ +#ifndef NDEBUG +static rtems_id llock = 0; /* MUTEX protecting the server list */ +static rtems_id hlock = 0; /* MUTEX protecting the hash table and the list of servers */ +#endif +static rtems_id fini = 0; /* a synchronization semaphore we use during + * module cleanup / driver unloading + */ +static rtems_interval ticksPerSec; /* cached system clock rate (WHO IS ASSUMED NOT + * TO CHANGE) + */ + +rtems_task_priority rpciodPriority = 0; + +#if (DEBUG) & DEBUG_MALLOC +/* malloc wrappers for debugging */ +static int nibufs = 0; + +static inline void *MY_MALLOC(int s) +{ + if (s) { + void *rval; + MU_LOCK(hlock); + assert(nibufs++ < 2000); + MU_UNLOCK(hlock); + assert((rval = malloc(s)) != 0); + return rval; + } + return 0; +} + +static inline void *MY_CALLOC(int n, int s) +{ + if (s) { + void *rval; + MU_LOCK(hlock); + assert(nibufs++ < 2000); + MU_UNLOCK(hlock); + assert((rval = calloc(n,s)) != 0); + return rval; + } + return 0; +} + + +static inline void MY_FREE(void *p) +{ + if (p) { + MU_LOCK(hlock); + nibufs--; + MU_UNLOCK(hlock); + free(p); + } +} +#else +#define MY_MALLOC malloc +#define MY_CALLOC calloc +#define MY_FREE free +#endif + +static inline bool_t +locked_marshal(RpcUdpServer s, XDR *xdrs) +{ +bool_t rval; + MU_LOCK(s->authlock); + rval = AUTH_MARSHALL(s->auth, xdrs); + MU_UNLOCK(s->authlock); + return rval; +} + +/* Locked operations on a server's auth object */ +static inline bool_t +locked_validate(RpcUdpServer s, struct opaque_auth *v) +{ +bool_t rval; + MU_LOCK(s->authlock); + rval = AUTH_VALIDATE(s->auth, v); + MU_UNLOCK(s->authlock); + return rval; +} + +static inline bool_t +locked_refresh(RpcUdpServer s) +{ +bool_t rval; + MU_LOCK(s->authlock); + rval = AUTH_REFRESH(s->auth); + MU_UNLOCK(s->authlock); + return rval; +} + +/* Create a server object + * + */ +enum clnt_stat +rpcUdpServerCreate( + struct sockaddr_in *paddr, + rpcprog_t prog, + rpcvers_t vers, + u_long uid, + u_long gid, + RpcUdpServer *psrv + ) +{ +RpcUdpServer rval; +u_short port; +char hname[MAX_MACHINE_NAME + 1]; +int theuid, thegid; +int thegids[NGRPS]; +gid_t gids[NGROUPS]; +int len,i; +AUTH *auth; +enum clnt_stat pmap_err; +struct pmap pmaparg; + + if ( gethostname(hname, MAX_MACHINE_NAME) ) { + fprintf(stderr, + "RPCIO - error: I have no hostname ?? (%s)\n", + strerror(errno)); + return RPC_UNKNOWNHOST; + } + + if ( (len = getgroups(NGROUPS, gids) < 0 ) ) { + fprintf(stderr, + "RPCIO - error: I unable to get group ids (%s)\n", + strerror(errno)); + return RPC_FAILED; + } + + if ( len > NGRPS ) + len = NGRPS; + + for (i=0; i<len; i++) + thegids[i] = (int)gids[i]; + + theuid = (int) ((RPCIOD_DEFAULT_ID == uid) ? geteuid() : uid); + thegid = (int) ((RPCIOD_DEFAULT_ID == gid) ? getegid() : gid); + + if ( !(auth = authunix_create(hname, theuid, thegid, len, thegids)) ) { + fprintf(stderr, + "RPCIO - error: unable to create RPC AUTH\n"); + return RPC_FAILED; + } + + /* if they specified no port try to ask the portmapper */ + if (!paddr->sin_port) { + + paddr->sin_port = htons(PMAPPORT); + + pmaparg.pm_prog = prog; + pmaparg.pm_vers = vers; + pmaparg.pm_prot = IPPROTO_UDP; + pmaparg.pm_port = 0; /* not needed or used */ + + + /* dont use non-reentrant pmap_getport ! */ + + pmap_err = rpcUdpCallRp( + paddr, + PMAPPROG, + PMAPVERS, + PMAPPROC_GETPORT, + xdr_pmap, + &pmaparg, + xdr_u_short, + &port, + uid, + gid, + 0); + + if ( RPC_SUCCESS != pmap_err ) { + paddr->sin_port = 0; + return pmap_err; + } + + paddr->sin_port = htons(port); + } + + if (0==paddr->sin_port) { + return RPC_PROGNOTREGISTERED; + } + + rval = (RpcUdpServer)MY_MALLOC(sizeof(*rval)); + memset(rval, 0, sizeof(*rval)); + + if (!inet_ntop(AF_INET, &paddr->sin_addr, rval->name, sizeof(rval->name))) + sprintf(rval->name,"?.?.?.?"); + rval->addr.sin = *paddr; + + /* start with a long retransmission interval - it + * will be adapted dynamically + */ + rval->retry_period = RPCIOD_RETX_CAP_S * ticksPerSec; + + rval->auth = auth; + + MU_CREAT( &rval->authlock ); + + /* link into list */ + MU_LOCK( llock ); + rval->next = rpcUdpServers; + rpcUdpServers = rval; + MU_UNLOCK( llock ); + + *psrv = rval; + return RPC_SUCCESS; +} + +void +rpcUdpServerDestroy(RpcUdpServer s) +{ +RpcUdpServer prev; + if (!s) + return; + /* we should probably verify (but how?) that nobody + * (at least: no outstanding XACTs) is using this + * server; + */ + + /* remove from server list */ + MU_LOCK(llock); + prev = rpcUdpServers; + if ( s == prev ) { + rpcUdpServers = s->next; + } else { + for ( ; prev ; prev = prev->next) { + if (prev->next == s) { + prev->next = s->next; + break; + } + } + } + MU_UNLOCK(llock); + + /* MUST have found it */ + assert(prev); + + auth_destroy(s->auth); + + MU_DESTROY(s->authlock); + MY_FREE(s); +} + +int +rpcUdpStats(FILE *f) +{ +RpcUdpServer s; + + if (!f) f = stdout; + + fprintf(f,"RPCIOD statistics:\n"); + + MU_LOCK(llock); + for (s = rpcUdpServers; s; s=s->next) { + fprintf(f,"\nServer -- %s:\n", s->name); + fprintf(f," requests sent: %10ld, retransmitted: %10ld\n", + s->requests, s->retrans); + fprintf(f," timed out: %10ld, send errors: %10ld\n", + s->timeouts, s->errors); + fprintf(f," current retransmission interval: %dms\n", + (unsigned)(s->retry_period * 1000 / ticksPerSec) ); + } + MU_UNLOCK(llock); + + return 0; +} + +RpcUdpXact +rpcUdpXactCreate( + u_long program, + u_long version, + u_long size + ) +{ +RpcUdpXact rval=0; +struct rpc_msg header; +register int i,j; + + if (!size) + size = UDPMSGSIZE; + /* word align */ + size = (size + 3) & ~3; + + rval = (RpcUdpXact)MY_CALLOC(1,sizeof(*rval) - sizeof(rval->obuf) + size); + + if (rval) { + + header.rm_xid = 0; + header.rm_direction = CALL; + header.rm_call.cb_rpcvers = RPC_MSG_VERSION; + header.rm_call.cb_prog = program; + header.rm_call.cb_vers = version; + xdrmem_create(&(rval->xdrs), rval->obuf.buf, size, XDR_ENCODE); + + if (!xdr_callhdr(&(rval->xdrs), &header)) { + MY_FREE(rval); + return 0; + } + /* pick a free table slot and initialize the XID */ + rval->obuf.xid = time(0) ^ (uintptr_t)rval; + MU_LOCK(hlock); + rval->obuf.xid = (xidHashSeed++ ^ ((uintptr_t)rval>>10)) & XACT_HASH_MSK; + i=j=(rval->obuf.xid & XACT_HASH_MSK); + if (msgQ) { + /* if there's no message queue, refuse to + * give them transactions; we might be in the process to + * go away... + */ + do { + i=(i+1) & XACT_HASH_MSK; /* cheap modulo */ + if (!xactHashTbl[i]) { +#if (DEBUG) & DEBUG_TRACE_XACT + fprintf(stderr,"RPCIO: entering index %i, val %x\n",i,rval); +#endif + xactHashTbl[i]=rval; + j=-1; + break; + } + } while (i!=j); + } + MU_UNLOCK(hlock); + if (i==j) { + XDR_DESTROY(&rval->xdrs); + MY_FREE(rval); + return 0; + } + rval->obuf.xid = xidUpper[i] | i; + rval->xdrpos = XDR_GETPOS(&(rval->xdrs)); + rval->obufsize = size; + } + return rval; +} + +void +rpcUdpXactDestroy(RpcUdpXact xact) +{ +int i = xact->obuf.xid & XACT_HASH_MSK; + +#if (DEBUG) & DEBUG_TRACE_XACT + fprintf(stderr,"RPCIO: removing index %i, val %x\n",i,xact); +#endif + + ASSERT( xactHashTbl[i]==xact ); + + MU_LOCK(hlock); + xactHashTbl[i]=0; + /* remember XID we used last time so we can avoid + * reusing the same one (incremented by rpcUdpSend routine) + */ + xidUpper[i] = xact->obuf.xid & ~XACT_HASH_MSK; + MU_UNLOCK(hlock); + + bufFree(&xact->ibuf); + + XDR_DESTROY(&xact->xdrs); + MY_FREE(xact); +} + + + +/* Send a transaction, i.e. enqueue it to the + * RPC daemon who will actually send it. + */ +enum clnt_stat +rpcUdpSend( + RpcUdpXact xact, + RpcUdpServer srvr, + struct timeval *timeout, + u_long proc, + xdrproc_t xres, caddr_t pres, + xdrproc_t xargs, caddr_t pargs, + ... + ) +{ +register XDR *xdrs; +unsigned long ms; +va_list ap; + + va_start(ap,pargs); + + if (!timeout) + timeout = RPCIOD_DEFAULT_TIMEOUT; + + ms = 1000 * timeout->tv_sec + timeout->tv_usec/1000; + + /* round lifetime to closest # of ticks */ + xact->lifetime = (ms * ticksPerSec + 500) / 1000; + if ( 0 == xact->lifetime ) + xact->lifetime = 1; + +#if (DEBUG) & DEBUG_TIMEOUT + { + static int once=0; + if (!once++) { + fprintf(stderr, + "Initial lifetime: %i (ticks)\n", + xact->lifetime); + } + } +#endif + + xact->tolive = xact->lifetime; + + xact->xres = xres; + xact->pres = pres; + xact->server = srvr; + + xdrs = &xact->xdrs; + xdrs->x_op = XDR_ENCODE; + /* increment transaction ID */ + xact->obuf.xid += XACT_HASHS; + XDR_SETPOS(xdrs, xact->xdrpos); + if ( !XDR_PUTLONG(xdrs,(long*)&proc) || !locked_marshal(srvr, xdrs) || + !xargs(xdrs, pargs) ) { + va_end(ap); + return(xact->status.re_status=RPC_CANTENCODEARGS); + } + while ((xargs=va_arg(ap,xdrproc_t))) { + if (!xargs(xdrs, va_arg(ap,caddr_t))) + va_end(ap); + return(xact->status.re_status=RPC_CANTENCODEARGS); + } + + va_end(ap); + + rtems_task_ident(RTEMS_SELF, RTEMS_WHO_AM_I, &xact->requestor); + if ( rtems_message_queue_send( msgQ, &xact, sizeof(xact)) ) { + return RPC_CANTSEND; + } + /* wakeup the rpciod */ + ASSERT( RTEMS_SUCCESSFUL==rtems_event_send(rpciod, RPCIOD_TX_EVENT) ); + + return RPC_SUCCESS; +} + +/* Block for the RPC reply to an outstanding + * transaction. + * The caller is woken by the RPC daemon either + * upon reception of the reply or on timeout. + */ +enum clnt_stat +rpcUdpRcv(RpcUdpXact xact) +{ +int refresh; +XDR reply_xdrs; +struct rpc_msg reply_msg; +rtems_status_code status; +rtems_event_set gotEvents; + + refresh = 0; + + do { + + /* block for the reply */ + status = rtems_event_receive( + RTEMS_RPC_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &gotEvents); + ASSERT( status == RTEMS_SUCCESSFUL ); + + if (xact->status.re_status) { +#ifdef MBUF_RX + /* add paranoia */ + ASSERT( !xact->ibuf ); +#endif + return xact->status.re_status; + } + +#ifdef MBUF_RX + xdrmbuf_create(&reply_xdrs, xact->ibuf, XDR_DECODE); +#else + xdrmem_create(&reply_xdrs, xact->ibuf->buf, xact->ibufsize, XDR_DECODE); +#endif + + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = xact->pres; + reply_msg.acpted_rply.ar_results.proc = xact->xres; + + if (xdr_replymsg(&reply_xdrs, &reply_msg)) { + /* OK */ + _seterr_reply(&reply_msg, &xact->status); + if (RPC_SUCCESS == xact->status.re_status) { + if ( !locked_validate(xact->server, + &reply_msg.acpted_rply.ar_verf) ) { + xact->status.re_status = RPC_AUTHERROR; + xact->status.re_why = AUTH_INVALIDRESP; + } + if (reply_msg.acpted_rply.ar_verf.oa_base) { + reply_xdrs.x_op = XDR_FREE; + xdr_opaque_auth(&reply_xdrs, &reply_msg.acpted_rply.ar_verf); + } + refresh = 0; + } else { + /* should we try to refresh our credentials ? */ + if ( !refresh ) { + /* had never tried before */ + refresh = RPCIOD_REFRESH; + } + } + } else { + reply_xdrs.x_op = XDR_FREE; + xdr_replymsg(&reply_xdrs, &reply_msg); + xact->status.re_status = RPC_CANTDECODERES; + } + XDR_DESTROY(&reply_xdrs); + + bufFree(&xact->ibuf); + +#ifndef MBUF_RX + xact->ibufsize = 0; +#endif + + if (refresh && locked_refresh(xact->server)) { + rtems_task_ident(RTEMS_SELF, RTEMS_WHO_AM_I, &xact->requestor); + if ( rtems_message_queue_send(msgQ, &xact, sizeof(xact)) ) { + return RPC_CANTSEND; + } + /* wakeup the rpciod */ + fprintf(stderr,"RPCIO INFO: refreshing my AUTH\n"); + ASSERT( RTEMS_SUCCESSFUL==rtems_event_send(rpciod, RPCIOD_TX_EVENT) ); + } + + } while ( 0 && refresh-- > 0 ); + + return xact->status.re_status; +} + + +/* On RTEMS, I'm told to avoid select(); this seems to + * be more efficient + */ +static void +rxWakeupCB(struct socket *sock, void *arg) +{ + rtems_id *rpciod = (rtems_id*) arg; + rtems_event_send(*rpciod, RPCIOD_RX_EVENT); +} + +int +rpcUdpInit(void) +{ +int s; +rtems_status_code status; +int noblock = 1; +struct sockwakeup wkup; + + if (ourSock < 0) { + fprintf(stderr,"RTEMS-RPCIOD $Release$, " \ + "Till Straumann, Stanford/SLAC/SSRL 2002, " \ + "See LICENSE file for licensing info.\n"); + + ourSock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (ourSock>=0) { + bindresvport(ourSock,(struct sockaddr_in*)0); + s = ioctl(ourSock, FIONBIO, (char*)&noblock); + assert( s == 0 ); + /* assume nobody tampers with the clock !! */ + ticksPerSec = rtems_clock_get_ticks_per_second(); + MU_CREAT( &hlock ); + MU_CREAT( &llock ); + + if ( !rpciodPriority ) { + /* use configured networking priority */ + if ( ! (rpciodPriority = rtems_bsdnet_config.network_task_priority) ) + rpciodPriority = RPCIOD_PRIO; /* fallback value */ + } + + status = rtems_task_create( + rtems_build_name('R','P','C','d'), + rpciodPriority, + RPCIOD_STACK, + RTEMS_DEFAULT_MODES, + /* fprintf saves/restores FP registers on PPC :-( */ + RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, + &rpciod); + assert( status == RTEMS_SUCCESSFUL ); + + wkup.sw_pfn = rxWakeupCB; + wkup.sw_arg = &rpciod; + assert( 0==setsockopt(ourSock, SOL_SOCKET, SO_RCVWAKEUP, &wkup, sizeof(wkup)) ); + status = rtems_message_queue_create( + rtems_build_name('R','P','C','q'), + RPCIOD_QDEPTH, + sizeof(RpcUdpXact), + RTEMS_DEFAULT_ATTRIBUTES, + &msgQ); + assert( status == RTEMS_SUCCESSFUL ); + status = rtems_task_start( rpciod, rpcio_daemon, 0 ); + assert( status == RTEMS_SUCCESSFUL ); + + } else { + return -1; + } + } + return 0; +} + +int +rpcUdpCleanup(void) +{ + rtems_semaphore_create( + rtems_build_name('R','P','C','f'), + 0, + RTEMS_DEFAULT_ATTRIBUTES, + 0, + &fini); + rtems_event_send(rpciod, RPCIOD_KILL_EVENT); + /* synchronize with daemon */ + rtems_semaphore_obtain(fini, RTEMS_WAIT, 5*ticksPerSec); + /* if the message queue is still there, something went wrong */ + if (!msgQ) { + rtems_task_delete(rpciod); + } + rtems_semaphore_delete(fini); + return (msgQ !=0); +} + +/* Another API - simpler but less efficient. + * For each RPCall, a server and a Xact + * are created and destroyed on the fly. + * + * This should be used for infrequent calls + * (e.g. a NFS mount request). + * + * This is roughly compatible with the original + * clnt_call() etc. API - but it uses our + * daemon and is fully reentrant. + */ +enum clnt_stat +rpcUdpClntCreate( + struct sockaddr_in *psaddr, + rpcprog_t prog, + rpcvers_t vers, + u_long uid, + u_long gid, + RpcUdpClnt *pclnt +) +{ +RpcUdpXact x; +RpcUdpServer s; +enum clnt_stat err; + + if ( RPC_SUCCESS != (err=rpcUdpServerCreate(psaddr, prog, vers, uid, gid, &s)) ) + return err; + + if ( !(x=rpcUdpXactCreate(prog, vers, UDPMSGSIZE)) ) { + rpcUdpServerDestroy(s); + return RPC_FAILED; + } + /* TODO: could maintain a server cache */ + + x->server = s; + + *pclnt = x; + + return RPC_SUCCESS; +} + +void +rpcUdpClntDestroy(RpcUdpClnt xact) +{ + rpcUdpServerDestroy(xact->server); + rpcUdpXactDestroy(xact); +} + +enum clnt_stat +rpcUdpClntCall( + RpcUdpClnt xact, + u_long proc, + XdrProcT xargs, + CaddrT pargs, + XdrProcT xres, + CaddrT pres, + struct timeval *timeout + ) +{ +enum clnt_stat stat; + + if ( (stat = rpcUdpSend(xact, xact->server, timeout, proc, + xres, pres, + xargs, pargs, + 0)) ) { + fprintf(stderr,"RPCIO Send failed: %i\n",stat); + return stat; + } + return rpcUdpRcv(xact); +} + +/* a yet simpler interface */ +enum clnt_stat +rpcUdpCallRp( + struct sockaddr_in *psrvr, + u_long prog, + u_long vers, + u_long proc, + XdrProcT xargs, + CaddrT pargs, + XdrProcT xres, + CaddrT pres, + u_long uid, /* RPCIO_DEFAULT_ID picks default */ + u_long gid, /* RPCIO_DEFAULT_ID picks default */ + struct timeval *timeout /* NULL picks default */ +) +{ +RpcUdpClnt clp; +enum clnt_stat stat; + + stat = rpcUdpClntCreate( + psrvr, + prog, + vers, + uid, + gid, + &clp); + + if ( RPC_SUCCESS != stat ) + return stat; + + stat = rpcUdpClntCall( + clp, + proc, + xargs, pargs, + xres, pres, + timeout); + + rpcUdpClntDestroy(clp); + + return stat; +} + +/* linked list primitives */ +static void +nodeXtract(ListNode n) +{ + if (n->prev) + n->prev->next = n->next; + if (n->next) + n->next->prev = n->prev; + n->next = n->prev = 0; +} + +static void +nodeAppend(ListNode l, ListNode n) +{ + if ( (n->next = l->next) ) + n->next->prev = n; + l->next = n; + n->prev = l; + +} + +/* this code does the work */ +static void +rpcio_daemon(rtems_task_argument arg) +{ +rtems_status_code stat; +RpcUdpXact xact; +RpcUdpServer srv; +rtems_interval next_retrans, then, unow; +long now; /* need to do signed comparison with age! */ +rtems_event_set events; +ListNode newList; +size_t size; +rtems_id q = 0; +ListNodeRec listHead = {0, 0}; +unsigned long epoch = RPCIOD_EPOCH_SECS * ticksPerSec; +unsigned long max_period = RPCIOD_RETX_CAP_S * ticksPerSec; +rtems_status_code status; + + + then = rtems_clock_get_ticks_since_boot(); + + for (next_retrans = epoch;;) { + + if ( RTEMS_SUCCESSFUL != + (stat = rtems_event_receive( + RPCIOD_RX_EVENT | RPCIOD_TX_EVENT | RPCIOD_KILL_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + next_retrans, + &events)) ) { + ASSERT( RTEMS_TIMEOUT == stat ); + events = 0; + } + + if (events & RPCIOD_KILL_EVENT) { + int i; + +#if (DEBUG) & DEBUG_EVENTS + fprintf(stderr,"RPCIO: got KILL event\n"); +#endif + + MU_LOCK(hlock); + for (i=XACT_HASHS-1; i>=0; i--) { + if (xactHashTbl[i]) { + break; + } + } + if (i<0) { + /* prevent them from creating and enqueueing more messages */ + q=msgQ; + /* messages queued after we executed this assignment will fail */ + msgQ=0; + } + MU_UNLOCK(hlock); + if (i>=0) { + fprintf(stderr,"RPCIO There are still transactions circulating; I refuse to go away\n"); + fprintf(stderr,"(1st in slot %i)\n",i); + rtems_semaphore_release(fini); + } else { + break; + } + } + + unow = rtems_clock_get_ticks_since_boot(); + + /* measure everything relative to then to protect against + * rollover + */ + now = unow - then; + + /* NOTE: we don't lock the hash table while we are operating + * on transactions; the paradigm is that we 'own' a particular + * transaction (and hence it's hash table slot) from the + * time the xact was put into the message queue until we + * wake up the requestor. + */ + + if (RPCIOD_RX_EVENT & events) { + +#if (DEBUG) & DEBUG_EVENTS + fprintf(stderr,"RPCIO: got RX event\n"); +#endif + + while ((xact=sockRcv())) { + + /* extract from the retransmission list */ + nodeXtract(&xact->node); + + /* change the ID - there might already be + * a retransmission on the way. When it's + * reply arrives we must not find it's ID + * in the hashtable + */ + xact->obuf.xid += XACT_HASHS; + + xact->status.re_status = RPC_SUCCESS; + + /* calculate roundtrip ticks */ + xact->trip = now - xact->trip; + + srv = xact->server; + + /* adjust the server's retry period */ + { + register TimeoutT rtry = srv->retry_period; + register TimeoutT trip = xact->trip; + + ASSERT( trip >= 0 ); + + if ( 0==trip ) + trip = 1; + + /* retry_new = 0.75*retry_old + 0.25 * 8 * roundrip */ + rtry = (3*rtry + (trip << 3)) >> 2; + + if ( rtry > max_period ) + rtry = max_period; + + srv->retry_period = rtry; + } + + /* wakeup requestor */ + rtems_event_send(xact->requestor, RTEMS_RPC_EVENT); + } + } + + if (RPCIOD_TX_EVENT & events) { + +#if (DEBUG) & DEBUG_EVENTS + fprintf(stderr,"RPCIO: got TX event\n"); +#endif + + while (RTEMS_SUCCESSFUL == rtems_message_queue_receive( + msgQ, + &xact, + &size, + RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT)) { + /* put to the head of timeout q */ + nodeAppend(&listHead, &xact->node); + + xact->age = now; + xact->trip = FIRST_ATTEMPT; + } + } + + + /* work the timeout q */ + newList = 0; + for ( xact=(RpcUdpXact)listHead.next; + xact && xact->age <= now; + xact=(RpcUdpXact)listHead.next ) { + + /* extract from the list */ + nodeXtract(&xact->node); + + srv = xact->server; + + if (xact->tolive < 0) { + /* this one timed out */ + xact->status.re_errno = ETIMEDOUT; + xact->status.re_status = RPC_TIMEDOUT; + + srv->timeouts++; + + /* Change the ID - there might still be + * a reply on the way. When it arrives we + * must not find it's ID in the hash table + * + * Thanks to Steven Johnson for hunting this + * one down. + */ + xact->obuf.xid += XACT_HASHS; + +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr,"RPCIO XACT timed out; waking up requestor\n"); +#endif + if ( rtems_event_send(xact->requestor, RTEMS_RPC_EVENT) ) { + rtems_panic("RPCIO PANIC file %s line: %i, requestor id was 0x%08x", + __FILE__, + __LINE__, + xact->requestor); + } + + } else { + int len; + + len = (int)XDR_GETPOS(&xact->xdrs); + +#ifdef MBUF_TX + xact->refcnt = 1; /* sendto itself */ +#endif + if ( len != SENDTO( ourSock, + xact->obuf.buf, + len, + 0, + &srv->addr.sa, + sizeof(srv->addr.sin) +#ifdef MBUF_TX + , xact, + paranoia_free, + paranoia_ref +#endif + ) ) { + + xact->status.re_errno = errno; + xact->status.re_status = RPC_CANTSEND; + srv->errors++; + + /* wakeup requestor */ + fprintf(stderr,"RPCIO: SEND failure\n"); + status = rtems_event_send(xact->requestor, RTEMS_RPC_EVENT); + assert( status == RTEMS_SUCCESSFUL ); + + } else { + /* send successful; calculate retransmission time + * and enqueue to temporary list + */ + if (FIRST_ATTEMPT != xact->trip) { +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr, + "timed out; tolive is %i (ticks), retry period is %i (ticks)\n", + xact->tolive, + srv->retry_period); +#endif + /* this is a real retry; we backup + * the server's retry interval + */ + if ( srv->retry_period < max_period ) { + + /* If multiple transactions for this server + * fail (e.g. because it died) this will + * back-off very agressively (doubling + * the retransmission period for every + * timed out transaction up to the CAP limit) + * which is desirable - single packet failure + * is treated more gracefully by this algorithm. + */ + + srv->retry_period<<=1; +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr, + "adjusted to; retry period %i\n", + srv->retry_period); +#endif + } else { + /* never wait longer than RPCIOD_RETX_CAP_S seconds */ + fprintf(stderr, + "RPCIO: server '%s' not responding - still trying\n", + srv->name); + } + if ( 0 == ++srv->retrans % 1000) { + fprintf(stderr, + "RPCIO - statistics: already %li retries to server %s\n", + srv->retrans, + srv->name); + } + } else { + srv->requests++; + } + xact->trip = now; + { + long capped_period = srv->retry_period; + if ( xact->lifetime < capped_period ) + capped_period = xact->lifetime; + xact->age = now + capped_period; + xact->tolive -= capped_period; + } + /* enqueue to the list of newly sent transactions */ + xact->node.next = newList; + newList = &xact->node; +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr, + "XACT (0x%08x) age is 0x%x, now: 0x%x\n", + xact, + xact->age, + now); +#endif + } + } + } + + /* insert the newly sent transactions into the + * sorted retransmission list + */ + for (; (xact = (RpcUdpXact)newList); ) { + register ListNode p,n; + newList = newList->next; + for ( p=&listHead; (n=p->next) && xact->age > ((RpcUdpXact)n)->age; p=n ) + /* nothing else to do */; + nodeAppend(p, &xact->node); + } + + if (now > epoch) { + /* every now and then, readjust the epoch */ + register ListNode n; + then += now; + for (n=listHead.next; n; n=n->next) { + /* readjust outstanding time intervals subject to the + * condition that the 'absolute' time must remain + * the same. 'age' and 'trip' are measured with + * respect to 'then' - hence: + * + * abs_age == old_age + old_then == new_age + new_then + * + * ==> new_age = old_age + old_then - new_then == old_age - 'now' + */ + ((RpcUdpXact)n)->age -= now; + ((RpcUdpXact)n)->trip -= now; +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr, + "readjusted XACT (0x%08x); age is 0x%x, trip: 0x%x now: 0x%x\n", + (RpcUdpXact)n, + ((RpcUdpXact)n)->trip, + ((RpcUdpXact)n)->age, + now); +#endif + } + now = 0; + } + + next_retrans = listHead.next ? + ((RpcUdpXact)listHead.next)->age - now : + epoch; /* make sure we don't miss updating the epoch */ +#if (DEBUG) & DEBUG_TIMEOUT + fprintf(stderr,"RPCIO: next timeout is %x\n",next_retrans); +#endif + } + /* close our socket; shut down the receiver */ + close(ourSock); + +#if 0 /* if we get here, no transactions exist, hence there can be none + * in the queue whatsoever + */ + /* flush the message queue */ + while (RTEMS_SUCCESSFUL == rtems_message_queue_receive( + q, + &xact, + &size, + RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT)) { + /* TODO enque xact */ + } + + /* flush all outstanding transactions */ + + for (xact=((RpcUdpXact)listHead.next); xact; xact=((RpcUdpXact)xact->node.next)) { + xact->status.re_status = RPC_TIMEDOUT; + rtems_event_send(xact->requestor, RTEMS_RPC_EVENT); + } +#endif + + rtems_message_queue_delete(q); + + MU_DESTROY(hlock); + + fprintf(stderr,"RPC daemon exited...\n"); + + rtems_semaphore_release(fini); + rtems_task_suspend(RTEMS_SELF); +} + + +/* support for transaction 'pools'. A number of XACT objects + * is always kept around. The initial number is 0 but it + * is allowed to grow up to a maximum. + * If the need grows beyond the maximum, behavior depends: + * Users can either block until a transaction becomes available, + * they can create a new XACT on the fly or get an error + * if no free XACT is available from the pool. + */ + +RpcUdpXactPool +rpcUdpXactPoolCreate( + rpcprog_t prog, rpcvers_t version, + int xactsize, int poolsize) +{ +RpcUdpXactPool rval = MY_MALLOC(sizeof(*rval)); +rtems_status_code status; + + ASSERT( rval ); + status = rtems_message_queue_create( + rtems_build_name('R','P','C','p'), + poolsize, + sizeof(RpcUdpXact), + RTEMS_DEFAULT_ATTRIBUTES, + &rval->box); + assert( status == RTEMS_SUCCESSFUL ); + + rval->prog = prog; + rval->version = version; + rval->xactSize = xactsize; + return rval; +} + +void +rpcUdpXactPoolDestroy(RpcUdpXactPool pool) +{ +RpcUdpXact xact; + + while ((xact = rpcUdpXactPoolGet(pool, XactGetFail))) { + rpcUdpXactDestroy(xact); + } + rtems_message_queue_delete(pool->box); + MY_FREE(pool); +} + +RpcUdpXact +rpcUdpXactPoolGet(RpcUdpXactPool pool, XactPoolGetMode mode) +{ +RpcUdpXact xact = 0; +size_t size; + + if (RTEMS_SUCCESSFUL != rtems_message_queue_receive( + pool->box, + &xact, + &size, + XactGetWait == mode ? + RTEMS_WAIT : RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT)) { + + /* nothing found in box; should we create a new one ? */ + + xact = (XactGetCreate == mode) ? + rpcUdpXactCreate( + pool->prog, + pool->version, + pool->xactSize) : 0 ; + if (xact) + xact->pool = pool; + + } + return xact; +} + +void +rpcUdpXactPoolPut(RpcUdpXact xact) +{ +RpcUdpXactPool pool; + + pool = xact->pool; + ASSERT( pool ); + + if (RTEMS_SUCCESSFUL != rtems_message_queue_send( + pool->box, + &xact, + sizeof(xact))) + rpcUdpXactDestroy(xact); +} + +#ifdef MBUF_RX + +/* WORKAROUND: include sys/mbuf.h (or other bsdnet headers) only + * _after_ using malloc()/free() & friends because + * the RTEMS/BSDNET headers redefine those :-( + */ + +#define _KERNEL +#include <sys/mbuf.h> + +ssize_t +recv_mbuf_from(int s, struct mbuf **ppm, long len, struct sockaddr *fromaddr, int *fromlen); + +static void +bufFree(struct mbuf **m) +{ + if (*m) { + rtems_bsdnet_semaphore_obtain(); + m_freem(*m); + rtems_bsdnet_semaphore_release(); + *m = 0; + } +} +#endif + +#ifdef MBUF_TX +static void +paranoia_free(caddr_t closure, u_int size) +{ +#if (DEBUG) +RpcUdpXact xact = (RpcUdpXact)closure; +int len = (int)XDR_GETPOS(&xact->xdrs); + + ASSERT( --xact->refcnt >= 0 && size == len ); +#endif +} + +static void +paranoia_ref (caddr_t closure, u_int size) +{ +#if (DEBUG) +RpcUdpXact xact = (RpcUdpXact)closure; +int len = (int)XDR_GETPOS(&xact->xdrs); + ASSERT( size == len ); + xact->refcnt++; +#endif +} +#endif + +/* receive from a socket and find + * the transaction corresponding to the + * transaction ID received in the server + * reply. + * + * The semantics of the 'pibuf' pointer are + * as follows: + * + * MBUF_RX: + * + */ + +#define RPCIOD_RXBUFSZ UDPMSGSIZE + +static RpcUdpXact +sockRcv(void) +{ +int len,i; +uint32_t xid; +union { + struct sockaddr_in sin; + struct sockaddr sa; +} fromAddr; +int fromLen = sizeof(fromAddr.sin); +RxBuf ibuf = 0; +RpcUdpXact xact = 0; + + do { + + /* rcv_mbuf() and recvfrom() differ in that the + * former allocates buffers and passes them back + * to us whereas the latter requires us to provide + * buffer space. + * Hence, in the first case whe have to make sure + * no old buffer is leaked - in the second case, + * we might well re-use an old buffer but must + * make sure we have one allocated + */ +#ifdef MBUF_RX + if (ibuf) + bufFree(&ibuf); + + len = recv_mbuf_from( + ourSock, + &ibuf, + RPCIOD_RXBUFSZ, + &fromAddr.sa, + &fromLen); +#else + if ( !ibuf ) + ibuf = (RpcBuf)MY_MALLOC(RPCIOD_RXBUFSZ); + if ( !ibuf ) + goto cleanup; /* no memory - drop this message */ + + len = recvfrom(ourSock, + ibuf->buf, + RPCIOD_RXBUFSZ, + 0, + &fromAddr.sa, + &fromLen); +#endif + + if (len <= 0) { + if (EAGAIN != errno) + fprintf(stderr,"RECV failed: %s\n",strerror(errno)); + goto cleanup; + } + +#if (DEBUG) & DEBUG_PACKLOSS + if ( (unsigned)rand() < DEBUG_PACKLOSS_FRACT ) { + /* lose packets once in a while */ + static int xxx = 0; + if ( ++xxx % 16 == 0 ) + fprintf(stderr,"DEBUG: dropped %i packets, so far...\n",xxx); + if ( ibuf ) + bufFree( &ibuf ); + continue; + } +#endif + + i = (xid=XID(ibuf)) & XACT_HASH_MSK; + + if ( !(xact=xactHashTbl[i]) || + xact->obuf.xid != xid || +#ifdef REJECT_SERVERIP_MISMATCH + xact->server->addr.sin.sin_addr.s_addr != fromAddr.sin.sin_addr.s_addr || +#endif + xact->server->addr.sin.sin_port != fromAddr.sin.sin_port ) { + + if (xact) { + if ( +#ifdef REJECT_SERVERIP_MISMATCH + xact->server->addr.sin.sin_addr.s_addr == fromAddr.sin.sin_addr.s_addr && +#endif + xact->server->addr.sin.sin_port == fromAddr.sin.sin_port && + ( xact->obuf.xid == xid + XACT_HASHS || + xact->obuf.xid == xid + 2*XACT_HASHS ) + ) { +#ifndef DEBUG /* don't complain if it's just a late arrival of a retry */ + fprintf(stderr,"RPCIO - FYI sockRcv(): dropping late/redundant retry answer\n"); +#endif + } else { + fprintf(stderr,"RPCIO WARNING sockRcv(): transaction mismatch\n"); + fprintf(stderr,"xact: xid 0x%08" PRIx32 " -- got 0x%08" PRIx32 "\n", + xact->obuf.xid, xid); + fprintf(stderr,"xact: addr 0x%08" PRIx32 " -- got 0x%08" PRIx32 "\n", + xact->server->addr.sin.sin_addr.s_addr, + fromAddr.sin.sin_addr.s_addr); + fprintf(stderr,"xact: port 0x%08x -- got 0x%08x\n", + xact->server->addr.sin.sin_port, + fromAddr.sin.sin_port); + } + } else { + fprintf(stderr, + "RPCIO WARNING sockRcv(): got xid 0x%08" PRIx32 " but its slot is empty\n", + xid); + } + /* forget about this one and try again */ + xact = 0; + } + + } while ( !xact ); + + xact->ibuf = ibuf; +#ifndef MBUF_RX + xact->ibufsize = RPCIOD_RXBUFSZ; +#endif + + return xact; + +cleanup: + + bufFree(&ibuf); + + return 0; +} + + +#include <rtems/rtems_bsdnet_internal.h> +/* double check the event configuration; should probably globally + * manage system events!! + * We do this at the end of the file for the same reason we had + * included mbuf.h only a couple of lines above - see comment up + * there... + */ +#if RTEMS_RPC_EVENT & SOSLEEP_EVENT & SBWAIT_EVENT & NETISR_EVENTS +#error ILLEGAL EVENT CONFIGURATION +#endif diff --git a/cpukit/libfs/src/nfsclient/src/rpcio.h b/cpukit/libfs/src/nfsclient/src/rpcio.h new file mode 100644 index 0000000000..0d65b76dc1 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/rpcio.h @@ -0,0 +1,209 @@ +#ifndef RPCIO_H +#define RPCIO_H +/* $Id$ */ + +/* A multihreaded RPC/UDP multiplexor */ + +/* Author: Till Straumann, <strauman@slac.stanford.edu>, 2002 */ + +/* + * Authorship + * ---------- + * This software (NFS-2 client implementation for RTEMS) was created by + * Till Straumann <strauman@slac.stanford.edu>, 2002-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The NFS-2 client implementation for RTEMS was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifdef __rtems +#include <rtems.h> +#endif + +#include <rpc/rpc.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <stdarg.h> + +typedef struct RpcUdpServerRec_ *RpcUdpServer; +typedef struct RpcUdpXactRec_ *RpcUdpXact; + +typedef RpcUdpXact RpcUdpClnt; + +#define RPCIOD_DEFAULT_ID 0xdef10000 + +int +rpcUdpInit(void); + +enum clnt_stat +rpcUdpServerCreate( + struct sockaddr_in *paddr, + rpcprog_t prog, + rpcvers_t vers, + u_long uid, /* RPCIO_DEFAULT_ID picks default */ + u_long gid, /* RPCIO_DEFAULT_ID picks default */ + RpcUdpServer *pclnt /* new server is returned here */ + ); + + +void +rpcUdpServerDestroy(RpcUdpServer s); + +/* Dump statistics to a file (stdout if NULL); + * returns 0 for convenience + */ +int +rpcUdpStats(FILE *f); + +enum clnt_stat +rpcUdpClntCreate( + struct sockaddr_in *psaddr, + rpcprog_t prog, + rpcvers_t vers, + u_long uid, /* RPCIO_DEFAULT_ID picks default */ + u_long gid, /* RPCIO_DEFAULT_ID picks default */ + RpcUdpClnt *pclnt /* new client is returned here */ + ); + +void +RpcUdpClntDestroy(RpcUdpClnt clnt); + +/* mute compiler warnings */ +typedef void *XdrProcT; +typedef void *CaddrT; + +enum clnt_stat +rpcUdpClntCall( + RpcUdpClnt clnt, + u_long proc, + XdrProcT xargs, + CaddrT pargs, + XdrProcT xres, + CaddrT pres, + struct timeval *timeout /* optional timeout; maybe NULL to pick default */ + ); + +RpcUdpXact +rpcUdpXactCreate( + u_long program, + u_long version, + u_long size + ); + +void +rpcUdpXactDestroy( + RpcUdpXact xact + ); + +/* send a transaction */ +enum clnt_stat +rpcUdpSend( + RpcUdpXact xact, + RpcUdpServer srvr, + struct timeval *timeout, /* maybe NULL to pick default */ + u_long proc, + xdrproc_t xres, + caddr_t pres, + xdrproc_t xargs, + caddr_t pargs, + ... /* 0 terminated xdrproc/pobj additional argument list */ + ); + +/* wait for a transaction to complete */ +enum clnt_stat +rpcUdpRcv(RpcUdpXact xact); + +/* a yet simpler interface */ +enum clnt_stat +rpcUdpCallRp( + struct sockaddr_in *pserver_addr, + u_long prog, + u_long vers, + u_long proc, + XdrProcT xargs, + CaddrT pargs, + XdrProcT xres, + CaddrT pres, + u_long uid, /* RPCIO_DEFAULT_ID picks default */ + u_long gid, /* RPCIO_DEFAULT_ID picks default */ + struct timeval *timeout /* NULL picks default */ +); + + +/* manage pools of transactions */ + +/* A pool of transactions. The idea is not to malloc/free them + * all the time but keep a limited number around in a 'pool'. + * Users who need a XACT may get it from the pool and put it back + * when done. + * The pool is implemented by RTEMS message queues who manage + * the required task synchronization. + * A requestor has different options if the pool is empty: + * - it can wait (block) for a XACT to become available + * - it can get an error status + * - or it can malloc an extra XACT from the heap which + * will eventually be released. + */ + +typedef struct RpcUdpXactPoolRec_ *RpcUdpXactPool; + +/* NOTE: the pool is empty initially, must get messages (in + * GetCreate mode + */ +RpcUdpXactPool +rpcUdpXactPoolCreate( + rpcprog_t prog, rpcvers_t version, + int xactsize, int poolsize); + +void +rpcUdpXactPoolDestroy(RpcUdpXactPool pool); + +typedef enum { + XactGetFail, /* call fails if no transaction available */ + XactGetWait, /* call blocks until transaction available */ + XactGetCreate /* a new transaction is allocated (and freed when put back to the pool */ +} XactPoolGetMode; + +RpcUdpXact +rpcUdpXactPoolGet(RpcUdpXactPool pool, XactPoolGetMode mode); + +void +rpcUdpXactPoolPut(RpcUdpXact xact); + +#endif diff --git a/cpukit/libfs/src/nfsclient/src/rpcio.modini.c b/cpukit/libfs/src/nfsclient/src/rpcio.modini.c new file mode 100644 index 0000000000..7aa802fe51 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/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/cpukit/libfs/src/nfsclient/src/sock_mbuf.c b/cpukit/libfs/src/nfsclient/src/sock_mbuf.c new file mode 100644 index 0000000000..3c589d9e81 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/sock_mbuf.c @@ -0,0 +1,283 @@ +/* + * $Id$ + * + * NOTE: + * This is derived from libnetworking/rtems/rtems_syscall.c + * + * RTEMS/libnetworking LICENSING restrictions may apply + * + * Author (modifications only): + * Copyright: 2002, Stanford University and + * Till Straumann, <strauman@slac.stanford.edu> + * Licensing: 'LICENSE.NET' file in the RTEMS top source directory + * for more information. + */ + +/* +The RTEMS TCP/IP stack is a port of the FreeBSD TCP/IP stack. The following +copyright and licensing information applies to this code. + +This code is found under the c/src/libnetworking directory but does not +constitute the entire contents of that subdirectory. + +============================================================================= + +Copyright (c) 1980, 1983, 1988, 1993 + The Regents of the University of California. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by the University of + California, Berkeley and its contributors. +4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +- +Portions Copyright (c) 1993 by Digital Equipment Corporation. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies, and that +the name of Digital Equipment Corporation not be used in advertising or +publicity pertaining to distribution of the document or software without +specific, written prior permission. + +THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT +CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +============================================================================= +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/error.h> + +#define _KERNEL +#define __BSD_VISIBLE 1 +#include <rtems/rtems_bsdnet.h> + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/protosw.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/filio.h> + +#include <net/if.h> +#include <net/route.h> + +struct socket *rtems_bsdnet_fdToSocket(int fd); + +/* + * Package system call argument into mbuf. + * + * (unfortunately, the original is not public) + */ +static int +sockaddrtombuf (struct mbuf **mp, const struct sockaddr *buf, int buflen) +{ +struct mbuf *m; +struct sockaddr *sa; + + if ((u_int)buflen > MLEN) + return (EINVAL); + + rtems_bsdnet_semaphore_obtain(); + m = m_get(M_WAIT, MT_SONAME); + rtems_bsdnet_semaphore_release(); + + if (m == NULL) + return (ENOBUFS); + m->m_len = buflen; + memcpy (mtod(m, caddr_t), buf, buflen); + *mp = m; + sa = mtod(m, struct sockaddr *); + sa->sa_len = buflen; + + return 0; +} + +static void +dummyproc(caddr_t ext_buf, u_int ext_size) +{ +} + +/* + * send data by simply allocating an MBUF packet + * header and pointing it to our data region. + * + * Optionally, the caller may supply 'reference' + * and 'free' procs. (The latter may call the + * user back once the networking stack has + * released the buffer). + * + * The callbacks are provided with the 'closure' + * pointer and the 'buflen' argument. + */ +ssize_t +sendto_nocpy ( + int s, + const void *buf, size_t buflen, + int flags, + const struct sockaddr *toaddr, int tolen, + void *closure, + void (*freeproc)(caddr_t, u_int), + void (*refproc)(caddr_t, u_int) +) +{ + int error; + struct socket *so; + struct mbuf *to, *m; + int ret = -1; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + + error = sockaddrtombuf (&to, toaddr, tolen); + if (error) { + errno = error; + rtems_bsdnet_semaphore_release (); + return -1; + } + + MGETHDR(m, M_WAIT, MT_DATA); + m->m_pkthdr.len = 0; + m->m_pkthdr.rcvif = (struct ifnet *) 0; + + m->m_flags |= M_EXT; + m->m_ext.ext_buf = closure ? closure : (void*)buf; + m->m_ext.ext_size = buflen; + /* we _must_ supply non-null procs; otherwise, + * the kernel code assumes it's a mbuf cluster + */ + m->m_ext.ext_free = freeproc ? freeproc : dummyproc; + m->m_ext.ext_ref = refproc ? refproc : dummyproc; + m->m_pkthdr.len += buflen; + m->m_len = buflen; + m->m_data = (void*)buf; + + error = sosend (so, to, NULL, m, NULL, flags); + if (error) { + if (/*auio.uio_resid != len &&*/ (error == EINTR || error == EWOULDBLOCK)) + error = 0; + } + if (error) + errno = error; + else + ret = buflen; + if (to) + m_freem(to); + rtems_bsdnet_semaphore_release (); + return (ret); +} + + +/* + * receive data in an 'mbuf chain'. + * The chain must be released once the + * data has been extracted: + * + * rtems_bsdnet_semaphore_obtain(); + * m_freem(chain); + * rtems_bsdnet_semaphore_release(); + */ +ssize_t +recv_mbuf_from(int s, struct mbuf **ppm, long len, struct sockaddr *fromaddr, int *fromlen) +{ + int ret = -1; + int error; + struct uio auio; + struct socket *so; + struct mbuf *from = NULL; + + memset(&auio, 0, sizeof(auio)); + *ppm = 0; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } +/* auio.uio_iov = mp->msg_iov; + auio.uio_iovcnt = mp->msg_iovlen; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_rw = UIO_READ; + auio.uio_offset = 0; +*/ + auio.uio_resid = len; + error = soreceive (so, &from, &auio, (struct mbuf **) ppm, + (struct mbuf **)NULL, + NULL); + if (error) { + if (auio.uio_resid != len && (error == EINTR || error == EWOULDBLOCK)) + error = 0; + } + if (error) { + errno = error; + } + else { + ret = len - auio.uio_resid; + if (fromaddr) { + len = *fromlen; + if ((len <= 0) || (from == NULL)) { + len = 0; + } + else { + if (len > from->m_len) + len = from->m_len; + memcpy (fromaddr, mtod(from, caddr_t), len); + } + *fromlen = len; + } + } + if (from) + m_freem (from); + if (error && *ppm) { + m_freem(*ppm); + *ppm = 0; + } + rtems_bsdnet_semaphore_release (); + return (ret); +} diff --git a/cpukit/libfs/src/nfsclient/src/xdr_mbuf.c b/cpukit/libfs/src/nfsclient/src/xdr_mbuf.c new file mode 100644 index 0000000000..4294d6dcd2 --- /dev/null +++ b/cpukit/libfs/src/nfsclient/src/xdr_mbuf.c @@ -0,0 +1,539 @@ +/* $Id$ */ + +/* xdr_mbuf is derived from xdr_mem */ + +/* Author (mbuf specifica): Till Straumann <strauman@slac.stanford.edu>, 10/2002 */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$FreeBSD: src/lib/libc/xdr/xdr_mem.c,v 1.8 1999/08/28 00:02:56 peter Exp $"; +#endif + +/* + * xdr_mbuf, XDR implementation using mbuf buffers + * + * derived from: + * + * xdr_mem.h, XDR implementation using memory buffers. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * The MBUF stream is useful for BSDNET kernel (or RTEMS for that matter) + * use. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <netinet/in.h> + +#include <stdlib.h> + +#define TODO + +/* TODO remove: a hack because malloc is redefined */ +#ifdef TODO +static inline void * +my_malloc(size_t i) +{ + return malloc(i); +} + +static inline void +my_free(void *p) +{ + return free(p); +} +#endif + +#define DEBUG_ASSERT (1<<0) +#define DEBUG_VERB (1<<1) + +#define DEBUG DEBUG_ASSERT + +#define _KERNEL +#include <sys/mbuf.h> + +#include <assert.h> + +#if DEBUG & DEBUG_VERB || defined(TODO) +#include <stdio.h> +#endif + +static bool_t xdrmbuf_getlong_aligned(XDR *xdrs, long *lp); +static bool_t xdrmbuf_putlong_aligned(XDR *xdrs, const long *lp); +static bool_t xdrmbuf_getlong_unaligned(XDR *xdrs, long *lp); +static bool_t xdrmbuf_putlong_unaligned(XDR *xdrs, const long *lp); +static bool_t xdrmbuf_getbytes(XDR *xdrs, caddr_t addr, u_int len); +static bool_t xdrmbuf_putbytes(XDR *xdrs, const char *addr, u_int len); +static u_int xdrmbuf_getpos(XDR *xdrs); /* XXX w/64-bit pointers, u_int not enough! */ +static bool_t xdrmbuf_setpos(XDR *xdrs, u_int pos); +static int32_t *xdrmbuf_inline_aligned(XDR *xdrs, u_int len); +static int32_t *xdrmbuf_inline_unaligned(XDR *xdrs, u_int len); +static void xdrmbuf_destroy(XDR *); + +static struct xdr_ops xdrmbuf_ops_aligned = { + xdrmbuf_getlong_aligned, + xdrmbuf_putlong_aligned, + xdrmbuf_getbytes, + xdrmbuf_putbytes, + xdrmbuf_getpos, + xdrmbuf_setpos, + xdrmbuf_inline_aligned, + xdrmbuf_destroy +}; + +static struct xdr_ops xdrmbuf_ops_unaligned = { + xdrmbuf_getlong_unaligned, + xdrmbuf_putlong_unaligned, + xdrmbuf_getbytes, + xdrmbuf_putbytes, + xdrmbuf_getpos, + xdrmbuf_setpos, + xdrmbuf_inline_unaligned, + xdrmbuf_destroy +}; + +typedef struct MBPrivateRec_ { + struct mbuf *mchain; + struct mbuf *mcurrent; + u_int pos; /* number of bytes contained in all MUBFS ahead + * of mcurrent + */ +} MBPrivateRec, *MBPrivate; + +/* NOTE: the stream position helper 'pos' + * must be managed by the caller! + */ +static inline void +xdrmbuf_setup(XDR *xdrs, struct mbuf *m) +{ +MBPrivate mbp = (MBPrivate)xdrs->x_base; + + mbp->mcurrent = m; + xdrs->x_private = mtod(m,caddr_t); + xdrs->x_handy = m->m_len; + xdrs->x_ops = ((uintptr_t)xdrs->x_private & (sizeof(int32_t) - 1)) + ? &xdrmbuf_ops_unaligned : &xdrmbuf_ops_aligned; +} + +static struct mbuf * +xdrmbuf_next(XDR *xdrs) +{ +struct mbuf *rval; +MBPrivate mbp = (MBPrivate)xdrs->x_base; + + if (mbp->mcurrent) { + mbp->pos += mbp->mcurrent->m_len; + rval = mbp->mcurrent->m_next; + } else { + rval = 0; + } + + if (rval) { + xdrmbuf_setup(xdrs, rval); + } +#if DEBUG & DEBUG_VERB + else { + fprintf(stderr,"xdrmbuf: end of chain\n"); + } +#endif + + return rval; +} + +/* + * The procedure xdrmbuf_create initializes a stream descriptor for a + * memory buffer. + */ +void +xdrmbuf_create(XDR *xdrs, struct mbuf *mbuf, enum xdr_op op) +{ +MBPrivate mbp; + + xdrs->x_op = op; + mbp = (MBPrivate)my_malloc(sizeof(*mbp)); + assert( mbp ); + xdrs->x_base = (caddr_t) mbp; + + mbp->mchain = mbuf; + mbp->pos = 0; + +#if DEBUG & DEBUG_VERB + { + struct mbuf *mbf; + fprintf(stderr,"Dumping chain:\n"); + for (mbf = mbuf; mbf; mbf=mbf->m_next) { + int ii; + fprintf(stderr,"MBUF------------"); + for (ii=0; ii<mbf->m_len; ii++) { + fprintf(stderr,"%02x ",mtod(mbf,char*)[ii]); + if (ii%16==0) + fputc('\n',stderr); + } + fputc('\n',stderr); + } + } +#endif + + xdrmbuf_setup(xdrs, mbuf); +} + +static void +xdrmbuf_destroy(XDR *xdrs) +{ +MBPrivate mbp = (MBPrivate)xdrs->x_base; +#if 0 /* leave destroying the chain to the user */ +struct mbuf *m = mbp->mchain; + + rtems_bsdnet_semaphore_obtain(); + m_freem(m); + rtems_bsdnet_semaphore_release(); +#endif + + my_free(mbp); +} + +static bool_t +xdrmbuf_getlong_aligned(register XDR *xdrs, register long *lp) +{ + while ( (signed int)(xdrs->x_handy -= sizeof(int32_t)) < 0) { + if ((xdrs->x_handy += sizeof(int32_t)) == 0) { + /* handy was 0 on entry; request a new buffer. + * Coded this way, so the most frequently executed + * path needs only one comparison... + */ + if (!xdrmbuf_next(xdrs)) + return FALSE; + } else { + /* uh-oh an aligned long spread over two MBUFS ?? + * let the unaligned handler deal with this rare + * situation. + */ + return xdrmbuf_getlong_unaligned(xdrs,lp); + } + } + *lp = ntohl(*(int32_t *)(xdrs->x_private)); + xdrs->x_private += sizeof(int32_t); +#if DEBUG & DEBUG_VERB + fprintf(stderr,"Got aligned long %x\n",*lp); +#endif + return (TRUE); +} + +static bool_t +xdrmbuf_putlong_aligned( + XDR *xdrs, + const long *lp) +{ +fprintf(stderr,"TODO: xdrmbuf_putlong_aligned() is unimplemented\n"); + return FALSE; +#if 0 + if ((xdrs->x_handy -= sizeof(int32_t)) < 0) + return (FALSE); + *(int32_t *)xdrs->x_private = htonl(*lp); + xdrs->x_private += sizeof(int32_t); + return (TRUE); +#endif +} + +static bool_t +xdrmbuf_getlong_unaligned( + XDR *xdrs, + long *lp) +{ +union { + int32_t l; + char c[sizeof(int32_t)]; +} u; + +register int i,j; +register char *cp,*sp; + + i = xdrs->x_handy - sizeof(int32_t); + + /* handle the most common case first */ + if ( i >= 0 ) { + + xdrs->x_handy = i; + sp = (char*)xdrs->x_private; + xdrs->x_private = sp + sizeof(int32_t); + +#ifdef CANDO_UNALIGNED + { + *lp = ntohl(*(int32_t *)sp); +# if DEBUG & DEBUG_VERB + fprintf(stderr,"Got unaligned long %x (%i remaining)\n",*lp, xdrs->x_handy); +# endif + return TRUE; + } +#else /* machine can't do unaligned access */ + { + u.c[0] = *sp; + u.c[1] = *++sp; + u.c[2] = *++sp; + u.c[3] = *++sp; + + goto done; + } +#endif /* CANDO_UNALIGNED */ + } + + /* here the messy 'crossing buffers' business starts */ + + + j = sizeof(int32_t); + + cp = u.c-1; + + /* NOTE: on entry to this section, handy < j holds */ + do { + sp = ((char*)xdrs->x_private)-1; + + if ( (i=xdrs->x_handy) >= j ) { + /* more data in the buffer than we need: + * copy everything we need and goto 'done' + */ + xdrs->x_handy = i-j; + do { + *++cp = *++sp; + } while (--j > 0); + xdrs->x_private = (caddr_t)++sp; + + goto done; + + } else { + /* not enough data - copy as much as possible + * then get retrieve the next MBUF and start + * over + */ + j-=i; + while (i--) + *++cp = *++sp; + if (!xdrmbuf_next(xdrs)) + return FALSE; +#if DEBUG & DEBUG_VERB + fprintf(stderr,"getlong_unaligned: crossed mbuf boundary\n"); +#endif + } + } while (j > 0); + +done: + + *lp = ntohl(u.l); + +#if DEBUG & DEBUG_VERB + fprintf(stderr,"Got unaligned long %x (%i remaining)\n",*lp, xdrs->x_handy); +#endif + return (TRUE); +} + +static bool_t +xdrmbuf_putlong_unaligned( + XDR *xdrs, + const long *lp ) +{ + + fprintf(stderr,"TODO: xdrmbuf_putlong_unaligned() is unimplemented\n"); + return FALSE; +#if 0 + { + int32_t l; + + if ((xdrs->x_handy -= sizeof(int32_t)) < 0) + return (FALSE); + l = htonl(*lp); + memcpy(xdrs->x_private, &l, sizeof(int32_t)); + xdrs->x_private += sizeof(int32_t); + return (TRUE); + } +#endif +} + +static bool_t +xdrmbuf_getbytes( + XDR *xdrs, + caddr_t addr, + u_int len) +{ +#if DEBUG & DEBUG_VERB +int olen=len,bufs=0; +#endif + +#if DEBUG & DEBUG_VERB + fprintf(stderr,"wanting %i bytes (have %i)\n",olen,xdrs->x_handy); +#endif + + while (len>0) { + if (xdrs->x_handy >= len) { + memcpy(addr, xdrs->x_private, len); + xdrs->x_private += len; + xdrs->x_handy -= len; +#if 0 /* save a couple of instructions */ + len = 0; +#else + goto done; +#endif + } else { + if (xdrs->x_handy > 0) { + memcpy(addr, xdrs->x_private, xdrs->x_handy); + len -= xdrs->x_handy; + addr += xdrs->x_handy; + } + if (!xdrmbuf_next(xdrs)) + return FALSE; +#if DEBUG & DEBUG_VERB + bufs++; +#endif + } + } +done: +#if DEBUG & DEBUG_VERB + fprintf(stderr,"Got %i bytes (out of %i mbufs)\n",olen,bufs); +#endif + return (TRUE); +} + +static bool_t +xdrmbuf_putbytes( + XDR *xdrs, + const char *addr, + u_int len ) +{ + + fprintf(stderr,"TODO: xdrmbuf_putbytes() is unimplemented\n"); + return FALSE; +#if 0 + if ((xdrs->x_handy -= len) < 0) + return (FALSE); + memcpy(xdrs->x_private, addr, len); + xdrs->x_private += len; + return (TRUE); +#endif +} + +static u_int +xdrmbuf_getpos( + XDR *xdrs) +{ +#if 1 +MBPrivate mbp = (MBPrivate)xdrs->x_base; +struct mbuf *m = mbp->mcurrent; +u_int rval = mbp->pos; + + if (m) { + rval += xdrs->x_private - mtod(m, void*); + } +#else +struct mbuf *m; +u_int rval = 0; +MBPrivate mbp = (MBPrivate)xdrs->x_base; + + for ( m = mbp->mchain; m && m != mbp->mcurrent; m = m->m_next ) + rval += m->m_len; + if (m) { + rval += (u_long)xdrs->x_private - mtod(m, u_long); + } + +#endif + return rval; +} + +static bool_t +xdrmbuf_setpos( + XDR *xdrs, + u_int pos) +{ +struct mbuf *m; +MBPrivate mbp = (MBPrivate)xdrs->x_base; + + if (pos >= mbp->pos) { + pos -= mbp->pos; + m = mbp->mcurrent; + } else { + m = mbp->mchain; + mbp->pos = 0; + } + + while ( m && pos >= m->m_len ) { + pos -= m->m_len; + mbp->pos += m->m_len; + m = m->m_next; + } + + if (m) { + xdrmbuf_setup(xdrs, m); + xdrs->x_private += pos; + return TRUE; + } + + return 0 == pos ? TRUE : FALSE; +} + +static int32_t * +xdrmbuf_inline_aligned( + XDR *xdrs, + u_int len) +{ +int32_t *buf = 0; + + if (xdrs->x_handy == 0 && !xdrmbuf_next(xdrs)) + return 0; + + if (xdrs->x_handy >= len) { + xdrs->x_handy -= len; + buf = (int32_t *) xdrs->x_private; + xdrs->x_private += len; +#if DEBUG & DEBUG_VERB + fprintf(stderr,"Got %i aligned inline bytes at %x\n", len, buf); +#endif + } +#if DEBUG & DEBUG_VERB + else { + fprintf(stderr,"Skipped %i aligned inline bytes\n",len); + } +#endif + return (buf); +} + +static int32_t * +xdrmbuf_inline_unaligned( + XDR *xdrs, + u_int len ) +{ + return (0); +} |