source: rtems/cpukit/libfs/src/nfsclient/src/nfs.c @ 1cfe012

4.115
Last change on this file since 1cfe012 was 1cfe012, checked in by Sebastian Huber <sebastian.huber@…>, on 06/19/15 at 12:24:27

nfsclient: Use an interrupt lock

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