source: rtems/cpukit/libfs/src/nfsclient/src/nfs.c @ 2f68778

4.115
Last change on this file since 2f68778 was 2f68778, checked in by Sebastian Huber <sebastian.huber@…>, on 12/16/13 at 12:44:13

Filesystem: Add readv/writev handlers

The readv() and writev() support was implemented in terms of multiple
calls to the read and write handlers. This imposes a problem on device
files which use an IO vector as single request entity. For example a
low-level network device (e.g. BPF(4)) may use an IO vector to create
one frame from multiple protocol layers each with its own IO vector
entry.

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