source: rtems-libbsd/rtemsbsd/nfsclient/nfs.c @ 58c4e1c5

55-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since 58c4e1c5 was 58c4e1c5, checked in by Sebastian Huber <sebastian.huber@…>, on 06/10/16 at 12:11:40

nfsclient: Port to LibBSD

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