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

Last change on this file since a7d1992c was da154e14, checked in by Sebastian Huber <sebastian.huber@…>, on 05/14/12 at 14:55:41

Filesystem: Move operations to mount table entry

The scope of the file system operations is the file system instance.
The scope of the file system node handlers is the file location. The
benefit of moving the operations to the mount table entry is a size
reduction of the file location (rtems_filesystem_location_info_t). The
code size is slightly increased due to additional load instructions.

Restructure rtems_filesystem_mount_table_entry_t to improve cache
efficiency.

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