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

4.104.115
Last change on this file since 7baa484 was 7baa484, checked in by Chris Johns <chrisj@…>, on 06/12/09 at 01:53:33

2009-06-12 Chris Johns <chrisj@…>

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