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

4.115
Last change on this file since 183af89 was 30d4124, checked in by Sebastian Huber <sebastian.huber@…>, on 05/07/12 at 14:30:37

Filesystem: PR1398: Fix lseek() mechanic

According to POSIX the lseek() function shall not, by itself, extend the
size of a file.

Remove the size field of rtems_libio_t. A file has only one size but
may have multiple open file descriptors. Thus a file size field in the
file descriptor may lead to inconsistencies.

New default handlers rtems_filesystem_default_lseek_file() and
rtems_filesystem_default_lseek_directory().

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