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

4.115
Last change on this file since 7666afc was 7666afc, checked in by Sebastian Huber <sebastian.huber@…>, on 05/14/12 at 14:53:49

Filesystem: Add const qualifier to lock/unlock

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