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

4.104.114.95
Last change on this file since 9cf3432c was 9cf3432c, checked in by Till Straumann <strauman@…>, on 05/30/08 at 23:21:06

2008-05-30 Till Straumann <strauman@…>

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