source: rtems/cpukit/libfs/src/nfsclient/src/nfs.c @ 03fcbb1

5
Last change on this file since 03fcbb1 was 03fcbb1, checked in by Sebastian Huber <sebastian.huber@…>, on 11/27/18 at 11:45:53

fs: Add struct dirent::d_type support

  • Property mode set to 100644
File size: 71.5 KB
Line 
1/**
2 * @file
3 *
4 * @brief NFS Client Implementation for RTEMS
5 * @ingroup libfs
6 *
7 * Hooks Into the RTEMS NFS Filesystem
8 */
9
10/*
11 * Author: Till Straumann <strauman@slac.stanford.edu>, 2002
12 *
13 * Hacked on by others.
14 *
15 * Modifications to support reference counting in the file system are
16 * Copyright (c) 2012 embedded brains GmbH.
17 *
18 * Authorship
19 * ----------
20 * This software (NFS-2 client implementation for RTEMS) was created by
21 *     Till Straumann <strauman@slac.stanford.edu>, 2002-2007,
22 *         Stanford Linear Accelerator Center, Stanford University.
23 *
24 * Acknowledgement of sponsorship
25 * ------------------------------
26 * The NFS-2 client implementation for RTEMS was produced by
27 *     the Stanford Linear Accelerator Center, Stanford University,
28 *         under Contract DE-AC03-76SFO0515 with the Department of Energy.
29 *
30 * Government disclaimer of liability
31 * ----------------------------------
32 * Neither the United States nor the United States Department of Energy,
33 * nor any of their employees, makes any warranty, express or implied, or
34 * assumes any legal liability or responsibility for the accuracy,
35 * completeness, or usefulness of any data, apparatus, product, or process
36 * disclosed, or represents that its use would not infringe privately owned
37 * rights.
38 *
39 * Stanford disclaimer of liability
40 * --------------------------------
41 * Stanford University makes no representations or warranties, express or
42 * implied, nor assumes any liability for the use of this software.
43 *
44 * Stanford disclaimer of copyright
45 * --------------------------------
46 * Stanford University, owner of the copyright, hereby disclaims its
47 * copyright and all other rights in this software.  Hence, anyone may
48 * freely use it for any purpose without restriction.
49 *
50 * Maintenance of notices
51 * ----------------------
52 * In the interest of clarity regarding the origin and status of this
53 * SLAC software, this and all the preceding Stanford University notices
54 * are to remain affixed to any copy or derivative of this software made
55 * or distributed by the recipient and are to be affixed to any copy of
56 * software made or distributed by the recipient that contains a copy or
57 * derivative of this software.
58 *
59 * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
60 */
61
62#ifdef  HAVE_CONFIG_H
63#include <config.h>
64#endif
65
66#include <rtems.h>
67#include <rtems/libio.h>
68#include <rtems/libio_.h>
69#include <rtems/seterr.h>
70#include <rtems/thread.h>
71#include <string.h>
72#include <stdio.h>
73#include <stdint.h>
74#include <stdlib.h>
75#include <assert.h>
76#include <sys/stat.h>
77#include <dirent.h>
78#include <netdb.h>
79#include <ctype.h>
80#include <netinet/in.h>
81#include <arpa/inet.h>
82
83#include "../proto/nfs_prot.h"
84#include "../proto/mount_prot.h"
85
86#include "rpcio.h"
87#include "librtemsNfs.h"
88
89/* Configurable parameters */
90
91/* Estimated average length of a filename (including terminating 0).
92 * This was calculated by doing
93 *
94 *      find <some root> -print -exec basename '{}' \; > feil
95 *      wc feil
96 *
97 * AVG_NAMLEN = (num_chars + num_lines)/num_lines
98 */
99#define CONFIG_AVG_NAMLEN                               10
100
101#define CONFIG_NFS_SMALL_XACT_SIZE              800                     /* size of RPC arguments for non-write ops */
102/* lifetime of NFS attributes in a NfsNode;
103 * the time is in seconds and the lifetime is
104 * infinite if the symbol is #undef
105 */
106#define CONFIG_ATTR_LIFETIME                    10/*secs*/
107
108/*
109 * The 'st_blksize' (stat(2)) value this nfs
110 * client should report. If set to zero then the server's fattr data
111 * is passed throught which is not necessary optimal.
112 * Newlib's stdio uses 'st_blksize' (if built with HAVE_BLKSIZE defined)
113 * to size the default buffer.
114 * Due to the overhead of NFS it is probably better to use the maximum
115 * size of an NFS read request (8k) rather than the optimal block
116 * size on the server.
117 * This value can be overridden at run-time by setting the global
118 * variable 'nfsStBlksize'.
119 * Thanks to Steven Johnson <sjohnson@sakuraindustries.com> for helping
120 * working on this issue.
121 */
122#define DEFAULT_NFS_ST_BLKSIZE                  NFS_MAXDATA
123
124/* dont change this without changing the maximal write size */
125#define CONFIG_NFS_BIG_XACT_SIZE                UDPMSGSIZE      /* dont change this */
126
127/* The real values for these are specified further down */
128#define NFSCALL_TIMEOUT                                 (&_nfscalltimeout)
129#define MNTCALL_TIMEOUT                                 (&_nfscalltimeout)
130static struct timeval _nfscalltimeout = { 10, 0 };      /* {secs, us } */
131
132/* More or less fixed constants; in particular, NFS3 is not supported */
133#define DELIM                                                   '/'
134#define HOSTDELIM                                               ':'
135#define UPDIR                                                   ".."
136#define UIDSEP                                                  '@'
137#define NFS_VERSION_2                                   NFS_VERSION
138
139/* we use a dynamically assigned major number */
140#define NFS_MAJOR                                               (nfsGlob.nfs_major)
141
142
143/* NOTE: RTEMS (ss-20020301) uses a 'short st_ino' type :-( but the
144 * NFS fileid is 32 bit. [Later versions of RTEMS have fixed this;
145 * nfsInit() issues a warning if you run a version with 'short st_ino'.]
146 *
147 * As a workarount, we merge the upper 16bits of the fileid into the
148 * minor device no. Hence, it is still possible to uniquely identify
149 * a file by looking at its device number (major = nfs, minor = part
150 * of the fileid + our 'nfs-id' identifier).
151 *
152 * This has an impact on performance, as e.g. getcwd() stats() all
153 * directory entries when it believes it has crossed a mount point
154 * (a.st_dev != b.st_dev).
155 *
156 * OTOH, it also might cause node comparison failure! E.g. 'getcwd()'
157 * assumes that two nodes residing in the same directory must be located
158 * on the same device and hence compares 'st_ino' only.
159 * If two files in the same directory have the same inode number
160 * modulo 2^16, they will be considered equal (although their device
161 * number doesn't match - getcwd doesn't look at it).
162 *
163 * Other software might or might not be affected.
164 *
165 * The only clean solution to this problem is bumping up the size of
166 * 'ino_t' at least to 'long'.
167 * Note that this requires _all_ software (libraries etc.) to be
168 * recompiled.
169 */
170
171#define NFS_MAKE_DEV_T_INO_HACK(node) \
172                rtems_filesystem_make_dev_t( NFS_MAJOR, \
173                        (((rtems_device_minor_number)((node)->nfs->id))<<16) | (((rtems_device_minor_number)SERP_ATTR((node)).fileid) >> 16) )
174
175/* use our 'nfs id' and the server's fsid for the minor device number
176 * this should be fairly unique
177 */
178#define NFS_MAKE_DEV_T(node) \
179                rtems_filesystem_make_dev_t( NFS_MAJOR, \
180                        (((rtems_device_minor_number)((node)->nfs->id))<<16) | (SERP_ATTR((node)).fsid & (((rtems_device_minor_number)1<<16)-1)) )
181
182#define  DIRENT_HEADER_SIZE ( sizeof(struct dirent) - \
183                        sizeof( ((struct dirent *)0)->d_name ) )
184
185
186/* debugging flags */
187#define DEBUG_COUNT_NODES       (1<<0)
188#define DEBUG_TRACK_NODES       (1<<1)
189#define DEBUG_EVALPATH          (1<<2)
190#define DEBUG_READDIR           (1<<3)
191#define DEBUG_SYSCALLS          (1<<4)
192
193/* #define DEBUG        ( DEBUG_SYSCALLS | DEBUG_COUNT_NODES ) */
194
195#ifdef DEBUG
196#define STATIC
197#else
198#define STATIC static
199#endif
200
201#define LOCK(s)         rtems_recursive_mutex_lock(&(s))
202
203#define UNLOCK(s)       rtems_recursive_mutex_unlock(&(s))
204
205RTEMS_INTERRUPT_LOCK_DEFINE(static, nfs_global_lock, "NFS")
206
207#define NFS_GLOBAL_ACQUIRE(lock_context) \
208    rtems_interrupt_lock_acquire(&nfs_global_lock, lock_context)
209
210#define NFS_GLOBAL_RELEASE(lock_context) \
211    rtems_interrupt_lock_release(&nfs_global_lock, lock_context)
212
213static inline char *
214nfs_dupname(const char *name, size_t namelen)
215{
216        char *dupname = malloc(namelen + 1);
217
218        if (dupname != NULL) {
219                memcpy(dupname, name, namelen);
220                dupname [namelen] = '\0';
221        } else {
222                errno = ENOMEM;
223        }
224
225        return dupname;
226}
227
228/*****************************************
229        Types with Associated XDR Routines
230 *****************************************/
231
232/* a string buffer with a maximal length.
233 * If the buffer pointer is NULL, it is updated
234 * with an appropriately allocated area.
235 */
236typedef struct strbuf {
237        char    *buf;
238        u_int   max;
239} strbuf;
240
241/* Read 'readlink' results into a 'strbuf'.
242 * This is convenient as it avoids
243 * one extra step of copying / length
244 * checking.
245 */
246typedef struct readlinkres_strbuf {
247        nfsstat status;
248        strbuf  strbuf;
249} readlinkres_strbuf;
250
251static bool_t
252xdr_readlinkres_strbuf(XDR *xdrs, readlinkres_strbuf *objp)
253{
254        if ( !xdr_nfsstat(xdrs, &objp->status) )
255                return FALSE;
256
257        if ( NFS_OK == objp->status ) {
258                if ( !xdr_string(xdrs, &objp->strbuf.buf, objp->strbuf.max) )
259                        return FALSE;
260        }
261        return TRUE;
262}
263
264
265/* DirInfoRec is used instead of dirresargs
266 * to convert recursion into iteration. The
267 * 'rpcgen'erated xdr_dirresargs ends up
268 * doing nested calls when unpacking the
269 * 'next' pointers.
270 */
271
272typedef struct DirInfoRec_ {
273        readdirargs     readdirargs;
274        /* clone of the 'readdirres' fields;
275         * the cookie is put into the readdirargs above
276         */
277        nfsstat         status;
278        char            *buf, *ptr;
279        int                     len;
280        bool_t          eofreached;
281} DirInfoRec, *DirInfo;
282
283/* this deals with one entry / record */
284static bool_t
285xdr_dir_info_entry(XDR *xdrs, DirInfo di)
286{
287union   {
288        char                    nambuf[NFS_MAXNAMLEN+1];
289        nfscookie               cookie;
290}                               dummy;
291struct dirent   *pde = (struct dirent *)di->ptr;
292u_int                   fileid;
293char                    *name;
294register int    nlen = 0,len,naligned = 0;
295nfscookie               *pcookie;
296
297        len = di->len;
298
299        if ( !xdr_u_int(xdrs, &fileid) )
300                return FALSE;
301
302        /* we must pass the address of a char* */
303        name = (len > NFS_MAXNAMLEN) ? pde->d_name : dummy.nambuf;
304
305        if ( !xdr_filename(xdrs, &name) ) {
306                return FALSE;
307        }
308
309        if (len >= 0) {
310                nlen      = strlen(name);
311                naligned  = nlen + 1 /* string delimiter */ + 3 /* alignment */;
312                naligned &= ~3;
313                len      -= naligned;
314        }
315
316        /* if the cookie goes into the DirInfo, we hope this doesn't fail
317         * - the caller ends up with an invalid readdirargs cookie otherwise...
318         */
319        pcookie = (len >= 0) ? &di->readdirargs.cookie : &dummy.cookie;
320        if ( !xdr_nfscookie(xdrs, pcookie) ) {
321                return FALSE;
322        }
323
324        di->len = len;
325        /* adjust the buffer pointer */
326        if (len >= 0) {
327                pde->d_ino    = fileid;
328                pde->d_namlen = nlen;
329                pde->d_off        = di->ptr - di->buf;
330#ifdef DT_UNKNOWN
331                pde->d_type = DT_UNKNOWN;
332#endif
333                if (name == dummy.nambuf) {
334                        memcpy(pde->d_name, dummy.nambuf, nlen + 1);
335                }
336                pde->d_reclen = DIRENT_HEADER_SIZE + naligned;
337                di->ptr      += pde->d_reclen;
338        }
339
340        return TRUE;
341}
342
343/* this routine loops over all entries */
344static bool_t
345xdr_dir_info(XDR *xdrs, DirInfo di)
346{
347DirInfo dip;
348
349        if ( !xdr_nfsstat(xdrs, &di->status) )
350                return FALSE;
351
352        if ( NFS_OK != di->status )
353                return TRUE;
354
355        dip = di;
356
357        while (dip) {
358                /* reserve space for the dirent 'header' - we assume it's word aligned! */
359#ifdef DEBUG
360                assert( DIRENT_HEADER_SIZE % 4 == 0 );
361#endif
362                dip->len -= DIRENT_HEADER_SIZE;
363
364                /* we pass a 0 size - size is unused since
365                 * we always pass a non-NULL pointer
366                 */
367                if ( !xdr_pointer(xdrs, (void*)&dip, 0 /* size */, (xdrproc_t)xdr_dir_info_entry) )
368                        return FALSE;
369        }
370
371        if ( ! xdr_bool(xdrs, &di->eofreached) )
372                return FALSE;
373
374        /* if everything fits into the XDR buffer but not the user's buffer,
375         * they must resume reading from where xdr_dir_info_entry() started
376         * skipping and 'eofreached' needs to be adjusted
377         */
378        if ( di->len < 0 && di->eofreached )
379                di->eofreached = FALSE;
380
381        return TRUE;
382}
383
384
385/* a type better suited for node operations
386 * than diropres.
387 * fattr and fhs are swapped so parts of this
388 * structure may be used as a diroparg which
389 * is practical when looking up paths.
390 */
391
392/* Macro for accessing serporid fields
393 */
394#define SERP_ARGS(node) ((node)->serporid.serporid_u.serporid.arg_u)
395#define SERP_ATTR(node) ((node)->serporid.serporid_u.serporid.attributes)
396#define SERP_FILE(node) ((node)->serporid.serporid_u.serporid.file)
397
398
399typedef struct serporidok {
400        fattr                                   attributes;
401        nfs_fh                                  file;
402        union   {
403                struct {
404                        filename        name;
405                }                                       diroparg;
406                struct {
407                        sattr           attributes;
408                }                                       sattrarg;
409                struct {
410                        uint32_t        offset;
411                        uint32_t        count;
412                        uint32_t        totalcount;
413                }                                       readarg;
414                struct {
415                        uint32_t        beginoffset;
416                        uint32_t        offset;
417                        uint32_t        totalcount;
418                        struct {
419                                uint32_t data_len;
420                                char* data_val;
421                        }                       data;
422                }                                       writearg;
423                struct {
424                        filename        name;
425                        sattr           attributes;
426                }                                       createarg;
427                struct {
428                        filename        name;
429                        diropargs       to;
430                }                                       renamearg;
431                struct {
432                        diropargs       to;
433                }                                       linkarg;
434                struct {
435                        filename        name;
436                        nfspath         to;
437                        sattr           attributes;
438                }                                       symlinkarg;
439                struct {
440                        nfscookie       cookie;
441                        uint32_t        count;
442                }                                       readdirarg;
443        }                                                       arg_u;
444} serporidok;
445
446typedef struct serporid {
447        nfsstat                 status;
448        union   {
449                serporidok      serporid;
450        }                               serporid_u;
451} serporid;
452
453/* an XDR routine to encode/decode the inverted diropres
454 * into an nfsnodestat;
455 *
456 * NOTE: this routine only acts on
457 *   - 'serporid.status'
458 *   - 'serporid.file'
459 *   - 'serporid.attributes'
460 * and leaves the 'arg_u' alone.
461 *
462 * The idea is that a 'diropres' is read into 'serporid'
463 * which can then be used as an argument to subsequent
464 * NFS-RPCs (after filling in the node's arg_u).
465 */
466static bool_t
467xdr_serporidok(XDR *xdrs, serporidok *objp)
468{
469     if (!xdr_nfs_fh (xdrs, &objp->file))
470         return FALSE;
471     if (!xdr_fattr (xdrs, &objp->attributes))
472         return FALSE;
473    return TRUE;
474}
475
476static bool_t
477xdr_serporid(XDR *xdrs, serporid *objp)
478{
479     if (!xdr_nfsstat (xdrs, &objp->status))
480         return FALSE;
481    switch (objp->status) {
482    case NFS_OK:
483         if (!xdr_serporidok(xdrs, &objp->serporid_u.serporid))
484             return FALSE;
485        break;
486    default:
487        break;
488    }
489    return TRUE;
490}
491
492/*****************************************
493        Data Structures and Types
494 *****************************************/
495
496/* 'time()' hack with less overhead; */
497
498/* assume reading a long word is atomic */
499#define READ_LONG_IS_ATOMIC
500
501typedef uint32_t        TimeStamp;
502
503static inline TimeStamp
504nowSeconds(void)
505{
506  rtems_interval rval;
507  rtems_clock_get_seconds_since_epoch( &rval );
508  return rval;
509}
510
511
512/* Per mounted FS structure */
513typedef struct NfsRec_ {
514                /* the NFS server we're talking to.
515                 */
516        RpcUdpServer                                             server;
517                /* statistics; how many NfsNodes are
518                 * currently alive.
519                 */
520        volatile int                                             nodesInUse;
521#if DEBUG & DEBUG_COUNT_NODES
522                /* statistics; how many 'NfsNode.str'
523                 * strings are currently allocated.
524                 */
525        volatile int                                             stringsInUse;
526#endif
527                /* A small number who uniquely
528                 * identifies a mounted NFS within
529                 * this driver (i.e. this NfsRec).
530                 * Each time a NFS is mounted, the
531                 * global ID counter is incremented
532                 * and its value is assigned to the
533                 * newly created NfsRec.
534                 */
535        u_short                                                          id;
536                /* Our RTEMS filesystem mt_entry
537                 */
538        rtems_filesystem_mount_table_entry_t *mt_entry;
539                /* Next NfsRec on a linked list who
540                 * is anchored at nfsGlob
541                 */
542        struct NfsRec_                                           *next;
543                /* Who we pretend we are
544                 */
545        u_long                                                           uid,gid;
546} NfsRec, *Nfs;
547
548typedef struct NfsNodeRec_ {
549                /* This holds this node's attributes
550                 * (stats) and its nfs filehandle.
551                 * It also contains room for nfs rpc
552                 * arguments.
553                 */
554        serporid        serporid;
555                /* The arguments we used when doing
556                 * the 'lookup' call for this node.
557                 * We need this information (especially
558                 * the directory FH) for performing
559                 * certain operations on this
560                 * node (in particular: for unlinking
561                 * it from a parent directory)
562                 */
563        diropargs               args;
564                /* FS this node belongs to
565                 */
566        Nfs                             nfs;
567                /* A buffer for the string the
568                 * args.name points to.
569                 * We need this because args.name might
570                 * temporarily point to strings on the
571                 * stack. Duplicates are allocated from
572                 * the heap and attached to 'str' so
573                 * they can be released as appropriate.
574                 */
575        char               *str;
576                /* A timestamp for the stats
577                 */
578        TimeStamp               age;
579} NfsNodeRec, *NfsNode;
580
581/*****************************************
582        Forward Declarations
583 *****************************************/
584
585static ssize_t nfs_readlink_with_node(
586        NfsNode node,
587        char *buf,
588        size_t len
589);
590
591static int updateAttr(NfsNode node, int force);
592
593/* Mask bits when setting attributes.
594 * Only the 'arg' fields with their
595 * corresponding bit set in the mask
596 * will be used. The others are left
597 * unchanged.
598 * The 'TOUCH' bits instruct nfs_sattr()
599 * to update the respective time
600 * fields to the current time
601 */
602#define SATTR_MODE              (1<<0)
603#define SATTR_UID               (1<<1)
604#define SATTR_GID               (1<<2)
605#define SATTR_SIZE              (1<<3)
606#define SATTR_ATIME             (1<<4)
607#define SATTR_TOUCHA    (1<<5)
608#define SATTR_MTIME             (1<<6)
609#define SATTR_TOUCHM    (1<<7)
610#define SATTR_TOUCH             (SATTR_TOUCHM | SATTR_TOUCHA)
611
612static int
613nfs_sattr(NfsNode node, sattr *arg, u_long mask);
614
615extern const struct _rtems_filesystem_operations_table nfs_fs_ops;
616static const struct _rtems_filesystem_file_handlers_r nfs_file_file_handlers;
617static const struct _rtems_filesystem_file_handlers_r nfs_dir_file_handlers;
618static const struct _rtems_filesystem_file_handlers_r nfs_link_file_handlers;
619static             rtems_driver_address_table            drvNfs;
620
621int
622nfsMountsShow(FILE*);
623
624rtems_status_code
625rtems_filesystem_resolve_location(char *buf, int len, rtems_filesystem_location_info_t *loc);
626
627/*****************************************
628        Global Variables
629 *****************************************/
630
631/* These are (except for MAXNAMLEN/MAXPATHLEN) copied from IMFS */
632
633static const rtems_filesystem_limits_and_options_t
634nfs_limits_and_options = {
635   5,                           /* link_max */
636   6,                           /* max_canon */
637   7,                           /* max_input */
638   NFS_MAXNAMLEN,       /* name_max */
639   NFS_MAXPATHLEN,      /* path_max */
640   2,                           /* pipe_buf */
641   1,                           /* posix_async_io */
642   2,                           /* posix_chown_restrictions */
643   3,                           /* posix_no_trunc */
644   4,                           /* posix_prio_io */
645   5,                           /* posix_sync_io */
646   6                            /* posix_vdisable */
647};
648
649/* size of an encoded 'entry' object */
650static int dirres_entry_size;
651
652/* Global stuff and statistics */
653static struct nfsstats {
654                /* A lock for protecting the
655                 * linked ist of mounted NFS
656                 * and the num_mounted_fs field
657                 */
658        rtems_recursive_mutex                   llock;
659                /* A lock for protecting misc
660                 * stuff  within the driver.
661                 * The lock must only be held
662                 * for short periods of time.
663                 */
664        rtems_recursive_mutex                   lock;
665                /* Our major number as assigned
666                 * by RTEMS
667                 */
668        rtems_device_major_number       nfs_major;
669                /* The number of currently
670                 * mounted NFS
671                 */
672        int                                                     num_mounted_fs;
673                /* A list of the currently
674                 * mounted NFS
675                 */
676        struct NfsRec_                          *mounted_fs;
677                /* A counter for allocating
678                 * unique IDs to each mounted
679                 * NFS.
680                 * Assume we are not going to
681                 * do more than 16k mounts
682                 * during the system lifetime
683                 */
684        u_short                                         fs_ids;
685
686        /* Two pools of RPC transactions;
687         * One with small send buffers
688         * the other with a big one.
689         * The actual size of the small
690         * buffer is configurable (see top).
691         *
692         * Note: The RX buffers are always
693         * big
694         */
695        RpcUdpXactPool smallPool;
696        RpcUdpXactPool bigPool;
697} nfsGlob = {RTEMS_RECURSIVE_MUTEX_INITIALIZER("NFS List"), RTEMS_RECURSIVE_MUTEX_INITIALIZER("NFS Misc"),  0xffffffff, 0, 0, 0, NULL, NULL};
698
699/*
700 * Global variable to tune the 'st_blksize' (stat(2)) value this nfs
701 * client should report.
702 * size on the server.
703 */
704#ifndef DEFAULT_NFS_ST_BLKSIZE
705#define DEFAULT_NFS_ST_BLKSIZE  NFS_MAXDATA
706#endif
707int nfsStBlksize = DEFAULT_NFS_ST_BLKSIZE;
708
709
710/*****************************************
711        Implementation
712 *****************************************/
713
714static int nfsEvaluateStatus(nfsstat nfsStatus)
715{
716        static const uint8_t nfsStatusToErrno [71] = {
717                [NFS_OK] = 0,
718                [NFSERR_PERM] = EPERM,
719                [NFSERR_NOENT] = ENOENT,
720                [3] = EIO,
721                [4] = EIO,
722                [NFSERR_IO] = EIO,
723                [NFSERR_NXIO] = ENXIO,
724                [7] = EIO,
725                [8] = EIO,
726                [9] = EIO,
727                [10] = EIO,
728                [11] = EIO,
729                [12] = EIO,
730                [NFSERR_ACCES] = EACCES,
731                [14] = EIO,
732                [15] = EIO,
733                [16] = EIO,
734                [NFSERR_EXIST] = EEXIST,
735                [18] = EIO,
736                [NFSERR_NODEV] = ENODEV,
737                [NFSERR_NOTDIR] = ENOTDIR,
738                [NFSERR_ISDIR] = EISDIR,
739                [22] = EIO,
740                [24] = EIO,
741                [25] = EIO,
742                [26] = EIO,
743                [27] = EIO,
744                [NFSERR_FBIG] = EFBIG,
745                [NFSERR_NOSPC] = ENOSPC,
746                [29] = EIO,
747                [NFSERR_ROFS] = EROFS,
748                [31] = EIO,
749                [32] = EIO,
750                [34] = EIO,
751                [35] = EIO,
752                [36] = EIO,
753                [37] = EIO,
754                [38] = EIO,
755                [39] = EIO,
756                [40] = EIO,
757                [41] = EIO,
758                [42] = EIO,
759                [44] = EIO,
760                [45] = EIO,
761                [46] = EIO,
762                [47] = EIO,
763                [48] = EIO,
764                [49] = EIO,
765                [50] = EIO,
766                [51] = EIO,
767                [52] = EIO,
768                [54] = EIO,
769                [55] = EIO,
770                [56] = EIO,
771                [57] = EIO,
772                [58] = EIO,
773                [59] = EIO,
774                [60] = EIO,
775                [61] = EIO,
776                [62] = EIO,
777                [NFSERR_NAMETOOLONG] = ENAMETOOLONG,
778                [64] = EIO,
779                [65] = EIO,
780                [NFSERR_NOTEMPTY] = ENOTEMPTY,
781                [67] = EIO,
782                [68] = EIO,
783                [NFSERR_DQUOT] = EDQUOT,
784                [NFSERR_STALE] = ESTALE
785        };
786
787        size_t idx = (size_t) nfsStatus;
788        int eno = EIO;
789        int rv = 0;
790
791        if (idx < sizeof(nfsStatusToErrno) / sizeof(nfsStatusToErrno [0])) {
792                eno = nfsStatusToErrno [idx];
793        }
794
795        if (eno != 0) {
796                errno = eno;
797                rv = -1;
798        }
799
800        return rv;
801}
802
803/* Create a Nfs object. This is
804 * per-mounted NFS information.
805 *
806 * ARGS:        The Nfs server handle.
807 *
808 * RETURNS:     Nfs on success,
809 *                      NULL on failure with
810 *                      errno set
811 *
812 * NOTE:        The submitted server
813 *                      object is 'owned' by
814 *                      this Nfs and will be
815 *                      destroyed by nfsDestroy()
816 */
817static Nfs
818nfsCreate(RpcUdpServer server)
819{
820Nfs rval = calloc(1,sizeof(*rval));
821
822        if (rval) {
823                rval->server     = server;
824                LOCK(nfsGlob.llock);
825                        rval->next                 = nfsGlob.mounted_fs;
826                        nfsGlob.mounted_fs = rval;
827                UNLOCK(nfsGlob.llock);
828        } else {
829                errno = ENOMEM;
830        }
831                return rval;
832}
833
834/* Destroy an Nfs object and
835 * its associated server
836 */
837static void
838nfsDestroy(Nfs nfs)
839{
840register Nfs prev;
841        if (!nfs)
842                return;
843
844        LOCK(nfsGlob.llock);
845                if (nfs == nfsGlob.mounted_fs)
846                        nfsGlob.mounted_fs = nfs->next;
847                else {
848                        for (prev = nfsGlob.mounted_fs;
849                                 prev && prev->next != nfs;
850                                 prev = prev->next)
851                                        /* nothing else to do */;
852                        assert( prev );
853                        prev->next = nfs->next;
854                }
855        UNLOCK(nfsGlob.llock);
856
857        nfs->next = 0; /* paranoia */
858        rpcUdpServerDestroy(nfs->server);
859        free(nfs);
860}
861
862/*
863 * Create a Node. The node will
864 * be associated with a particular
865 * mounted NFS identified by 'nfs'
866 * Optionally, a NFS file handle
867 * may be copied into this node.
868 *
869 * ARGS:        nfs of the NFS this node
870 *                      belongs to.
871 *                      NFS file handle identifying
872 *                      this node.
873 * RETURNS:     node on success,
874 *                      NULL on failure with errno
875 *                      set.
876 *
877 * NOTE:        The caller of this routine
878 *                      is responsible for copying
879 *                      a NFS file handle if she
880 *                      choses to pass a NULL fh.
881 *
882 *                      The driver code assumes the
883 *                      a node always has a valid
884 *                      NFS filehandle and file
885 *                      attributes (unless the latter
886 *                      are aged).
887 */
888static NfsNode
889nfsNodeCreate(Nfs nfs, fhandle *fh)
890{
891NfsNode rval = malloc(sizeof(*rval));
892rtems_interrupt_lock_context lock_context;
893
894#if DEBUG & DEBUG_TRACK_NODES
895        fprintf(stderr,"NFS: creating a node\n");
896#endif
897
898        if (rval) {
899                if (fh)
900                        memcpy( &SERP_FILE(rval), fh, sizeof(*fh) );
901                NFS_GLOBAL_ACQUIRE(&lock_context);
902                        nfs->nodesInUse++;
903                NFS_GLOBAL_RELEASE(&lock_context);
904                rval->nfs       = nfs;
905                rval->str               = 0;
906        } else {
907                errno = ENOMEM;
908        }
909
910        return rval;
911}
912
913/* destroy a node */
914static void
915nfsNodeDestroy(NfsNode node)
916{
917rtems_interrupt_lock_context lock_context;
918
919#if DEBUG & DEBUG_TRACK_NODES
920        fprintf(stderr,"NFS: destroying a node\n");
921#endif
922#if 0
923        if (!node)
924                return;
925        /* this probably does nothing... */
926        xdr_free(xdr_serporid, &node->serporid);
927#endif
928
929        NFS_GLOBAL_ACQUIRE(&lock_context);
930                node->nfs->nodesInUse--;
931#if DEBUG & DEBUG_COUNT_NODES
932                if (node->str)
933                        node->nfs->stringsInUse--;
934#endif
935        NFS_GLOBAL_RELEASE(&lock_context);
936
937        if (node->str)
938                free(node->str);
939
940        free(node);
941}
942
943/* Clone a given node (AKA copy constructor),
944 * i.e. create an exact copy.
945 *
946 * ARGS:        node to clone
947 * RETURNS:     new node on success
948 *                      NULL on failure with errno set.
949 *
950 * NOTE:        a string attached to 'str'
951 *                      is cloned as well. Outdated
952 *                      attributes (of the new copy
953 *                      only) will be refreshed
954 *                      (if unsuccessful, this could
955 *                      be a reason for failure to
956 *                      clone a node).
957 */
958static NfsNode
959nfsNodeClone(NfsNode node)
960{
961NfsNode rval = nfsNodeCreate(node->nfs, 0);
962
963        if (rval) {
964                *rval = *node;
965
966                /* must clone the string also */
967                if (node->str) {
968                        rval->args.name = rval->str = strdup(node->str);
969                        if (!rval->str) {
970                                errno = ENOMEM;
971                                nfsNodeDestroy(rval);
972                                return 0;
973                        }
974#if DEBUG & DEBUG_COUNT_NODES
975                        { rtems_interrupt_lock_context lock_context;
976                        NFS_GLOBAL_ACQUIRE(&lock_context);
977                                node->nfs->stringsInUse++;
978                        NFS_GLOBAL_RELEASE(&lock_context);
979                        }
980#endif
981                }
982
983                /* possibly update the stats */
984                if (updateAttr(rval, 0 /* only if necessary */)) {
985                        nfsNodeDestroy(rval);
986                        return 0;
987                }
988        }
989        return rval;
990}
991
992/* Initialize the driver.
993 *
994 * ARGS:        depth of the small and big
995 *                      transaction pools, i.e. how
996 *                      many transactions (buffers)
997 *                      should always be kept around.
998 *
999 *                      (If more transactions are needed,
1000 *                      they are created and destroyed
1001 *                      on the fly).
1002 */
1003int
1004nfsInit(int smallPoolDepth, int bigPoolDepth)
1005{
1006static int initialised = 0;
1007entry   dummy;
1008
1009        if (initialised)
1010                return 0;
1011
1012        initialised = 1;
1013
1014        fprintf(stderr,
1015          "RTEMS-NFS, " \
1016          "Till Straumann, Stanford/SLAC/SSRL 2002, " \
1017          "See LICENSE file for licensing info.\n");
1018
1019        /* Get a major number */
1020
1021        if (RTEMS_SUCCESSFUL != rtems_io_register_driver(0, &drvNfs, &nfsGlob.nfs_major)) {
1022                fprintf(stderr,"Registering NFS driver failed - %s\n", strerror(errno));
1023                errno = ENOMEM;
1024                return -1;
1025        }
1026
1027        if (0==smallPoolDepth)
1028                smallPoolDepth = 20;
1029        if (0==bigPoolDepth)
1030                bigPoolDepth   = 10;
1031
1032        rtems_recursive_mutex_init(&nfsGlob.llock, "NFSl");
1033        rtems_recursive_mutex_init(&nfsGlob.lock, "NFSm");
1034
1035        /* it's crucial to zero out the 'next' pointer
1036         * because it terminates the xdr_entry recursion
1037         *
1038         * we also must make the filename some non-zero
1039         * char pointer!
1040         */
1041
1042        memset(&dummy, 0, sizeof(dummy));
1043
1044        dummy.nextentry   = 0;
1045        dummy.name        = "somename"; /* guess average length of a filename */
1046        dirres_entry_size = xdr_sizeof((xdrproc_t)xdr_entry, &dummy);
1047
1048        nfsGlob.smallPool = rpcUdpXactPoolCreate(
1049                NFS_PROGRAM,
1050                NFS_VERSION_2,
1051                CONFIG_NFS_SMALL_XACT_SIZE,
1052                smallPoolDepth);
1053        if (nfsGlob.smallPool == NULL) {
1054                goto cleanup;
1055        }
1056
1057        nfsGlob.bigPool = rpcUdpXactPoolCreate(
1058                NFS_PROGRAM,
1059                NFS_VERSION_2,
1060                CONFIG_NFS_BIG_XACT_SIZE,
1061                bigPoolDepth);
1062        if (nfsGlob.bigPool == NULL) {
1063                goto cleanup;
1064        }
1065
1066        if (sizeof(ino_t) < sizeof(u_int)) {
1067                fprintf(stderr,
1068                        "WARNING: Using 'short st_ino' hits performance and may fail to access/find correct files\n");
1069                fprintf(stderr,
1070                        "you should fix newlib's sys/stat.h - for now I'll enable a hack...\n");
1071
1072        }
1073
1074        return 0;
1075
1076cleanup:
1077
1078        nfsCleanup();
1079        initialised = 0;
1080
1081        return -1;
1082}
1083
1084/* Driver cleanup code
1085 */
1086int
1087nfsCleanup(void)
1088{
1089int                     refuse;
1090
1091        LOCK(nfsGlob.llock);
1092        if ( (refuse = nfsGlob.num_mounted_fs) ) {
1093                fprintf(stderr,"Refuse to unload NFS; %i filesystems still mounted.\n",
1094                                                refuse);
1095                nfsMountsShow(stderr);
1096                /* yes, printing is slow - but since you try to unload the driver,
1097                 * you assume nobody is using NFS, so what if they have to wait?
1098                 */
1099                UNLOCK(nfsGlob.llock);
1100                return -1;
1101        }
1102
1103        if (nfsGlob.smallPool != NULL) {
1104                rpcUdpXactPoolDestroy(nfsGlob.smallPool);
1105                nfsGlob.smallPool = NULL;
1106        }
1107
1108        if (nfsGlob.bigPool != NULL) {
1109                rpcUdpXactPoolDestroy(nfsGlob.bigPool);
1110                nfsGlob.bigPool = NULL;
1111        }
1112
1113        if (nfsGlob.nfs_major != 0xffffffff) {
1114                rtems_io_unregister_driver(nfsGlob.nfs_major);
1115                nfsGlob.nfs_major = 0xffffffff;
1116        }
1117
1118        UNLOCK(nfsGlob.llock);
1119
1120        rtems_recursive_mutex_destroy(&nfsGlob.lock);
1121        rtems_recursive_mutex_destroy(&nfsGlob.llock);
1122
1123        return 0;
1124}
1125
1126/* NFS RPC wrapper.
1127 *
1128 * ARGS:        srvr    the NFS server we want to call
1129 *                      proc    the NFSPROC_xx we want to invoke
1130 *                      xargs   xdr routine to wrap the arguments
1131 *                      pargs   pointer to the argument object
1132 *                      xres    xdr routine to unwrap the results
1133 *                      pres    pointer to the result object
1134 *
1135 * RETURNS:     0 on success, -1 on error with errno set.
1136 *
1137 * NOTE:        the caller assumes that errno is set to
1138 *                      a nonzero value if this routine returns
1139 *                      an error (nonzero return value).
1140 *
1141 *                      This routine prints RPC error messages to
1142 *                      stderr.
1143 */
1144STATIC int
1145nfscall(
1146        RpcUdpServer    srvr,
1147        int                             proc,
1148        xdrproc_t               xargs,
1149        void *                  pargs,
1150        xdrproc_t               xres,
1151        void *                  pres)
1152{
1153RpcUdpXact              xact;
1154enum clnt_stat  stat;
1155RpcUdpXactPool  pool;
1156int                             rval = -1;
1157
1158
1159        switch (proc) {
1160                case NFSPROC_SYMLINK:
1161                case NFSPROC_WRITE:
1162                                        pool = nfsGlob.bigPool;         break;
1163                default:        pool = nfsGlob.smallPool;       break;
1164        }
1165
1166        xact = rpcUdpXactPoolGet(pool, XactGetCreate);
1167
1168        if ( !xact ) {
1169                errno = ENOMEM;
1170                return -1;
1171        }
1172
1173        if ( RPC_SUCCESS != (stat=rpcUdpSend(
1174                                                                xact,
1175                                                                srvr,
1176                                                                NFSCALL_TIMEOUT,
1177                                                                proc,
1178                                                                xres,
1179                                                                pres,
1180                                                                xargs,
1181                                                                pargs,
1182                                                                0)) ||
1183             RPC_SUCCESS != (stat=rpcUdpRcv(xact)) ) {
1184
1185                fprintf(stderr,
1186                                "NFS (proc %i) - %s\n",
1187                                proc,
1188                                clnt_sperrno(stat));
1189
1190                switch (stat) {
1191                        /* TODO: this is probably not complete and/or fully accurate */
1192                        case RPC_CANTENCODEARGS : errno = EINVAL;       break;
1193                        case RPC_AUTHERROR      : errno = EPERM;        break;
1194
1195                        case RPC_CANTSEND               :
1196                        case RPC_CANTRECV               : /* hope they have errno set */
1197                        case RPC_SYSTEMERROR    : break;
1198
1199                        default                 : errno = EIO;          break;
1200                }
1201        } else {
1202                rval = 0;
1203        }
1204
1205        /* release the transaction back into the pool */
1206        rpcUdpXactPoolPut(xact);
1207
1208        if (rval && !errno)
1209                errno = EIO;
1210
1211        return rval;
1212}
1213
1214/* Check the 'age' of a node's stats
1215 * and read the attributes from the server
1216 * if necessary.
1217 *
1218 * ARGS:        node    node to update
1219 *                      force   enforce updating ignoring
1220 *                                      the timestamp/age
1221 *
1222 * RETURNS:     0 on success,
1223 *                      -1 on failure with errno set
1224 */
1225
1226static int
1227updateAttr(NfsNode node, int force)
1228{
1229        int rv = 0;
1230
1231        if (force
1232#ifdef CONFIG_ATTR_LIFETIME
1233                || (nowSeconds() - node->age > CONFIG_ATTR_LIFETIME)
1234#endif
1235        ) {
1236                rv = nfscall(
1237                        node->nfs->server,
1238                        NFSPROC_GETATTR,
1239                        (xdrproc_t) xdr_nfs_fh, &SERP_FILE(node),
1240                        (xdrproc_t) xdr_attrstat, &node->serporid
1241                );
1242
1243                if (rv == 0) {
1244                        rv = nfsEvaluateStatus(node->serporid.status);
1245
1246                        if (rv == 0) {
1247                                node->age = nowSeconds();
1248                        }
1249                }
1250        }
1251
1252        return rv;
1253}
1254
1255/*
1256 * IP address helper.
1257 *
1258 * initialize a sockaddr_in from a
1259 * [<uid>'.'<gid>'@']<host>':'<path>" string and let
1260 * pPath point to the <path> part; retrieve the optional
1261 * uid/gids
1262 *
1263 * ARGS:        see description above
1264 *
1265 * RETURNS:     0 on success,
1266 *                      -1 on failure with errno set
1267 */
1268static int
1269buildIpAddr(u_long *puid, u_long *pgid,
1270                        char **pHost, struct sockaddr_in *psa,
1271                        char **pPath)
1272{
1273struct hostent *h;
1274char    host[64];
1275char    *chpt = *pPath;
1276char    *path;
1277int             len;
1278
1279        if ( !chpt ) {
1280                errno = EINVAL;
1281                return -1;
1282        }
1283
1284        /* look for the optional uid/gid */
1285        if ( (chpt = strchr(chpt, UIDSEP)) ) {
1286                if ( 2 != sscanf(*pPath,"%li.%li",puid,pgid) ) {
1287                        errno = EINVAL;
1288                        return -1;
1289                }
1290                chpt++;
1291        } else {
1292                *puid = geteuid();
1293                *pgid = getegid();
1294                chpt  = *pPath;
1295        }
1296        if ( pHost )
1297                *pHost = chpt;
1298
1299        /* split the device name which is in the form
1300         *
1301         * <host> ':' <path>
1302         *
1303         * into its components using a local buffer
1304         */
1305
1306        if ( !(path = strchr(chpt, HOSTDELIM)) ||
1307              (len  = path - chpt) >= sizeof(host) - 1 ) {
1308                errno = EINVAL;
1309                return -1;
1310        }
1311        /* point to path beyond ':' */
1312        path++;
1313
1314        strncpy(host, chpt, len);
1315        host[len]=0;
1316
1317  /* BEGIN OF NON-THREAD SAFE REGION */
1318
1319        h = gethostbyname(host);
1320
1321        if ( !h ) {
1322                errno = EINVAL;
1323                return -1;
1324        }
1325
1326        memcpy(&psa->sin_addr, h->h_addr, sizeof (struct in_addr));
1327
1328  /* END OF NON-THREAD SAFE REGION */
1329
1330        psa->sin_family = AF_INET;
1331        psa->sin_port   = 0;
1332        *pPath          = path;
1333        return 0;
1334}
1335
1336/* wrapper similar to nfscall.
1337 * However, since it is not used
1338 * very often, the simpler and less
1339 * efficient rpcUdpCallRp API is used.
1340 *
1341 * ARGS:        see 'nfscall()' above
1342 *
1343 * RETURNS:     RPC status
1344 */
1345static enum clnt_stat
1346mntcall(
1347        struct sockaddr_in      *psrvr,
1348        int                                     proc,
1349        xdrproc_t                       xargs,
1350        void *                          pargs,
1351        xdrproc_t                       xres,
1352        void *                          pres,
1353        u_long                          uid,
1354        u_long                          gid)
1355{
1356#ifdef MOUNT_V1_PORT
1357int                                     retry;
1358#endif
1359enum clnt_stat          stat = RPC_FAILED;
1360
1361#ifdef MOUNT_V1_PORT
1362        /* if the portmapper fails, retry a fixed port */
1363        for (retry = 1, psrvr->sin_port = 0, stat = RPC_FAILED;
1364                 retry >= 0 && stat;
1365                 stat && (psrvr->sin_port = htons(MOUNT_V1_PORT)), retry-- )
1366#endif
1367                stat  = rpcUdpCallRp(
1368                                                psrvr,
1369                                                MOUNTPROG,
1370                                                MOUNTVERS,
1371                                                proc,
1372                                                xargs,
1373                                                pargs,
1374                                                xres,
1375                                                pres,
1376                                                uid,
1377                                                gid,
1378                                                MNTCALL_TIMEOUT
1379                                );
1380        return stat;
1381}
1382
1383/*****************************************
1384        RTEMS File System Operations for NFS
1385 *****************************************/
1386
1387static bool nfs_is_directory(
1388        rtems_filesystem_eval_path_context_t *ctx,
1389        void *arg
1390)
1391{
1392        bool is_dir = false;
1393        rtems_filesystem_location_info_t *currentloc =
1394                rtems_filesystem_eval_path_get_currentloc(ctx);
1395        NfsNode node = currentloc->node_access;
1396        int force_update = 0;
1397
1398        if (updateAttr(node, force_update) == 0) {
1399                is_dir = SERP_ATTR(node).type == NFDIR;
1400        }
1401
1402        return is_dir;
1403}
1404
1405static int nfs_search_in_directory(
1406        Nfs nfs,
1407        const NfsNode dir,
1408        char *part,
1409        NfsNode entry
1410)
1411{
1412        int rv;
1413
1414        entry->nfs = nfs;
1415
1416        /* lookup one element */
1417        SERP_ATTR(entry) = SERP_ATTR(dir);
1418        SERP_FILE(entry) = SERP_FILE(dir);
1419        SERP_ARGS(entry).diroparg.name = part;
1420
1421        /* remember args / directory fh */
1422        memcpy(&entry->args, &SERP_FILE(dir), sizeof(dir->args));
1423
1424#if DEBUG & DEBUG_EVALPATH
1425        fprintf(stderr,"Looking up '%s'\n",part);
1426#endif
1427
1428        rv = nfscall(
1429                nfs->server,
1430                NFSPROC_LOOKUP,
1431                (xdrproc_t) xdr_diropargs, &SERP_FILE(entry),
1432                (xdrproc_t) xdr_serporid,  &entry->serporid
1433        );
1434
1435        if (rv == 0 && entry->serporid.status == NFS_OK) {
1436                int force_update = 1;
1437
1438                rv = updateAttr(entry, force_update);
1439        } else {
1440                rv = -1;
1441        }
1442
1443        return rv;
1444}
1445
1446static void nfs_eval_follow_link(
1447        rtems_filesystem_eval_path_context_t *ctx,
1448        NfsNode link
1449)
1450{
1451        const size_t len = NFS_MAXPATHLEN + 1;
1452        char *buf = malloc(len);
1453
1454        if (buf != NULL) {
1455                ssize_t rv = nfs_readlink_with_node(link, buf, len);
1456
1457                if (rv >= 0) {
1458                        rtems_filesystem_eval_path_recursive(ctx, buf, (size_t) rv);
1459                } else {
1460                        rtems_filesystem_eval_path_error(ctx, 0);
1461                }
1462
1463                free(buf);
1464        } else {
1465                rtems_filesystem_eval_path_error(ctx, ENOMEM);
1466        }
1467}
1468
1469static void nfs_eval_set_handlers(
1470        rtems_filesystem_eval_path_context_t *ctx,
1471        ftype type
1472)
1473{
1474        rtems_filesystem_location_info_t *currentloc =
1475                rtems_filesystem_eval_path_get_currentloc(ctx);
1476
1477        switch (type) {
1478                case NFDIR:
1479                        currentloc->handlers = &nfs_dir_file_handlers;
1480                        break;
1481                case NFREG:
1482                        currentloc->handlers = &nfs_file_file_handlers;
1483                        break;
1484                case NFLNK:
1485                        currentloc->handlers = &nfs_link_file_handlers;
1486                        break;
1487                default:
1488                        currentloc->handlers = &rtems_filesystem_handlers_default;
1489                        break;
1490        }
1491}
1492
1493static int nfs_move_node(NfsNode dst, const NfsNode src, const char *part)
1494{
1495        int rv = 0;
1496
1497        if (dst->str != NULL) {
1498#if DEBUG & DEBUG_COUNT_NODES
1499                rtems_interrupt_lock_context lock_context;
1500                NFS_GLOBAL_ACQUIRE(&lock_context);
1501                        dst->nfs->stringsInUse--;
1502                NFS_GLOBAL_RELEASE(&lock_context);
1503#endif
1504                free(dst->str);
1505        }
1506
1507        *dst = *src;
1508
1509        dst->str = dst->args.name = strdup(part);
1510        if (dst->str != NULL) {
1511#if DEBUG & DEBUG_COUNT_NODES
1512                rtems_interrupt_lock_context lock_context;
1513                NFS_GLOBAL_ACQUIRE(&lock_context);
1514                        dst->nfs->stringsInUse++;
1515                NFS_GLOBAL_RELEASE(&lock_context);
1516#endif
1517        } else {
1518                rv = -1;
1519        }
1520
1521        return rv;
1522}
1523
1524static rtems_filesystem_eval_path_generic_status nfs_eval_part(
1525        rtems_filesystem_eval_path_context_t *ctx,
1526        char *part
1527)
1528{
1529        rtems_filesystem_eval_path_generic_status status =
1530                RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
1531        rtems_filesystem_location_info_t *currentloc =
1532                rtems_filesystem_eval_path_get_currentloc(ctx);
1533        Nfs nfs = currentloc->mt_entry->fs_info;
1534        NfsNode dir = currentloc->node_access;
1535        NfsNodeRec entry;
1536        int rv = nfs_search_in_directory(nfs, dir, part, &entry);
1537
1538        if (rv == 0) {
1539                bool terminal = !rtems_filesystem_eval_path_has_path(ctx);
1540                int eval_flags = rtems_filesystem_eval_path_get_flags(ctx);
1541                bool follow_sym_link = (eval_flags & RTEMS_FS_FOLLOW_SYM_LINK) != 0;
1542                ftype type = SERP_ATTR(&entry).type;
1543
1544                rtems_filesystem_eval_path_clear_token(ctx);
1545
1546                if (type == NFLNK && (follow_sym_link || !terminal)) {
1547                        nfs_eval_follow_link(ctx, &entry);
1548                } else {
1549                        rv = nfs_move_node(dir, &entry, part);
1550                        if (rv == 0) {
1551                                nfs_eval_set_handlers(ctx, type);
1552                                if (!terminal) {
1553                                        status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
1554                                }
1555                        } else {
1556                                rtems_filesystem_eval_path_error(ctx, ENOMEM);
1557                        }
1558                }
1559        } else {
1560                status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY;
1561        }
1562
1563        return status;
1564}
1565
1566static rtems_filesystem_eval_path_generic_status nfs_eval_token(
1567        rtems_filesystem_eval_path_context_t *ctx,
1568        void *arg,
1569        const char *token,
1570        size_t tokenlen
1571)
1572{
1573        rtems_filesystem_eval_path_generic_status status =
1574                RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
1575
1576        if (rtems_filesystem_is_current_directory(token, tokenlen)) {
1577                rtems_filesystem_eval_path_clear_token(ctx);
1578                if (rtems_filesystem_eval_path_has_path(ctx)) {
1579                        status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
1580                }
1581        } else {
1582                char *part = nfs_dupname(token, tokenlen);
1583
1584                if (part != NULL) {
1585                        status = nfs_eval_part(ctx, part);
1586                        free(part);
1587                } else {
1588                        rtems_filesystem_eval_path_error(ctx, ENOMEM);
1589                }
1590        }
1591
1592        return status;
1593}
1594
1595static const rtems_filesystem_eval_path_generic_config nfs_eval_config = {
1596        .is_directory = nfs_is_directory,
1597        .eval_token = nfs_eval_token
1598};
1599
1600static void nfs_eval_path(rtems_filesystem_eval_path_context_t *ctx)
1601{
1602        rtems_filesystem_eval_path_generic(ctx, NULL, &nfs_eval_config);
1603}
1604
1605/* create a hard link */
1606
1607static int nfs_link(
1608        const rtems_filesystem_location_info_t *parentloc,
1609        const rtems_filesystem_location_info_t *targetloc,
1610        const char *name,
1611        size_t namelen
1612)
1613{
1614int rv = 0;
1615NfsNode pNode = parentloc->node_access;
1616nfsstat status;
1617NfsNode tNode = targetloc->node_access;
1618char *dupname;
1619
1620        dupname = nfs_dupname(name, namelen);
1621        if (dupname == NULL)
1622                return -1;
1623
1624#if DEBUG & DEBUG_SYSCALLS
1625        fprintf(stderr,"Creating link '%s'\n",dupname);
1626#endif
1627
1628        memcpy(&SERP_ARGS(tNode).linkarg.to.dir,
1629                   &SERP_FILE(pNode),
1630                   sizeof(SERP_FILE(pNode)));
1631
1632        SERP_ARGS(tNode).linkarg.to.name = dupname;
1633
1634        rv = nfscall(
1635                tNode->nfs->server,
1636                NFSPROC_LINK,
1637                (xdrproc_t)xdr_linkargs, &SERP_FILE(tNode),
1638                (xdrproc_t)xdr_nfsstat, &status
1639        );
1640
1641        if (rv == 0) {
1642                rv = nfsEvaluateStatus(status);
1643#if DEBUG & DEBUG_SYSCALLS
1644                if (rv != 0) {
1645                        perror("nfs_link");
1646                }
1647#endif
1648        }
1649
1650        free(dupname);
1651
1652        return rv;
1653
1654}
1655
1656static int nfs_do_unlink(
1657        const rtems_filesystem_location_info_t *parentloc,
1658        const rtems_filesystem_location_info_t *loc,
1659        int                                                               proc
1660)
1661{
1662int rv = 0;
1663nfsstat                 status;
1664NfsNode                 node  = loc->node_access;
1665Nfs                             nfs   = node->nfs;
1666#if DEBUG & DEBUG_SYSCALLS
1667char                    *name = NFSPROC_REMOVE == proc ?
1668                                                        "nfs_unlink" : "nfs_rmdir";
1669#endif
1670
1671        /* The FS generics have determined that pathloc is _not_
1672         * a directory. Hence we may assume that the parent
1673         * is in our NFS.
1674         */
1675
1676#if DEBUG & DEBUG_SYSCALLS
1677        assert( node->args.name == node->str && node->str );
1678
1679        fprintf(stderr,"%s '%s'\n", name, node->args.name);
1680#endif
1681
1682        rv = nfscall(
1683                nfs->server,
1684                proc,
1685                (xdrproc_t)xdr_diropargs, &node->args,
1686                (xdrproc_t)xdr_nfsstat, &status
1687        );
1688
1689        if (rv == 0) {
1690                rv = nfsEvaluateStatus(status);
1691#if DEBUG & DEBUG_SYSCALLS
1692                if (rv != 0) {
1693                        perror(name);
1694                }
1695#endif
1696        }
1697
1698        return rv;
1699}
1700
1701static int nfs_chown(
1702        const rtems_filesystem_location_info_t  *pathloc,       /* IN */
1703        uid_t                                    owner,         /* IN */
1704        gid_t                                    group          /* IN */
1705)
1706{
1707sattr   arg;
1708
1709        arg.uid = owner;
1710        arg.gid = group;
1711
1712        return nfs_sattr(pathloc->node_access, &arg, SATTR_UID | SATTR_GID);
1713
1714}
1715
1716static int nfs_clonenode(rtems_filesystem_location_info_t *loc)
1717{
1718        NfsNode node = loc->node_access;
1719
1720        LOCK(nfsGlob.lock);
1721        node = nfsNodeClone(node);
1722        UNLOCK(nfsGlob.lock);
1723
1724        loc->node_access = node;
1725
1726        return node != NULL ? 0 : -1;
1727}
1728
1729/* Cleanup the FS private info attached to pathloc->node_access */
1730static void nfs_freenode(
1731        const rtems_filesystem_location_info_t      *pathloc       /* IN */
1732)
1733{
1734#if DEBUG & DEBUG_COUNT_NODES
1735Nfs     nfs    = ((NfsNode)pathloc->node_access)->nfs;
1736
1737        /* print counts at entry where they are > 0 so 'nfs' is safe from being destroyed
1738         * and there's no race condition
1739         */
1740        fprintf(stderr,
1741                        "entering freenode, in use count is %i nodes, %i strings\n",
1742                        nfs->nodesInUse,
1743                        nfs->stringsInUse);
1744#endif
1745
1746        nfsNodeDestroy(pathloc->node_access);
1747}
1748
1749/* NOTE/TODO: mounting on top of NFS is not currently supported
1750 *
1751 * Challenge: stateless protocol. It would be possible to
1752 * delete mount points on the server. We would need some sort
1753 * of a 'garbage collector' looking for dead/unreachable
1754 * mount points and unmounting them.
1755 * Also, the path evaluation routine would have to check
1756 * for crossing mount points. Crossing over from one NFS
1757 * into another NFS could probably handled iteratively
1758 * rather than by recursion.
1759 */
1760
1761int rtems_nfs_initialize(
1762  rtems_filesystem_mount_table_entry_t *mt_entry,
1763  const void                           *data
1764)
1765{
1766char                            *host;
1767struct sockaddr_in      saddr;
1768enum clnt_stat          stat;
1769fhstatus                        fhstat;
1770u_long                          uid,gid;
1771#ifdef NFS_V2_PORT
1772int                                     retry;
1773#endif
1774Nfs                                     nfs       = 0;
1775NfsNode                         rootNode  = 0;
1776RpcUdpServer            nfsServer = 0;
1777int                                     e         = -1;
1778char                            *path     = mt_entry->dev;
1779
1780  if (rpcUdpInit () < 0) {
1781    fprintf (stderr, "error: initialising RPC\n");
1782    return -1;
1783  }
1784
1785        if (nfsInit(0, 0) != 0) {
1786                fprintf (stderr, "error: initialising NFS\n");
1787                return -1;
1788        };
1789
1790#if 0
1791        printf("Trying to mount %s on %s\n",path,mntpoint);
1792#endif
1793
1794        if ( buildIpAddr(&uid, &gid, &host, &saddr, &path) )
1795                return -1;
1796
1797#ifdef NFS_V2_PORT
1798        /* if the portmapper fails, retry a fixed port */
1799        for (retry = 1, saddr.sin_port = 0, stat = RPC_FAILED;
1800                 retry >= 0 && stat;
1801                 stat && (saddr.sin_port = htons(NFS_V2_PORT)), retry-- )
1802#endif
1803                stat = rpcUdpServerCreate(
1804                                        &saddr,
1805                                        NFS_PROGRAM,
1806                                        NFS_VERSION_2,
1807                                        uid,
1808                                        gid,
1809                                        &nfsServer
1810                                        );
1811
1812        if ( RPC_SUCCESS != stat ) {
1813                fprintf(stderr,
1814                                "Unable to contact NFS server - invalid port? (%s)\n",
1815                                clnt_sperrno(stat));
1816                e = EPROTONOSUPPORT;
1817                goto cleanup;
1818        }
1819
1820
1821        /* first, try to ping the NFS server by
1822         * calling the NULL proc.
1823         */
1824        if ( nfscall(nfsServer,
1825                                         NFSPROC_NULL,
1826                                         (xdrproc_t)xdr_void, 0,
1827                                         (xdrproc_t)xdr_void, 0) ) {
1828
1829                fputs("NFS Ping ",stderr);
1830                fwrite(host, 1, path-host-1, stderr);
1831                fprintf(stderr," failed: %s\n", strerror(errno));
1832
1833                e = errno ? errno : EIO;
1834                goto cleanup;
1835        }
1836
1837        /* that seemed to work - we now try the
1838         * actual mount
1839         */
1840
1841        /* reuse server address but let the mntcall()
1842         * search for the mountd's port
1843         */
1844        saddr.sin_port = 0;
1845
1846        stat = mntcall( &saddr,
1847                                        MOUNTPROC_MNT,
1848                                        (xdrproc_t)xdr_dirpath,
1849                                        &path,
1850                                        (xdrproc_t)xdr_fhstatus,
1851                                        &fhstat,
1852                                        uid,
1853                                        gid );
1854
1855        if (stat) {
1856                fprintf(stderr,"MOUNT -- %s\n",clnt_sperrno(stat));
1857                if ( e<=0 )
1858                        e = EIO;
1859                goto cleanup;
1860        } else if (NFS_OK != (e=fhstat.fhs_status)) {
1861                fprintf(stderr,"MOUNT: %s\n",strerror(e));
1862                goto cleanup;
1863        }
1864
1865        nfs = nfsCreate(nfsServer);
1866        assert( nfs );
1867        nfsServer = 0;
1868
1869        nfs->uid  = uid;
1870        nfs->gid  = gid;
1871
1872        /* that seemed to work - we now create the root node
1873         * and we also must obtain the root node attributes
1874         */
1875        rootNode = nfsNodeCreate(nfs, &fhstat.fhstatus_u.fhs_fhandle);
1876        assert( rootNode );
1877
1878        if ( updateAttr(rootNode, 1 /* force */) ) {
1879                e = errno;
1880                goto cleanup;
1881        }
1882
1883        /* looks good so far */
1884
1885        mt_entry->mt_fs_root->location.node_access = rootNode;
1886
1887        rootNode = 0;
1888
1889        mt_entry->ops = &nfs_fs_ops;
1890        mt_entry->mt_fs_root->location.handlers  = &nfs_dir_file_handlers;
1891        mt_entry->pathconf_limits_and_options = &nfs_limits_and_options;
1892
1893        LOCK(nfsGlob.llock);
1894                nfsGlob.num_mounted_fs++;
1895                /* allocate a new ID for this FS */
1896                nfs->id = nfsGlob.fs_ids++;
1897        UNLOCK(nfsGlob.llock);
1898
1899        mt_entry->fs_info                                = nfs;
1900        nfs->mt_entry                                    = mt_entry;
1901        nfs = 0;
1902
1903        e = 0;
1904
1905cleanup:
1906        if (nfs)
1907                nfsDestroy(nfs);
1908        if (nfsServer)
1909                rpcUdpServerDestroy(nfsServer);
1910        if (rootNode)
1911                nfsNodeDestroy(rootNode);
1912        if (e)
1913                rtems_set_errno_and_return_minus_one(e);
1914        else
1915                return 0;
1916}
1917
1918/* This op is called when they try to unmount THIS fs */
1919STATIC void nfs_fsunmount_me(
1920        rtems_filesystem_mount_table_entry_t *mt_entry    /* in */
1921)
1922{
1923enum clnt_stat          stat;
1924struct sockaddr_in      saddr;
1925char                    *path = mt_entry->dev;
1926int                     nodesInUse;
1927u_long                  uid,gid;
1928int                     status;
1929
1930LOCK(nfsGlob.llock);
1931        nodesInUse = ((Nfs)mt_entry->fs_info)->nodesInUse;
1932
1933        if (nodesInUse > 1 /* one ref to the root node used by us */) {
1934                UNLOCK(nfsGlob.llock);
1935                fprintf(stderr,
1936                                "Refuse to unmount; there are still %i nodes in use (1 used by us)\n",
1937                                nodesInUse);
1938                rtems_fatal_error_occurred(0xdeadbeef);
1939                return;
1940        }
1941
1942        status = buildIpAddr(&uid, &gid, 0, &saddr, &path);
1943        assert( !status );
1944
1945        stat = mntcall( &saddr,
1946                                        MOUNTPROC_UMNT,
1947                                        (xdrproc_t)xdr_dirpath, &path,
1948                                        (xdrproc_t)xdr_void,     0,
1949                                    uid,
1950                                    gid
1951                                  );
1952
1953        if (stat) {
1954                UNLOCK(nfsGlob.llock);
1955                fprintf(stderr,"NFS UMOUNT -- %s\n", clnt_sperrno(stat));
1956                return;
1957        }
1958
1959        nfsNodeDestroy(mt_entry->mt_fs_root->location.node_access);
1960        mt_entry->mt_fs_root->location.node_access = 0;
1961
1962        nfsDestroy(mt_entry->fs_info);
1963        mt_entry->fs_info = 0;
1964
1965        nfsGlob.num_mounted_fs--;
1966UNLOCK(nfsGlob.llock);
1967}
1968
1969static int nfs_mknod(
1970        const rtems_filesystem_location_info_t *parentloc,
1971        const char *name,
1972        size_t namelen,
1973        mode_t mode,
1974        dev_t dev
1975)
1976{
1977
1978int                                     rv = 0;
1979struct timeval                          now;
1980diropres                                res;
1981NfsNode                                 node = parentloc->node_access;
1982Nfs                                     nfs  = node->nfs;
1983mode_t                                  type = S_IFMT & mode;
1984char                                    *dupname;
1985
1986        if (type != S_IFDIR && type != S_IFREG)
1987                rtems_set_errno_and_return_minus_one(ENOTSUP);
1988
1989        dupname = nfs_dupname(name, namelen);
1990        if (dupname == NULL)
1991                return -1;
1992
1993#if DEBUG & DEBUG_SYSCALLS
1994        fprintf(stderr,"nfs_mknod: creating %s\n", dupname);
1995#endif
1996
1997        rtems_clock_get_tod_timeval(&now);
1998
1999        SERP_ARGS(node).createarg.name                  = dupname;
2000        SERP_ARGS(node).createarg.attributes.mode       = mode;
2001        SERP_ARGS(node).createarg.attributes.uid        = nfs->uid;
2002        SERP_ARGS(node).createarg.attributes.gid        = nfs->gid;
2003        SERP_ARGS(node).createarg.attributes.size       = 0;
2004        SERP_ARGS(node).createarg.attributes.atime.seconds      = now.tv_sec;
2005        SERP_ARGS(node).createarg.attributes.atime.useconds     = now.tv_usec;
2006        SERP_ARGS(node).createarg.attributes.mtime.seconds      = now.tv_sec;
2007        SERP_ARGS(node).createarg.attributes.mtime.useconds     = now.tv_usec;
2008
2009        rv = nfscall(
2010                nfs->server,
2011                (type == S_IFDIR) ? NFSPROC_MKDIR : NFSPROC_CREATE,
2012                (xdrproc_t)xdr_createargs, &SERP_FILE(node),
2013                (xdrproc_t)xdr_diropres, &res
2014        );
2015
2016        if (rv == 0) {
2017                rv = nfsEvaluateStatus(res.status);
2018#if DEBUG & DEBUG_SYSCALLS
2019                if (rv != 0) {
2020                        perror("nfs_mknod");
2021                }
2022#endif
2023        }
2024
2025        free(dupname);
2026
2027        return rv;
2028}
2029
2030static int nfs_rmnod(
2031        const rtems_filesystem_location_info_t *parentloc,
2032        const rtems_filesystem_location_info_t *loc
2033)
2034{
2035        int rv = 0;
2036        NfsNode node  = loc->node_access;
2037        int force_update = 0;
2038
2039        if (updateAttr(node, force_update) == 0) {
2040                int proc = SERP_ATTR(node).type == NFDIR
2041                        ? NFSPROC_RMDIR
2042                                : NFSPROC_REMOVE;
2043
2044                rv = nfs_do_unlink(parentloc, loc, proc);
2045        } else {
2046                rv = -1;
2047        }
2048
2049        return rv;
2050}
2051
2052static int nfs_utime(
2053        const rtems_filesystem_location_info_t  *pathloc, /* IN */
2054        time_t                                   actime,  /* IN */
2055        time_t                                   modtime  /* IN */
2056)
2057{
2058sattr   arg;
2059
2060        /* TODO: add rtems EPOCH - UNIX EPOCH seconds */
2061        arg.atime.seconds  = actime;
2062        arg.atime.useconds = 0;
2063        arg.mtime.seconds  = modtime;
2064        arg.mtime.useconds = 0;
2065
2066        return nfs_sattr(pathloc->node_access, &arg, SATTR_ATIME | SATTR_MTIME);
2067}
2068
2069static int nfs_symlink(
2070        const rtems_filesystem_location_info_t *parentloc,
2071        const char *name,
2072        size_t namelen,
2073        const char *target
2074)
2075{
2076int                                     rv = 0;
2077struct timeval                          now;
2078nfsstat                                 status;
2079NfsNode                                 node = parentloc->node_access;
2080Nfs                                     nfs  = node->nfs;
2081char                                    *dupname;
2082
2083        dupname = nfs_dupname(name, namelen);
2084        if (dupname == NULL)
2085                return -1;
2086
2087#if DEBUG & DEBUG_SYSCALLS
2088        fprintf(stderr,"nfs_symlink: creating %s -> %s\n", dupname, target);
2089#endif
2090
2091        rtems_clock_get_tod_timeval(&now);
2092
2093        SERP_ARGS(node).symlinkarg.name                 = dupname;
2094        SERP_ARGS(node).symlinkarg.to                           = (nfspath) target;
2095
2096        SERP_ARGS(node).symlinkarg.attributes.mode      = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
2097        SERP_ARGS(node).symlinkarg.attributes.uid       = nfs->uid;
2098        SERP_ARGS(node).symlinkarg.attributes.gid       = nfs->gid;
2099        SERP_ARGS(node).symlinkarg.attributes.size      = 0;
2100        SERP_ARGS(node).symlinkarg.attributes.atime.seconds  = now.tv_sec;
2101        SERP_ARGS(node).symlinkarg.attributes.atime.useconds = now.tv_usec;
2102        SERP_ARGS(node).symlinkarg.attributes.mtime.seconds  = now.tv_sec;
2103        SERP_ARGS(node).symlinkarg.attributes.mtime.useconds = now.tv_usec;
2104
2105        rv = nfscall(
2106                nfs->server,
2107                NFSPROC_SYMLINK,
2108                (xdrproc_t)xdr_symlinkargs, &SERP_FILE(node),
2109                (xdrproc_t)xdr_nfsstat, &status
2110        );
2111
2112        if (rv == 0) {
2113                rv = nfsEvaluateStatus(status);
2114#if DEBUG & DEBUG_SYSCALLS
2115                perror("nfs_symlink");
2116#endif
2117        }
2118
2119        free(dupname);
2120
2121        return rv;
2122}
2123
2124static ssize_t nfs_readlink_with_node(
2125        NfsNode node,
2126        char *buf,
2127        size_t len
2128)
2129{
2130        ssize_t rv;
2131        Nfs nfs = node->nfs;
2132        readlinkres_strbuf rr;
2133
2134        rr.strbuf.buf = buf;
2135        rr.strbuf.max = len - 1;
2136
2137        rv = nfscall(
2138                nfs->server,
2139                NFSPROC_READLINK,
2140                (xdrproc_t)xdr_nfs_fh, &SERP_FILE(node),
2141                (xdrproc_t)xdr_readlinkres_strbuf, &rr
2142        );
2143
2144        if (rv == 0) {
2145                rv = nfsEvaluateStatus(rr.status);
2146
2147                if (rv == 0) {
2148                        rv = (ssize_t) strlen(rr.strbuf.buf);
2149                } else {
2150#if DEBUG & DEBUG_SYSCALLS
2151                        perror("nfs_readlink_with_node");
2152#endif
2153                }
2154        }
2155
2156        return rv;
2157}
2158
2159static ssize_t nfs_readlink(
2160        const rtems_filesystem_location_info_t *loc,
2161        char *buf,
2162        size_t len
2163)
2164{
2165        NfsNode node = loc->node_access;
2166
2167        return nfs_readlink_with_node(node, buf, len);
2168}
2169
2170static int nfs_rename(
2171        const rtems_filesystem_location_info_t *oldparentloc,
2172        const rtems_filesystem_location_info_t *oldloc,
2173        const rtems_filesystem_location_info_t *newparentloc,
2174        const char *name,
2175        size_t namelen
2176)
2177{
2178        int rv = 0;
2179        char *dupname = nfs_dupname(name, namelen);
2180
2181        if (dupname != NULL) {
2182                NfsNode oldParentNode = oldparentloc->node_access;
2183                NfsNode oldNode = oldloc->node_access;
2184                NfsNode newParentNode = newparentloc->node_access;
2185                Nfs nfs = oldParentNode->nfs;
2186                const nfs_fh *toDirSrc = &SERP_FILE(newParentNode);
2187                nfs_fh *toDirDst = &SERP_ARGS(oldParentNode).renamearg.to.dir;
2188                nfsstat status;
2189
2190                SERP_ARGS(oldParentNode).renamearg.name = oldNode->str;
2191                SERP_ARGS(oldParentNode).renamearg.to.name = dupname;
2192                memcpy(toDirDst, toDirSrc, sizeof(*toDirDst));
2193
2194                rv = nfscall(
2195                        nfs->server,
2196                        NFSPROC_RENAME,
2197                        (xdrproc_t) xdr_renameargs,
2198                        &SERP_FILE(oldParentNode),
2199                        (xdrproc_t) xdr_nfsstat,
2200                        &status
2201                );
2202
2203                if (rv == 0) {
2204                        rv = nfsEvaluateStatus(status);
2205                }
2206
2207                free(dupname);
2208        } else {
2209                rv = -1;
2210        }
2211
2212        return rv;
2213}
2214
2215static void nfs_lock(const rtems_filesystem_mount_table_entry_t *mt_entry)
2216{
2217}
2218
2219static void nfs_unlock(const rtems_filesystem_mount_table_entry_t *mt_entry)
2220{
2221}
2222
2223static bool nfs_are_nodes_equal(
2224        const rtems_filesystem_location_info_t *a,
2225        const rtems_filesystem_location_info_t *b
2226)
2227{
2228        bool equal = false;
2229        NfsNode na = a->node_access;
2230
2231        if (updateAttr(na, 0) == 0) {
2232                NfsNode nb = b->node_access;
2233
2234                if (updateAttr(nb, 0) == 0) {
2235                        equal = SERP_ATTR(na).fileid == SERP_ATTR(nb).fileid
2236                                && SERP_ATTR(na).fsid == SERP_ATTR(nb).fsid;
2237                }
2238        }
2239
2240        return equal;
2241}
2242
2243static int nfs_fchmod(
2244        const rtems_filesystem_location_info_t *loc,
2245        mode_t mode
2246)
2247{
2248sattr   arg;
2249
2250        arg.mode = mode;
2251        return nfs_sattr(loc->node_access, &arg, SATTR_MODE);
2252
2253}
2254
2255const struct _rtems_filesystem_operations_table nfs_fs_ops = {
2256        .lock_h         = nfs_lock,
2257        .unlock_h       = nfs_unlock,
2258        .eval_path_h    = nfs_eval_path,
2259        .link_h         = nfs_link,
2260        .are_nodes_equal_h = nfs_are_nodes_equal,
2261        .mknod_h        = nfs_mknod,
2262        .rmnod_h        = nfs_rmnod,
2263        .fchmod_h       = nfs_fchmod,
2264        .chown_h        = nfs_chown,
2265        .clonenod_h     = nfs_clonenode,
2266        .freenod_h      = nfs_freenode,
2267        .mount_h        = rtems_filesystem_default_mount,
2268        .unmount_h      = rtems_filesystem_default_unmount,
2269        .fsunmount_me_h = nfs_fsunmount_me,
2270        .utime_h        = nfs_utime,
2271        .symlink_h      = nfs_symlink,
2272        .readlink_h     = nfs_readlink,
2273        .rename_h       = nfs_rename,
2274        .statvfs_h      = rtems_filesystem_default_statvfs
2275};
2276
2277/*****************************************
2278        File Handlers
2279
2280        NOTE: the FS generics expect a FS'
2281              evalpath_h() to switch the
2282                  pathloc->handlers according
2283                  to the pathloc/node's file
2284                  type.
2285                  We currently have 'file' and
2286                  'directory' handlers and very
2287                  few 'symlink' handlers.
2288
2289                  The handlers for each type are
2290                  implemented or #defined ZERO
2291                  in a 'nfs_file_xxx',
2292                  'nfs_dir_xxx', 'nfs_link_xxx'
2293                  sequence below this point.
2294
2295                  In some cases, a common handler,
2296                  can be used for all file types.
2297                  It is then simply called
2298                  'nfs_xxx'.
2299 *****************************************/
2300
2301/* stateless NFS protocol makes this trivial */
2302static int nfs_file_open(
2303        rtems_libio_t *iop,
2304        const char    *pathname,
2305        int           oflag,
2306        mode_t        mode
2307)
2308{
2309        return 0;
2310}
2311
2312/* reading directories is not stateless; we must
2313 * remember the last 'read' position, i.e.
2314 * the server 'cookie'. We do manage this information
2315 * attached to the pathinfo.node_access_2.
2316 */
2317static int nfs_dir_open(
2318        rtems_libio_t *iop,
2319        const char    *pathname,
2320        int           oflag,
2321        mode_t        mode
2322)
2323{
2324NfsNode         node = iop->pathinfo.node_access;
2325DirInfo         di;
2326
2327        /* create a readdirargs object and copy the file handle;
2328         * attach to the pathinfo.node_access_2
2329         */
2330
2331        di = (DirInfo) malloc(sizeof(*di));
2332        iop->pathinfo.node_access_2 = di;
2333
2334        if ( !di  ) {
2335                errno = ENOMEM;
2336                return -1;
2337        }
2338
2339        memcpy( &di->readdirargs.dir,
2340                        &SERP_FILE(node),
2341                        sizeof(di->readdirargs.dir) );
2342
2343        /* rewind cookie */
2344        memset( &di->readdirargs.cookie,
2345                0,
2346                sizeof(di->readdirargs.cookie) );
2347
2348        di->eofreached = FALSE;
2349
2350        return 0;
2351}
2352
2353static int nfs_file_close(
2354        rtems_libio_t *iop
2355)
2356{
2357        return 0;
2358}
2359
2360static int nfs_dir_close(
2361        rtems_libio_t *iop
2362)
2363{
2364        free(iop->pathinfo.node_access_2);
2365        iop->pathinfo.node_access_2 = 0;
2366        return 0;
2367}
2368
2369static ssize_t nfs_file_read_chunk(
2370        NfsNode node,
2371        uint32_t offset,
2372        void *buffer,
2373        size_t count
2374)
2375{
2376ssize_t rv;
2377readres rr;
2378Nfs             nfs  = node->nfs;
2379
2380        SERP_ARGS(node).readarg.offset          = offset;
2381        SERP_ARGS(node).readarg.count           = count;
2382        SERP_ARGS(node).readarg.totalcount      = UINT32_C(0xdeadbeef);
2383
2384        rr.readres_u.reply.data.data_val        = buffer;
2385
2386        rv = nfscall(
2387                nfs->server,
2388                NFSPROC_READ,
2389                (xdrproc_t)xdr_readargs, &SERP_FILE(node),
2390                (xdrproc_t)xdr_readres, &rr
2391        );
2392
2393        if (rv == 0) {
2394                rv = nfsEvaluateStatus(rr.status);
2395
2396                if (rv == 0) {
2397                        rv = rr.readres_u.reply.data.data_len;
2398
2399#if DEBUG & DEBUG_SYSCALLS
2400                        fprintf(stderr,
2401                                "Read %i (asked for %i) bytes from offset %i to 0x%08x\n",
2402                                rr.readres_u.reply.data.data_len,
2403                                count,
2404                                iop->offset,
2405                                rr.readres_u.reply.data.data_val);
2406#endif
2407                }
2408        }
2409
2410        return rv;
2411}
2412
2413static ssize_t nfs_file_read(
2414        rtems_libio_t *iop,
2415        void *buffer,
2416        size_t count
2417)
2418{
2419        ssize_t rv = 0;
2420        NfsNode node = iop->pathinfo.node_access;
2421        uint32_t offset = iop->offset;
2422        char *in = buffer;
2423
2424        if (iop->offset < 0) {
2425                errno = EINVAL;
2426                return -1;
2427        }
2428
2429        if ((uintmax_t) iop->offset >= UINT32_MAX) {
2430                errno = EFBIG;
2431                return -1;
2432        }
2433
2434        if (count > UINT32_MAX - offset) {
2435                count = UINT32_MAX - offset;
2436        }
2437
2438        do {
2439                size_t chunk = count <= NFS_MAXDATA ? count : NFS_MAXDATA;
2440                ssize_t done = nfs_file_read_chunk(node, offset, in, chunk);
2441
2442                if (done > 0) {
2443                        offset += (uint32_t) done;
2444                        in += done;
2445                        count -= (size_t) done;
2446                        rv += done;
2447                } else {
2448                        count = 0;
2449                        if (done < 0) {
2450                                rv = -1;
2451                        }
2452                }
2453        } while (count > 0);
2454
2455        if (rv > 0) {
2456                iop->offset = offset;
2457        }
2458
2459        return rv;
2460}
2461
2462/* this is called by readdir() / getdents() */
2463static ssize_t nfs_dir_read(
2464        rtems_libio_t *iop,
2465        void          *buffer,
2466        size_t        count
2467)
2468{
2469ssize_t rv;
2470DirInfo                 di     = iop->pathinfo.node_access_2;
2471RpcUdpServer    server = ((Nfs)iop->pathinfo.mt_entry->fs_info)->server;
2472
2473        if ( di->eofreached )
2474                return 0;
2475
2476        di->ptr = di->buf = buffer;
2477
2478        /* align + round down the buffer */
2479        count &= ~ (DIRENT_HEADER_SIZE - 1);
2480        di->len = count;
2481
2482#if 0
2483        /* now estimate the number of entries we should ask for */
2484        count /= DIRENT_HEADER_SIZE + CONFIG_AVG_NAMLEN;
2485
2486        /* estimate the encoded size that might take up */
2487        count *= dirres_entry_size + CONFIG_AVG_NAMLEN;
2488#else
2489        /* integer arithmetics are better done the other way round */
2490        count *= dirres_entry_size + CONFIG_AVG_NAMLEN;
2491        count /= DIRENT_HEADER_SIZE + CONFIG_AVG_NAMLEN;
2492#endif
2493
2494        if (count > NFS_MAXDATA)
2495                count = NFS_MAXDATA;
2496
2497        di->readdirargs.count = count;
2498
2499#if DEBUG & DEBUG_READDIR
2500        fprintf(stderr,
2501                        "Readdir: asking for %i XDR bytes, buffer is %i\n",
2502                        count, di->len);
2503#endif
2504
2505        rv = nfscall(
2506                server,
2507                NFSPROC_READDIR,
2508                (xdrproc_t)xdr_readdirargs, &di->readdirargs,
2509                (xdrproc_t)xdr_dir_info, di
2510        );
2511
2512        if (rv == 0) {
2513                rv = nfsEvaluateStatus(di->status);
2514
2515                if (rv == 0) {
2516                        rv = (char*)di->ptr - (char*)buffer;
2517                }
2518        }
2519
2520        return rv;
2521}
2522
2523static ssize_t nfs_file_write(
2524        rtems_libio_t *iop,
2525        const void    *buffer,
2526        size_t        count
2527)
2528{
2529ssize_t rv;
2530NfsNode         node = iop->pathinfo.node_access;
2531Nfs                     nfs  = node->nfs;
2532
2533        if (count > NFS_MAXDATA)
2534                count = NFS_MAXDATA;
2535
2536
2537        SERP_ARGS(node).writearg.beginoffset = UINT32_C(0xdeadbeef);
2538        if (rtems_libio_iop_is_append(iop)) {
2539                if ( updateAttr(node, 0) ) {
2540                        return -1;
2541                }
2542                if (SERP_ATTR(node).size >= UINT32_MAX) {
2543                        errno = EFBIG;
2544                        return -1;
2545                }
2546                SERP_ARGS(node).writearg.offset = SERP_ATTR(node).size;
2547        } else {
2548                if (iop->offset < 0) {
2549                        errno = EINVAL;
2550                        return -1;
2551                }
2552                if ((uintmax_t) iop->offset >= UINT32_MAX) {
2553                        errno = EFBIG;
2554                        return -1;
2555                }
2556                SERP_ARGS(node).writearg.offset = iop->offset;
2557        }
2558
2559        if (count > UINT32_MAX - SERP_ARGS(node).writearg.offset) {
2560                count = UINT32_MAX - SERP_ARGS(node).writearg.offset;
2561        }
2562
2563        SERP_ARGS(node).writearg.totalcount        = UINT32_C(0xdeadbeef);
2564        SERP_ARGS(node).writearg.data.data_len = count;
2565        SERP_ARGS(node).writearg.data.data_val = (void*)buffer;
2566
2567        /* write XDR buffer size will be chosen by nfscall based
2568         * on the PROC specifier
2569         */
2570
2571        rv = nfscall(
2572                nfs->server,
2573                NFSPROC_WRITE,
2574                (xdrproc_t)xdr_writeargs, &SERP_FILE(node),
2575                (xdrproc_t)xdr_attrstat, &node->serporid
2576        );
2577
2578        if (rv == 0) {
2579                rv = nfsEvaluateStatus(node->serporid.status);
2580
2581                if (rv == 0) {
2582                        node->age = nowSeconds();
2583
2584                        iop->offset += count;
2585                        rv = count;
2586                } else {
2587                        /* try at least to recover the current attributes */
2588                        updateAttr(node, 1 /* force */);
2589                }
2590        }
2591
2592        return rv;
2593}
2594
2595static off_t nfs_dir_lseek(
2596        rtems_libio_t *iop,
2597        off_t          length,
2598        int            whence
2599)
2600{
2601        off_t rv = rtems_filesystem_default_lseek_directory(iop, length, whence);
2602
2603        if (rv == 0) {
2604                DirInfo di = iop->pathinfo.node_access_2;
2605                nfscookie *cookie = &di->readdirargs.cookie;
2606
2607                di->eofreached = FALSE;
2608
2609                /* rewind cookie */
2610                memset(cookie, 0, sizeof(*cookie));
2611        }
2612
2613        return rv;
2614}
2615
2616#if 0   /* structure types for reference */
2617struct fattr {
2618                ftype type;
2619                u_int mode;
2620                u_int nlink;
2621                u_int uid;
2622                u_int gid;
2623                u_int size;
2624                u_int blocksize;
2625                u_int rdev;
2626                u_int blocks;
2627                u_int fsid;
2628                u_int fileid;
2629                nfstime atime;
2630                nfstime mtime;
2631                nfstime ctime;
2632};
2633
2634struct  stat
2635{
2636                dev_t         st_dev;
2637                ino_t         st_ino;
2638                mode_t        st_mode;
2639                nlink_t       st_nlink;
2640                uid_t         st_uid;
2641                gid_t         st_gid;
2642                dev_t         st_rdev;
2643                off_t         st_size;
2644                /* SysV/sco doesn't have the rest... But Solaris, eabi does.  */
2645#if defined(__svr4__) && !defined(__PPC__) && !defined(__sun__)
2646                time_t        st_atime;
2647                time_t        st_mtime;
2648                time_t        st_ctime;
2649#else
2650                time_t        st_atime;
2651                long          st_spare1;
2652                time_t        st_mtime;
2653                long          st_spare2;
2654                time_t        st_ctime;
2655                long          st_spare3;
2656                long          st_blksize;
2657                long          st_blocks;
2658                long      st_spare4[2];
2659#endif
2660};
2661#endif
2662
2663/* common for file/dir/link */
2664static int nfs_fstat(
2665        const rtems_filesystem_location_info_t *loc,
2666        struct stat *buf
2667)
2668{
2669NfsNode node = loc->node_access;
2670fattr   *fa  = &SERP_ATTR(node);
2671
2672        if (updateAttr(node, 0 /* only if old */)) {
2673                return -1;
2674        }
2675
2676/* done by caller
2677        memset(buf, 0, sizeof(*buf));
2678 */
2679
2680        /* translate */
2681
2682        /* one of the branches hopefully is optimized away */
2683        if (sizeof(ino_t) < sizeof(u_int)) {
2684        buf->st_dev             = NFS_MAKE_DEV_T_INO_HACK((NfsNode)loc->node_access);
2685        } else {
2686        buf->st_dev             = NFS_MAKE_DEV_T((NfsNode)loc->node_access);
2687        }
2688        buf->st_mode    = fa->mode;
2689        buf->st_nlink   = fa->nlink;
2690        buf->st_uid             = fa->uid;
2691        buf->st_gid             = fa->gid;
2692        buf->st_size    = fa->size;
2693        /* Set to "preferred size" of this NFS client implementation */
2694        buf->st_blksize = nfsStBlksize ? nfsStBlksize : fa->blocksize;
2695        buf->st_rdev    = fa->rdev;
2696        buf->st_blocks  = fa->blocks;
2697        buf->st_ino     = fa->fileid;
2698        buf->st_atime   = fa->atime.seconds;
2699        buf->st_mtime   = fa->mtime.seconds;
2700        buf->st_ctime   = fa->ctime.seconds;
2701
2702#if 0 /* NFS should return the modes */
2703        switch(fa->type) {
2704                default:
2705                case NFNON:
2706                case NFBAD:
2707                                break;
2708
2709                case NFSOCK: buf->st_mode |= S_IFSOCK; break;
2710                case NFFIFO: buf->st_mode |= S_IFIFO;  break;
2711                case NFREG : buf->st_mode |= S_IFREG;  break;
2712                case NFDIR : buf->st_mode |= S_IFDIR;  break;
2713                case NFBLK : buf->st_mode |= S_IFBLK;  break;
2714                case NFCHR : buf->st_mode |= S_IFCHR;  break;
2715                case NFLNK : buf->st_mode |= S_IFLNK;  break;
2716        }
2717#endif
2718
2719        return 0;
2720}
2721
2722/* a helper which does the real work for
2723 * a couple of handlers (such as chmod,
2724 * ftruncate or utime)
2725 */
2726static int
2727nfs_sattr(NfsNode node, sattr *arg, u_long mask)
2728{
2729int rv;
2730struct timeval                          now;
2731nfstime                                 nfsnow, t;
2732u_int                                   mode;
2733
2734        if (updateAttr(node, 0 /* only if old */))
2735                return -1;
2736
2737        rtems_clock_get_tod_timeval(&now);
2738
2739        /* TODO: add rtems EPOCH - UNIX EPOCH seconds */
2740        nfsnow.seconds  = now.tv_sec;
2741        nfsnow.useconds = now.tv_usec;
2742
2743        /* merge permission bits into existing type bits */
2744        mode = SERP_ATTR(node).mode;
2745        if (mask & SATTR_MODE) {
2746                mode &= S_IFMT;
2747                mode |= arg->mode & ~S_IFMT;
2748        } else {
2749                mode = -1;
2750        }
2751        SERP_ARGS(node).sattrarg.attributes.mode  = mode;
2752
2753        SERP_ARGS(node).sattrarg.attributes.uid   =
2754                (mask & SATTR_UID)  ? arg->uid : -1;
2755
2756        SERP_ARGS(node).sattrarg.attributes.gid   =
2757                (mask & SATTR_GID)  ? arg->gid : -1;
2758
2759        SERP_ARGS(node).sattrarg.attributes.size  =
2760                (mask & SATTR_SIZE) ? arg->size : -1;
2761
2762        if (mask & SATTR_ATIME)
2763                t = arg->atime;
2764        else if (mask & SATTR_TOUCHA)
2765                t = nfsnow;
2766        else
2767                t.seconds = t.useconds = -1;
2768        SERP_ARGS(node).sattrarg.attributes.atime = t;
2769
2770        if (mask & SATTR_ATIME)
2771                t = arg->mtime;
2772        else if (mask & SATTR_TOUCHA)
2773                t = nfsnow;
2774        else
2775                t.seconds = t.useconds = -1;
2776        SERP_ARGS(node).sattrarg.attributes.mtime = t;
2777
2778        node->serporid.status = NFS_OK;
2779
2780        rv = nfscall(
2781                node->nfs->server,
2782                NFSPROC_SETATTR,
2783                (xdrproc_t)xdr_sattrargs, &SERP_FILE(node),
2784                (xdrproc_t)xdr_attrstat, &node->serporid
2785        );
2786
2787        if (rv == 0) {
2788                rv = nfsEvaluateStatus(node->serporid.status);
2789
2790                if (rv == 0) {
2791                        node->age = nowSeconds();
2792                } else {
2793#if DEBUG & DEBUG_SYSCALLS
2794                        fprintf(stderr,"nfs_sattr: %s\n",strerror(errno));
2795#endif
2796                        /* try at least to recover the current attributes */
2797                        updateAttr(node, 1 /* force */);
2798                }
2799        } else {
2800#if DEBUG & DEBUG_SYSCALLS
2801                fprintf(stderr,
2802                                "nfs_sattr (mask 0x%08x): %s",
2803                                mask,
2804                                strerror(errno));
2805#endif
2806        }
2807
2808        return rv;
2809}
2810
2811/* just set the size attribute to 'length'
2812 * the server will take care of the rest :-)
2813 */
2814static int nfs_file_ftruncate(
2815        rtems_libio_t *iop,
2816        off_t          length
2817)
2818{
2819sattr                                   arg;
2820
2821        if (length < 0) {
2822                errno = EINVAL;
2823                return -1;
2824        }
2825
2826        if ((uintmax_t) length > UINT32_MAX) {
2827                errno = EFBIG;
2828                return -1;
2829        }
2830
2831        arg.size = length;
2832        /* must not modify any other attribute; if we are not the owner
2833         * of the file or directory but only have write access changing
2834         * any attribute besides 'size' will fail...
2835         */
2836        return nfs_sattr(iop->pathinfo.node_access,
2837                                         &arg,
2838                                         SATTR_SIZE);
2839}
2840
2841/* the file handlers table */
2842static const
2843struct _rtems_filesystem_file_handlers_r nfs_file_file_handlers = {
2844        .open_h      = nfs_file_open,
2845        .close_h     = nfs_file_close,
2846        .read_h      = nfs_file_read,
2847        .write_h     = nfs_file_write,
2848        .ioctl_h     = rtems_filesystem_default_ioctl,
2849        .lseek_h     = rtems_filesystem_default_lseek_file,
2850        .fstat_h     = nfs_fstat,
2851        .ftruncate_h = nfs_file_ftruncate,
2852        .fsync_h     = rtems_filesystem_default_fsync_or_fdatasync,
2853        .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
2854        .fcntl_h     = rtems_filesystem_default_fcntl,
2855        .kqfilter_h  = rtems_filesystem_default_kqfilter,
2856        .mmap_h      = rtems_filesystem_default_mmap,
2857        .poll_h      = rtems_filesystem_default_poll,
2858        .readv_h     = rtems_filesystem_default_readv,
2859        .writev_h    = rtems_filesystem_default_writev
2860};
2861
2862/* the directory handlers table */
2863static const
2864struct _rtems_filesystem_file_handlers_r nfs_dir_file_handlers = {
2865        .open_h      = nfs_dir_open,
2866        .close_h     = nfs_dir_close,
2867        .read_h      = nfs_dir_read,
2868        .write_h     = rtems_filesystem_default_write,
2869        .ioctl_h     = rtems_filesystem_default_ioctl,
2870        .lseek_h     = nfs_dir_lseek,
2871        .fstat_h     = nfs_fstat,
2872        .ftruncate_h = rtems_filesystem_default_ftruncate_directory,
2873        .fsync_h     = rtems_filesystem_default_fsync_or_fdatasync,
2874        .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
2875        .fcntl_h     = rtems_filesystem_default_fcntl,
2876        .kqfilter_h  = rtems_filesystem_default_kqfilter,
2877        .mmap_h      = rtems_filesystem_default_mmap,
2878        .poll_h      = rtems_filesystem_default_poll,
2879        .readv_h     = rtems_filesystem_default_readv,
2880        .writev_h    = rtems_filesystem_default_writev
2881};
2882
2883/* the link handlers table */
2884static const
2885struct _rtems_filesystem_file_handlers_r nfs_link_file_handlers = {
2886        .open_h      = rtems_filesystem_default_open,
2887        .close_h     = rtems_filesystem_default_close,
2888        .read_h      = rtems_filesystem_default_read,
2889        .write_h     = rtems_filesystem_default_write,
2890        .ioctl_h     = rtems_filesystem_default_ioctl,
2891        .lseek_h     = rtems_filesystem_default_lseek,
2892        .fstat_h     = nfs_fstat,
2893        .ftruncate_h = rtems_filesystem_default_ftruncate,
2894        .fsync_h     = rtems_filesystem_default_fsync_or_fdatasync,
2895        .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
2896        .fcntl_h     = rtems_filesystem_default_fcntl,
2897        .kqfilter_h  = rtems_filesystem_default_kqfilter,
2898        .mmap_h      = rtems_filesystem_default_mmap,
2899        .poll_h      = rtems_filesystem_default_poll,
2900        .readv_h     = rtems_filesystem_default_readv,
2901        .writev_h    = rtems_filesystem_default_writev
2902};
2903
2904/* we need a dummy driver entry table to get a
2905 * major number from the system
2906 */
2907static
2908rtems_device_driver nfs_initialize(
2909                rtems_device_major_number       major,
2910                rtems_device_minor_number       minor,
2911                void                                            *arg
2912)
2913{
2914        /* we don't really use this routine because
2915     * we cannot supply an argument (contrary
2916     * to what the 'arg' parameter suggests - it
2917     * is always set to 0 by the generics :-()
2918     * and because we don't want the user to
2919     * have to deal with the major number (which
2920     * OTOH is something WE are interested in. The
2921     * only reason for using this API was getting
2922     * a major number, after all).
2923     *
2924         * Something must be present, however, to
2925         * reserve a slot in the driver table.
2926         */
2927        return RTEMS_SUCCESSFUL;
2928}
2929
2930static rtems_driver_address_table       drvNfs = {
2931                nfs_initialize,
2932                0,                                      /* open    */
2933                0,                                      /* close   */
2934                0,                                      /* read    */
2935                0,                                      /* write   */
2936                0                                       /* control */
2937};
2938
2939/* Dump a list of the currently mounted NFS to a  file */
2940int
2941nfsMountsShow(FILE *f)
2942{
2943char    *mntpt = 0;
2944Nfs             nfs;
2945
2946        if (!f)
2947                f = stdout;
2948
2949        if ( !(mntpt=malloc(MAXPATHLEN)) ) {
2950                fprintf(stderr,"nfsMountsShow(): no memory\n");
2951                return -1;
2952        }
2953
2954        fprintf(f,"Currently Mounted NFS:\n");
2955
2956        LOCK(nfsGlob.llock);
2957
2958        for (nfs = nfsGlob.mounted_fs; nfs; nfs=nfs->next) {
2959                fprintf(f,"%s on ", nfs->mt_entry->dev);
2960                if (rtems_filesystem_resolve_location(mntpt, MAXPATHLEN, &nfs->mt_entry->mt_fs_root->location))
2961                        fprintf(f,"<UNABLE TO LOOKUP MOUNTPOINT>\n");
2962                else
2963                        fprintf(f,"%s\n",mntpt);
2964        }
2965
2966        UNLOCK(nfsGlob.llock);
2967
2968        free(mntpt);
2969        return 0;
2970}
2971
2972#if 0
2973CCJ_REMOVE_MOUNT
2974/* convenience wrapper
2975 *
2976 * NOTE: this routine calls NON-REENTRANT
2977 *       gethostbyname() if the host is
2978 *       not in 'dot' notation.
2979 */
2980int
2981nfsMount(char *uidhost, char *path, char *mntpoint)
2982{
2983struct stat                                                             st;
2984int                                                                             devl;
2985char                                                                    *host;
2986int                                                                             rval = -1;
2987char                                                                    *dev =  0;
2988
2989        if (!uidhost || !path || !mntpoint) {
2990                fprintf(stderr,"usage: nfsMount(""[uid.gid@]host"",""path"",""mountpoint"")\n");
2991                nfsMountsShow(stderr);
2992                return -1;
2993        }
2994
2995        if ( !(dev = malloc((devl=strlen(uidhost) + 20 + strlen(path)+1))) ) {
2996                fprintf(stderr,"nfsMount: out of memory\n");
2997                return -1;
2998        }
2999
3000        /* Try to create the mount point if nonexistent */
3001        if (stat(mntpoint, &st)) {
3002                if (ENOENT != errno) {
3003                        perror("nfsMount trying to create mount point - stat failed");
3004                        goto cleanup;
3005                } else if (mkdir(mntpoint,0777)) {
3006                        perror("nfsMount trying to create mount point");
3007                        goto cleanup;
3008                }
3009        }
3010
3011        if ( !(host=strchr(uidhost,UIDSEP)) ) {
3012                host = uidhost;
3013        } else {
3014                host++;
3015        }
3016
3017        if (isdigit((unsigned char)*host)) {
3018                /* avoid using gethostbyname */
3019                sprintf(dev,"%s:%s",uidhost,path);
3020        } else {
3021                struct hostent *h;
3022
3023                /* copy the uid part (hostname will be
3024                 * overwritten)
3025                 */
3026                strcpy(dev, uidhost);
3027
3028                /* NOTE NOTE NOTE: gethostbyname is NOT
3029                 * thread safe. This is UGLY
3030                 */
3031
3032/* BEGIN OF NON-THREAD SAFE REGION */
3033
3034                h = gethostbyname(host);
3035
3036                if ( !h ||
3037                         !inet_ntop( AF_INET,
3038                                             (struct in_addr*)h->h_addr_list[0],
3039                                                 dev  + (host - uidhost),
3040                                                 devl - (host - uidhost) )
3041                        ) {
3042                        fprintf(stderr,"nfsMount: host '%s' not found\n",host);
3043                        goto cleanup;
3044                }
3045
3046/* END OF NON-THREAD SAFE REGION */
3047
3048                /* append ':<path>' */
3049                strcat(dev,":");
3050                strcat(dev,path);
3051        }
3052
3053        printf("Trying to mount %s on %s\n",dev,mntpoint);
3054
3055        if (mount(dev,
3056                          mntpoint,
3057                          "nfs",
3058                          RTEMS_FILESYSTEM_READ_WRITE,
3059                          NULL)) {
3060                perror("nfsMount - mount");
3061                goto cleanup;
3062        }
3063
3064        rval = 0;
3065
3066cleanup:
3067        free(dev);
3068        return rval;
3069}
3070#endif
3071
3072/* HERE COMES A REALLY UGLY HACK */
3073
3074/* This is stupid; it is _very_ hard to find the path
3075 * leading to a rtems_filesystem_location_info_t node :-(
3076 * The only easy way is making the location the current
3077 * directory and issue a getcwd().
3078 * However, since we don't want to tamper with the
3079 * current directory, we must create a separate
3080 * task to do the job for us - sigh.
3081 */
3082
3083typedef struct ResolvePathArgRec_ {
3084        rtems_filesystem_location_info_t        *loc;   /* IN: location to resolve      */
3085        char                                                            *buf;   /* IN/OUT: buffer where to put the path */
3086        int                                                                     len;    /* IN: buffer length            */
3087        rtems_binary_semaphore                  sync;   /* IN: synchronization          */
3088        rtems_status_code                                       status; /* OUT: result                          */
3089} ResolvePathArgRec, *ResolvePathArg;
3090
3091static void
3092resolve_path(rtems_task_argument arg)
3093{
3094ResolvePathArg                                          rpa = (ResolvePathArg)arg;
3095rtems_filesystem_location_info_t        old;
3096
3097        /* IMPORTANT: let the helper task have its own libio environment (i.e. cwd) */
3098        if (RTEMS_SUCCESSFUL == (rpa->status = rtems_libio_set_private_env())) {
3099
3100                old = rtems_filesystem_current->location;
3101
3102                rtems_filesystem_current->location = *(rpa->loc);
3103
3104                if ( !getcwd(rpa->buf, rpa->len) )
3105                        rpa->status = RTEMS_UNSATISFIED;
3106
3107                /* must restore the cwd because 'freenode' will be called on it */
3108                rtems_filesystem_current->location = old;
3109        }
3110        rtems_binary_semaphore_post(&rpa->sync);
3111        rtems_task_exit();
3112}
3113
3114
3115/* a utility routine to find the path leading to a
3116 * rtems_filesystem_location_info_t node
3117 *
3118 * INPUT: 'loc' and a buffer 'buf' (length 'len') to hold the
3119 *        path.
3120 * OUTPUT: path copied into 'buf'
3121 *
3122 * RETURNS: 0 on success, RTEMS error code on error.
3123 */
3124rtems_status_code
3125rtems_filesystem_resolve_location(char *buf, int len, rtems_filesystem_location_info_t *loc)
3126{
3127ResolvePathArgRec       arg;
3128rtems_id                        tid = 0;
3129rtems_task_priority     pri;
3130rtems_status_code       status;
3131
3132        arg.loc  = loc;
3133        arg.buf  = buf;
3134        arg.len  = len;
3135
3136        rtems_binary_semaphore_init(&arg.sync, "NFSress");
3137
3138        rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &pri);
3139
3140        status = rtems_task_create(
3141                                        rtems_build_name('r','e','s','s'),
3142                                        pri,
3143                                        RTEMS_MINIMUM_STACK_SIZE + 50000,
3144                                        RTEMS_DEFAULT_MODES,
3145                                        RTEMS_DEFAULT_ATTRIBUTES,
3146                                        &tid);
3147
3148        if (RTEMS_SUCCESSFUL != status)
3149                goto cleanup;
3150
3151        status = rtems_task_start(tid, resolve_path, (rtems_task_argument)&arg);
3152
3153        if (RTEMS_SUCCESSFUL != status) {
3154                rtems_task_delete(tid);
3155                goto cleanup;
3156        }
3157
3158
3159        /* synchronize with the helper task */
3160        rtems_binary_semaphore_wait(&arg.sync);
3161
3162        status = arg.status;
3163
3164cleanup:
3165        rtems_binary_semaphore_destroy(&arg.sync);
3166
3167        return status;
3168}
3169
3170int
3171nfsSetTimeout(uint32_t timeout_ms)
3172{
3173rtems_interrupt_lock_context lock_context;
3174uint32_t                  s,us;
3175
3176        if ( timeout_ms > 100000 ) {
3177                /* out of range */
3178                return -1;
3179        }
3180
3181        s  = timeout_ms/1000;
3182        us = (timeout_ms % 1000) * 1000;
3183
3184        NFS_GLOBAL_ACQUIRE(&lock_context);
3185        _nfscalltimeout.tv_sec  = s;
3186        _nfscalltimeout.tv_usec = us;
3187        NFS_GLOBAL_RELEASE(&lock_context);
3188
3189        return 0;
3190}
3191
3192uint32_t
3193nfsGetTimeout( void )
3194{
3195rtems_interrupt_lock_context lock_context;
3196uint32_t              s,us;
3197        NFS_GLOBAL_ACQUIRE(&lock_context);
3198        s  = _nfscalltimeout.tv_sec;
3199        us = _nfscalltimeout.tv_usec;
3200        NFS_GLOBAL_RELEASE(&lock_context);
3201        return s*1000 + us/1000;
3202}
Note: See TracBrowser for help on using the repository browser.