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

4.104.115
Last change on this file since 55d883d was 29e92b0, checked in by Chris Johns <chrisj@…>, on 05/31/10 at 13:56:37

2010-05-31 Chris Johns <chrisj@…>

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