diff packages/fs/ram/current/src/ramfs.c @ 115:6ed91473a1cd ecos-sw-2000-08-21

Merge from eCos master repository on 2000-08-21-22:40:54-BST
author jlarmour
date Fri, 25 Aug 2000 17:32:38 +0000
parents
children 38892b97b685
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/fs/ram/current/src/ramfs.c
@@ -0,0 +1,2386 @@
+//==========================================================================
+//
+//      ramfs.c
+//
+//      RAM file system
+//
+//==========================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// -------------------------------------------                              
+// The contents of this file are subject to the Red Hat eCos Public License 
+// Version 1.1 (the "License"); you may not use this file except in         
+// compliance with the License.  You may obtain a copy of the License at    
+// http://www.redhat.com/                                                   
+//                                                                          
+// Software distributed under the License is distributed on an "AS IS"      
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
+// License for the specific language governing rights and limitations under 
+// the License.                                                             
+//                                                                          
+// The Original Code is eCos - Embedded Configurable Operating System,      
+// released September 30, 1998.                                             
+//                                                                          
+// The Initial Developer of the Original Code is Red Hat.                   
+// Portions created by Red Hat are                                          
+// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
+// All Rights Reserved.                                                     
+// -------------------------------------------                              
+//                                                                          
+//####COPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):           nickg
+// Contributors:        nickg
+// Date:                2000-07-25
+// Purpose:             RAM file system
+// Description:         This is a RAM filesystem for eCos. It attempts to
+//                      provide full POSIX-compatible filesystem behaviour
+//                      while at the same time being efficient in terms of
+//                      time and space used.
+//                      
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+// 
+// General Description
+// ===================
+// 
+// This is an implementation of a RAM filesystem for eCos. Its goal is
+// to provide a working example of a filesystem that provides most of
+// the required POSIX functionality. And obviously it may also be
+// useful in its own right.
+//
+//
+// Nodes
+// -----
+//
+// All files and directories are represented by node objects. Each
+// ramfs_node structure contains the following fields:
+//
+// mode   - Node type, file or directory.
+// refcnt - Number of references to this node. For files each open counts as
+//          a reference and for directories a node is referenced when it is made
+//          current directory, or is opened for reading.
+// nlink  - Number of links to this node. Each directory entry that references
+//          this node is a link. 
+// size   - Size of the data in this node in bytes.
+// atime  - Last time this node was accessed.
+// mtime  - Last time the data in this node was modified.
+// ctime  - Last time the status information in this node was changed.
+//
+// The data storage in a node is controlled by the configuration and
+// can take two forms. These will be described later.
+//          
+// Directories
+// -----------
+//
+// A directory is a node whose data is a list of directory entries. To
+// simplify management of these, long directory entries are split into
+// a chain of fixed size ramfs_dirent structures. These contain the
+// following fields:
+//
+// node    - Pointer to node referenced by this entry. This is present in
+//           every directory entry fragment
+// inuse   - Set to 1 if this entry is in use, zero if it is free.
+// first   - Set to 1 if this is the first fragment of a directory entry.
+// last    - Set to 1 if this is the last fragment of a directory entry.
+// namelen - The size of the whole file name.
+// fraglen - The number of bytes of the file name that are stored in this
+//           fragment.
+// next    - The offset of the next fragment of this directory entry.
+// name    - The characters of the fragment of the file name stored in this
+//           entry.
+//
+// Small file names are stored in a single fragment. Longer names are
+// stored in a chain of fragments.
+//
+// Data Storage
+// ------------
+//
+// Two data storage mechanisms may be configured, the SIMPLE and the
+// BLOCKS mechanisms.
+//
+// SIMPLE Data Storage
+// ~~~~~~~~~~~~~~~~~~~
+//
+// This mechanism simply uses malloc() and free() to allocate the
+// memory for both nodes and file data. File data is stored in a
+// single malloced vector that is realloced as necessary to acquire
+// more space. 
+//
+// The advantage of this approach is that the RAM filesystem only uses
+// as much memory as it needs, the rest is available for use by other
+// components. It also requires much simpler data structures and code
+// in the filesystem to manage. However, if any files get to be a
+// significant proportion of the size of the heap, there is the danger
+// that fragmentation will prevent any further extension of some
+// files, even if there is enough memory in total. It also requires an
+// implementation of malloc() to be present. If this needs to be
+// present for other components,then this is not a significant
+// overhead, but including it just for use by this filesystem
+// represents a major addition of code and data structures.
+//
+//
+// BLOCKS Data Storage
+// ~~~~~~~~~~~~~~~~~~~
+//
+// This mechanism divides the memory used for file storage into fixed
+// sized blocks. These blocks may either be allocated using
+// malloc()/free(), or may be obtained from a array of blocks reserved
+// for the purpose. Configuration allows the block size to be
+// selected, as well as the allocation mechanism, and in the case of a
+// block array, whether it is defined here or by an external
+// component.
+// 
+// Data storage in nodes is arranges in three arrays of pointers to
+// blocks. The first array points directly to data blocks, the second
+// to blocks which themselves contain pointers to data blocks, and the
+// third to blocks which contain pointers to blocks which contain
+// pointers to data blocks. In the default configuration These last
+// two arrays have only one element each.
+// 
+// The following shows how the data is arranged in a fully populated
+// file with a 256 byte block size using the default configuration.
+// 
+//      Node
+// ~            ~
+// |            |
+// |            |
+// +------------+
+// |     *------+--------> data block 0
+// +------------+
+// |     *------+--------> data block 1
+// +------------+
+// |     *------+--------> data block 2
+// +------------+
+// |     *------+--------> data block 3
+// +------------+
+// |     *------+--------> data block 4
+// +------------+
+// |     *------+--------> data block 5
+// +------------+
+// |     *------+--------> data block 6
+// +------------+
+// |     *------+--------> data block 7
+// +------------+
+// |     *------+--------> +------------+
+// +------------+          |     *------+--------> data block 8
+// |     *------+----+     +------------+
+// +------------+    |     |            |
+//                   |     ~            ~
+//                   |     |            |
+//                   |     +------------+
+//                   |     |     *------+--------> data block 71
+//                   |     +------------+
+//                   |     
+//                   +---->+------------+         +------------+
+//                         |     *------+-------->|     *------+---->data block 72
+//                         +------------+         +------------+
+//                         |            |         |            |
+//                         ~            ~         ~            ~
+//                         |            |         |            |
+//                         +------------+         +------------+
+//                         |     *------+---+     |     *------+----> data block 135
+//                         +------------+   |     +------------+
+//                                          |
+//                                          |     +------------+
+//                                          +---->|     *------+----> data block 4104
+//                                                +------------+
+//                                                |            |
+//                                                ~            ~
+//                                                |            |
+//                                                +------------+
+//                                                |     *------+----> data block 4167
+//                                                +------------+
+// 
+// 
+//
+// The advantages of this approach are that, first, memory usage is
+// divided into discreet fixed size blocks which are easier to
+// manage. When using malloc() to allocate them, they will fit into
+// any free memory of at least the right size. Using the block array
+// option removes the need to have a malloc() implementation at all.
+//
+// The disadvantages of this mechanism are that, first, when using
+// malloc() to allocate blocks, the per-block memory allocator
+// overhead is paid for each block, rather than per file. This may
+// result in less memory overall being available for data
+// storage. When using the block array, it is permanently reserved for
+// use by the ram filesystem, and is not available for use by other
+// components.
+//
+//==========================================================================
+
+#include <pkgconf/system.h>
+#include <pkgconf/hal.h>
+#include <pkgconf/kernel.h>
+#include <pkgconf/io_fileio.h>
+#include <pkgconf/fs_ram.h>
+
+#include <cyg/kernel/ktypes.h>         // base kernel types
+#include <cyg/infra/cyg_trac.h>        // tracing macros
+#include <cyg/infra/cyg_ass.h>         // assertion macros
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <cyg/fileio/fileio.h>
+
+#include <cyg/kernel/kapi.h>
+#include <cyg/infra/diag.h>
+
+//==========================================================================
+// Sizes derived from configuration
+
+// -------------------------------------------------------------------------
+// Simple malloc based allocator parameters
+
+#ifdef CYGPKG_FS_RAM_SIMPLE
+
+#define RAMFS_FILESIZE_MAX      UINT_MAX
+
+#else
+
+// -------------------------------------------------------------------------
+// Block allocator parameters
+
+// The number of nodes per block
+#define RAMFS_NODES_PER_BLOCK (CYGNUM_RAMFS_BLOCK_SIZE/sizeof(ramfs_node))
+
+// The number of indirect pointers that can be stored in a single data block
+#define RAMFS_INDIRECT_PER_BLOCK (CYGNUM_RAMFS_BLOCK_SIZE/sizeof(ramfs_block *))
+
+// The number of directory entries that can be stored in a single data block
+#define RAMFS_DIRENT_PER_BLOCK  (CYGNUM_RAMFS_BLOCK_SIZE/sizeof(ramfs_dirent))
+
+// Number of bytes contained in a one level indirect block
+#define RAMFS_INDIRECT1_BLOCK_EXTENT (RAMFS_INDIRECT_PER_BLOCK* \
+                                      CYGNUM_RAMFS_BLOCK_SIZE)
+
+// number of bytes contained in a two level indirect block
+#define RAMFS_INDIRECT2_BLOCK_EXTENT (RAMFS_INDIRECT_PER_BLOCK* \
+                                      RAMFS_INDIRECT_PER_BLOCK* \
+                                      CYGNUM_RAMFS_BLOCK_SIZE)
+
+// The maximum data offset for data directly accessed from the node
+#define RAMFS_DIRECT_MAX        (CYGNUM_RAMFS_BLOCKS_DIRECT*CYGNUM_RAMFS_BLOCK_SIZE)
+
+// The maximum data offset for data accessed from the single level indirect blocks
+#define RAMFS_INDIRECT1_MAX     (RAMFS_DIRECT_MAX+                      \
+                                 (CYGNUM_RAMFS_BLOCKS_INDIRECT1*        \
+                                  RAMFS_INDIRECT1_BLOCK_EXTENT))
+
+// The maximum data offset for data accessed from the two level indirect blocks
+#define RAMFS_INDIRECT2_MAX     (RAMFS_INDIRECT1_MAX+                   \
+                                 (CYGNUM_RAMFS_BLOCKS_INDIRECT2*        \
+                                  RAMFS_INDIRECT2_BLOCK_EXTENT))
+
+// The maximum size of a file
+#define RAMFS_FILESIZE_MAX      RAMFS_INDIRECT2_MAX
+
+#endif
+
+//==========================================================================
+// Forward definitions
+
+// Filesystem operations
+static int ramfs_mount    ( cyg_fstab_entry *fste, cyg_mtab_entry *mte );
+static int ramfs_umount   ( cyg_mtab_entry *mte );
+static int ramfs_open     ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             int mode,  cyg_file *fte );
+static int ramfs_unlink   ( cyg_mtab_entry *mte, cyg_dir dir, const char *name );
+static int ramfs_mkdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *name );
+static int ramfs_rmdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *name );
+static int ramfs_rename   ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
+                             cyg_dir dir2, const char *name2 );
+static int ramfs_link     ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
+                             cyg_dir dir2, const char *name2, int type );
+static int ramfs_opendir  ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             cyg_file *fte );
+static int ramfs_chdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             cyg_dir *dir_out );
+static int ramfs_stat     ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             struct stat *buf);
+static int ramfs_getinfo  ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             int key, void *buf, int len );
+static int ramfs_setinfo  ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             int key, void *buf, int len );
+
+// File operations
+static int ramfs_fo_read      (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
+static int ramfs_fo_write     (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
+static int ramfs_fo_lseek     (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
+static int ramfs_fo_ioctl     (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
+                                CYG_ADDRWORD data);
+static int ramfs_fo_fsync     (struct CYG_FILE_TAG *fp, int mode );        
+static int ramfs_fo_close     (struct CYG_FILE_TAG *fp);
+static int ramfs_fo_fstat     (struct CYG_FILE_TAG *fp, struct stat *buf );
+static int ramfs_fo_getinfo   (struct CYG_FILE_TAG *fp, int key, void *buf, int len );
+static int ramfs_fo_setinfo   (struct CYG_FILE_TAG *fp, int key, void *buf, int len );
+
+// Directory operations
+static int ramfs_fo_dirread      (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
+static int ramfs_fo_dirlseek     (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
+
+
+//==========================================================================
+// Filesystem table entries
+
+// -------------------------------------------------------------------------
+// Fstab entry.
+// This defines the entry in the filesystem table.
+// For simplicity we use _FILESYSTEM synchronization for all accesses since
+// we should never block in any filesystem operations.
+
+FSTAB_ENTRY( ramfs_fste, "ramfs", 0,
+             CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM,
+             ramfs_mount,
+             ramfs_umount,
+             ramfs_open,
+             ramfs_unlink,
+             ramfs_mkdir,
+             ramfs_rmdir,
+             ramfs_rename,
+             ramfs_link,
+             ramfs_opendir,
+             ramfs_chdir,
+             ramfs_stat,
+             ramfs_getinfo,
+             ramfs_setinfo);
+
+// -------------------------------------------------------------------------
+// File operations.
+// This set of file operations are used for normal open files.
+
+static cyg_fileops ramfs_fileops =
+{
+    ramfs_fo_read,
+    ramfs_fo_write,
+    ramfs_fo_lseek,
+    ramfs_fo_ioctl,
+    cyg_fileio_seltrue,
+    ramfs_fo_fsync,
+    ramfs_fo_close,
+    ramfs_fo_fstat,
+    ramfs_fo_getinfo,
+    ramfs_fo_setinfo
+};
+
+// -------------------------------------------------------------------------
+// Directory file operations.
+// This set of operations are used for open directories. Most entries
+// point to error-returning stub functions. Only the read, lseek and
+// close entries are functional.
+
+static cyg_fileops ramfs_dirops =
+{
+    ramfs_fo_dirread,
+    (cyg_fileop_write *)cyg_fileio_enosys,
+    ramfs_fo_dirlseek,
+    (cyg_fileop_ioctl *)cyg_fileio_enosys,
+    cyg_fileio_seltrue,
+    (cyg_fileop_fsync *)cyg_fileio_enosys,
+    ramfs_fo_close,
+    (cyg_fileop_fstat *)cyg_fileio_enosys,
+    (cyg_fileop_getinfo *)cyg_fileio_enosys,
+    (cyg_fileop_setinfo *)cyg_fileio_enosys
+};
+
+//==========================================================================
+// Data typedefs
+// Some forward typedefs for the main data structures.
+
+struct ramfs_node;
+typedef struct ramfs_node ramfs_node;
+
+struct ramfs_dirent;
+typedef struct ramfs_dirent ramfs_dirent;
+
+#ifndef CYGPKG_FS_RAM_SIMPLE
+
+typedef cyg_uint8 ramfs_block[CYGNUM_RAMFS_BLOCK_SIZE];
+
+#endif
+
+//==========================================================================
+// File and directory node
+// This data structure represents a file or directory.
+
+struct ramfs_node
+{
+    mode_t              mode;           // node type
+    cyg_ucount32        refcnt;         // open file/current dir references
+    nlink_t             nlink;          // number of links to this node
+    size_t              size;           // size of file in bytes
+    time_t              atime;          // last access time
+    time_t              mtime;          // last modified time
+    time_t              ctime;          // last changed status time
+
+#ifdef CYGPKG_FS_RAM_SIMPLE
+
+    // The data storage in this case consists of a single
+    // malloced memory block, together with its size.
+    
+    size_t              datasize;       // size of data block
+    cyg_uint8           *data;          // malloced data buffer
+
+#else
+
+    // The data storage in this case consists of arrays of pointers
+    // to data blocks. 
+    
+#if CYGNUM_RAMFS_BLOCKS_DIRECT > 0
+    // Directly accessible blocks from the inode.
+    ramfs_block         *direct[CYGNUM_RAMFS_BLOCKS_DIRECT];
+#endif
+#if  CYGNUM_RAMFS_BLOCKS_INDIRECT1 > 0
+    // Single level indirection
+    ramfs_block         **indirect1[CYGNUM_RAMFS_BLOCKS_INDIRECT1];
+#endif
+#if  CYGNUM_RAMFS_BLOCKS_INDIRECT2 > 0
+    // Two level indirection
+    ramfs_block         ***indirect2[CYGNUM_RAMFS_BLOCKS_INDIRECT2];
+#endif
+
+#endif
+    
+};
+
+//==========================================================================
+// Directory entry.
+// Fixed sized entry containing a fragment of the name of a file/directory.
+
+struct ramfs_dirent
+{
+    ramfs_node          *node;          // pointer to node
+    unsigned int        inuse:1,        // entry in use?
+                        first:1,        // first directory entry fragment?
+                        last:1,         // last directory entry fragment?
+                        namelen:8,      // bytes in whole name
+                        fraglen:8;      // bytes in name fragment
+    off_t               next;           // offset of next dirent
+
+    // Name fragment, fills rest of entry.
+    char                name[CYGNUM_RAMFS_DIRENT_SIZE-
+                             sizeof(ramfs_node *)-
+                             sizeof( cyg_uint32)-
+                             sizeof(off_t)];
+};
+
+//==========================================================================
+// Directory search data
+// Parameters for a directory search. The fields of this structure are
+// updated as we follow a pathname through the directory tree.
+
+struct ramfs_dirsearch
+{
+    ramfs_node          *dir;           // directory to search
+    const char          *path;          // path to follow
+    ramfs_node          *node;          // Node found
+    const char          *name;          // last name fragment used
+    int                 namelen;        // name fragment length
+    cyg_bool            last;           // last name in path?
+};
+
+typedef struct ramfs_dirsearch ramfs_dirsearch;
+
+//==========================================================================
+// Forward defs
+
+static int del_direntry( ramfs_node *dir, const char *name, int namelen );
+
+
+//==========================================================================
+// Block array
+// This is used for block allocation when malloc is not being used.
+
+#ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY
+
+# ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY_EXTERN
+
+// Array is defined externally with a user-supplied name
+
+__externC ramfs_block CYGPKG_FS_RAM_BLOCKS_ARRAY_NAME[CYGNUM_FS_RAM_BLOCKS_ARRAY_SIZE];
+
+// Translate into a usable common name
+#define ramfs_block_array CYGPKG_FS_RAM_BLOCKS_ARRAY_NAME
+
+# else
+
+// Array is defined here
+
+static ramfs_block cyg_ramfs_block_array[CYGNUM_FS_RAM_BLOCKS_ARRAY_SIZE];
+
+#define ramfs_block_array cyg_ramfs_block_array
+
+# endif
+
+// Pointer to list of free blocks
+static ramfs_block *block_free_list = NULL;
+
+#endif
+
+//==========================================================================
+// Block allocation
+
+#ifndef CYGPKG_FS_RAM_SIMPLE
+
+// -------------------------------------------------------------------------
+// block_init()
+// Initialize the block allocator by chaining them all together on
+// block_free_list.
+
+#ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY
+
+static void block_init()
+{
+    static cyg_bool initialized = false;
+    int i;
+
+    if( !initialized )
+    {
+        for( i = 0; i < CYGNUM_FS_RAM_BLOCKS_ARRAY_SIZE; i++ )
+        {
+            ramfs_block *b = &ramfs_block_array[i];
+            *(ramfs_block **)b = block_free_list;
+            block_free_list = b;
+        }
+        initialized = true;
+    }
+}
+
+#endif
+
+// -------------------------------------------------------------------------
+// block_alloc()
+// Allocate a block for data storage.
+// If we have a block array, just pick the first off the free list.
+// If we are mallocing, call malloc() to get it.
+
+static ramfs_block *block_alloc(void)
+{
+    ramfs_block *b;
+
+#ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY
+
+    block_init();       // Check blocks are initialized
+
+    // pick first block off free list.
+    b = block_free_list;
+
+    // and advance list
+    if( b != NULL )
+        block_free_list = *(ramfs_block **)b;
+
+#else
+
+    b = malloc(CYGNUM_RAMFS_BLOCK_SIZE);
+
+#endif
+
+    // Clear the block to zero if it was allocated
+    if( b != NULL )
+        memset( b, 0, CYGNUM_RAMFS_BLOCK_SIZE );
+
+    return b;
+
+}
+
+// -------------------------------------------------------------------------
+// block_free()
+// Free a block. Depending on the configuration send it back to the
+// heap or put it back on free list.
+
+static void block_free( ramfs_block *b )
+{
+#ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY
+
+    // Put the block back on the free list
+    
+    *(ramfs_block **)b = block_free_list;
+    block_free_list = b;
+    
+#else    
+
+    // Call free() to return it to the memory pool
+    
+    free( b );
+
+#endif
+}
+
+#endif
+
+//==========================================================================
+// Node buffer management
+// There are two versions of this, one for the _SIMPLE variant and one for
+// the _BLOCKS variant. In both cases the interface to this code is via the
+// findbuffer_node() and freebuffer_node() functions.
+
+#ifdef CYGPKG_FS_RAM_SIMPLE
+
+//==========================================================================
+// SIMPLE buffer management.
+// Each node has a data buffer pointer and a size. This buffer is
+// realloc()ed as needed.
+
+// -------------------------------------------------------------------------
+// findbuffer_node()
+// return a pointer to the data at the indicated file position, extending
+// the buffer if required.
+
+static int findbuffer_node( ramfs_node  *node,  // node pointer
+                            off_t pos,          // data position to get
+                            cyg_uint8 **buffer, // returned buffer pointer
+                            size_t *size,       // returned buffer size
+                            cyg_bool alloc)     // extend allocation?
+{
+    if( alloc && (pos == node->datasize || node->datasize == 0) )
+    {
+        // If we are allowed to alloc new data, and we are at the end of the
+        // current data allocation, or there is no data present, allocate or
+        // extend the data buffer.
+        
+        cyg_uint8 *newdata;
+        
+        if( node->data == NULL )
+            newdata = malloc( CYGNUM_RAMFS_REALLOC_INCREMENT );
+        else
+            newdata = realloc( node->data, pos+CYGNUM_RAMFS_REALLOC_INCREMENT );
+        
+        if( newdata == NULL ) return ENOSPC;
+        else memset( newdata+pos, 0, CYGNUM_RAMFS_REALLOC_INCREMENT );
+        
+        node->data = newdata;
+        node->datasize = pos+CYGNUM_RAMFS_REALLOC_INCREMENT;
+    }
+    else if( pos > node->datasize )
+    {
+        // Indicate end of data.
+        *size = 0;
+        return ENOERR;
+    }
+
+    *buffer = node->data+pos;
+    *size = node->datasize-pos;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// freebuffer_node()
+// Empty out the data storage from the node.
+
+static int freebuffer_node( ramfs_node *node )
+{
+    if( node->data != NULL )
+    {
+        free( node->data );
+    }
+
+    node->data = NULL;
+    node->datasize = 0;
+
+    return ENOERR;
+}
+
+//==========================================================================
+
+#else
+
+//==========================================================================
+// _BLOCKS storage management.
+// Data storage in the node is by means of a set of arrays of pointers to
+// blocks. The first array points directly to the data blocks. Subsequent
+// arrays point to single and double indirect blocks respectively. 
+
+// -------------------------------------------------------------------------
+// findbuffer_direct()
+// Indexes into an array of block pointers and extracts a pointer to the
+// data at offset _pos_, allocating new blocks if required.
+
+static int findbuffer_direct( off_t pos,
+                              ramfs_block **blocks,
+                              int nblocks,
+                              cyg_uint8 **buffer,
+                              size_t *size,
+                              cyg_bool alloc)
+{
+    int bi = pos / CYGNUM_RAMFS_BLOCK_SIZE;
+    int bpos = pos % CYGNUM_RAMFS_BLOCK_SIZE;
+    ramfs_block *b;
+    
+    *buffer = NULL;
+    *size = 0;
+    
+    if( bi > nblocks )
+        return ENOERR;
+
+    b = blocks[bi];
+
+    if( b == NULL )
+    {
+        // There is no block there. If _alloc_ is true we can fill the
+        // slot in with a new block. If it is false, we indicate end of
+        // data with a zero size result.
+        if( alloc )
+        {
+            b = block_alloc();
+            if( b == NULL )
+                return ENOSPC;
+            blocks[bi] = b;
+        }
+        else return ENOERR;
+    }
+
+    *buffer = &((*b)[bpos]);
+    *size = CYGNUM_RAMFS_BLOCK_SIZE - bpos;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// findbuffer_indirect1()
+// Indexes into an array of pointers to blocks containing pointers to
+// blocks and extracts a pointer to the data at offset _pos_,
+// allocating new blocks if required.
+
+#if CYGNUM_RAMFS_BLOCKS_INDIRECT1 > 0
+
+static int findbuffer_indirect1( off_t pos,
+                                 ramfs_block ***blocks,
+                                 int nblocks,
+                                 cyg_uint8 **buffer,
+                                 size_t *size,
+                                 cyg_bool alloc)
+{
+
+    int bi = pos / RAMFS_INDIRECT1_BLOCK_EXTENT;
+    int bpos = pos % RAMFS_INDIRECT1_BLOCK_EXTENT;
+    int err;
+    cyg_uint8 *b;
+    size_t sz;
+
+    // Use findbuffer_direct() to index and allocate
+    // the first level indirect block.
+    
+    err = findbuffer_direct( bi,
+                             (ramfs_block **)blocks,
+                             nblocks,
+                             &b,
+                             &sz,
+                             alloc);
+
+    if( err != ENOERR )
+        return err;
+
+    if( sz == 0 )
+    {
+        *size = 0;
+        return ENOERR;
+    }
+
+    // Use findbuffer_direct() on the first level indirect
+    // block to allocate and return the data pointer.
+    
+    return findbuffer_direct( bpos,
+                              blocks[bi],
+                              RAMFS_INDIRECT_PER_BLOCK,
+                              buffer,
+                              size,
+                              alloc);
+}
+
+#endif
+
+// -------------------------------------------------------------------------
+// findbuffer_indirect1()
+// Indexes into an array of pointers to blocks containing pointers to
+// blocks containing pointers to blocks (!) and extracts a pointer to
+// the data at offset _pos_, allocating new blocks if required.
+
+#if CYGNUM_RAMFS_BLOCKS_INDIRECT2 > 0
+
+static int findbuffer_indirect2( off_t pos,
+                                 ramfs_block ****blocks,
+                                 int nblocks,
+                                 cyg_uint8 **buffer,
+                                 size_t *size,
+                                 cyg_bool alloc)
+{
+    int bi = pos / RAMFS_INDIRECT2_BLOCK_EXTENT;
+    int bpos = pos % RAMFS_INDIRECT2_BLOCK_EXTENT;
+    int err;
+    cyg_uint8 *b;
+    size_t sz;
+
+    // Use findbuffer_direct() to index and allocate
+    // the first level indirect block.
+
+    err = findbuffer_direct( bi,
+                             (ramfs_block **)blocks,
+                             nblocks,
+                             &b,
+                             &sz,
+                             alloc);
+
+    if( err != ENOERR )
+        return err;
+
+    if( sz == 0 )
+    {
+        *size = 0;
+        return ENOERR;
+    }
+
+    // Use findbuffer_indirect1() on the first level indirect block to
+    // index and allocate the next level indirect block and the data
+    // block.
+    
+    return findbuffer_indirect1( bpos,
+                                 blocks[bi],
+                                 RAMFS_INDIRECT_PER_BLOCK,
+                                 buffer,
+                                 size,
+                                 alloc);
+}
+
+#endif
+
+// -------------------------------------------------------------------------
+// findbuffer_node()
+// Depending on the offset and configuration, call the appropriate
+// function to get the buffer pointer.
+
+static int findbuffer_node( ramfs_node  *node,
+                            off_t pos,
+                            cyg_uint8 **buffer,
+                            size_t *size,
+                            cyg_bool alloc)
+{
+#if CYGNUM_RAMFS_BLOCKS_DIRECT > 0    
+    if( pos < RAMFS_DIRECT_MAX )
+        return findbuffer_direct( pos,
+                                  node->direct,
+                                  CYGNUM_RAMFS_BLOCKS_DIRECT,
+                                  buffer,
+                                  size,
+                                  alloc);
+#endif        
+#if CYGNUM_RAMFS_BLOCKS_INDIRECT1 > 0    
+    if( pos < RAMFS_INDIRECT1_MAX )
+        return findbuffer_indirect1( pos - RAMFS_DIRECT_MAX,
+                                     node->indirect1,
+                                     CYGNUM_RAMFS_BLOCKS_INDIRECT1,
+                                     buffer,
+                                     size,
+                                     alloc);
+#endif        
+#if CYGNUM_RAMFS_BLOCKS_INDIRECT2 > 0    
+    if( pos < RAMFS_INDIRECT2_MAX )
+        return findbuffer_indirect2( pos - RAMFS_INDIRECT1_MAX,
+                                     node->indirect2,
+                                     CYGNUM_RAMFS_BLOCKS_INDIRECT2,
+                                     buffer,
+                                     size,
+                                     alloc);
+#endif
+
+    return EINVAL;
+}
+
+// -------------------------------------------------------------------------
+// freeblock_list()
+// Free a list of data blocks.
+
+static void freeblock_list( ramfs_block *blocks[],int nblocks )
+{
+    int i;
+    for( i = 0; i < nblocks ; i++ )
+    {
+        if( blocks[i] != NULL )
+        {
+            block_free( blocks[i] );
+            blocks[i] = NULL;
+        }
+    }
+}
+
+// -------------------------------------------------------------------------
+// freebuffer_node()
+// Free all the data blocks in the node and clear the pointers.
+
+static int freebuffer_node( ramfs_node *node )
+{
+#if CYGNUM_RAMFS_BLOCKS_DIRECT > 0
+    freeblock_list( node->direct, CYGNUM_RAMFS_BLOCKS_DIRECT );
+#endif
+
+#if CYGNUM_RAMFS_BLOCKS_INDIRECT1 > 0
+    {
+        int i;
+        for( i = 0; i < CYGNUM_RAMFS_BLOCKS_INDIRECT1 ; i++ )
+        {
+            if( node->indirect1[i] != NULL )
+            {
+                freeblock_list( (ramfs_block **)node->indirect1[i], RAMFS_INDIRECT_PER_BLOCK );
+                block_free( (ramfs_block *)node->indirect1[i] );
+                node->indirect1[i] = NULL;
+            }
+        }
+    }
+#endif    
+
+#if CYGNUM_RAMFS_BLOCKS_INDIRECT2 > 0
+    {
+        int i;
+        for( i = 0; i < CYGNUM_RAMFS_BLOCKS_INDIRECT2 ; i++ )
+        {
+            if( node->indirect2[i] != NULL )
+            {
+                ramfs_block ***b = node->indirect2[i];
+                int j;
+                for( j = 0; j < RAMFS_INDIRECT_PER_BLOCK ; j++ )
+                {
+                    if( b[j] != NULL )
+                    {
+                        freeblock_list( (ramfs_block **)b[j], RAMFS_INDIRECT_PER_BLOCK );
+                        block_free( (ramfs_block *)b[j] );
+                        b[j] = NULL;
+                    }
+                }
+                block_free( (ramfs_block *)node->indirect2[i] );
+                node->indirect2[i] = NULL;
+            }
+        }
+    }
+#endif
+
+    return ENOERR;
+}
+
+//==========================================================================
+
+#endif
+
+//==========================================================================
+// Node allocation
+
+// -------------------------------------------------------------------------
+// alloc_node()
+// Allocate a node and initialize it.
+// For the _SIMPLE allocation option, we just malloc it. For the
+// _BLOCKS option we allocate a block and use that. In theory we could
+// pack several nodes into a single block, but we don't at present due
+// to sheer lazyness.
+
+static ramfs_node *alloc_node( mode_t mode )
+{
+#ifdef CYGPKG_FS_RAM_SIMPLE
+    ramfs_node *node = malloc( sizeof( ramfs_node ) );
+
+    if( node == NULL )
+        return NULL;
+    
+#else
+    ramfs_block *b = block_alloc();
+    ramfs_node *node;
+
+    if( b == NULL )
+        return NULL;
+
+    node = (ramfs_node *)b;
+    
+#endif
+
+    memset( node, 0, sizeof(ramfs_node) );
+    
+    node->mode          = mode;
+    node->refcnt        = 0;
+    node->nlink         = 0;
+    node->size          = 0;
+    node->atime         = 
+    node->mtime         = 
+    node->ctime         = cyg_timestamp();
+
+#ifdef CYGPKG_FS_RAM_SIMPLE    
+    node->datasize      = 0;
+    node->data          = NULL;
+#else
+
+    // The node is already all zero
+    
+#endif    
+    return node;
+}
+
+// -------------------------------------------------------------------------
+// free_node()
+// Release a node either back to the free pool or back into the block
+// pool.
+
+static void free_node( ramfs_node *node )
+{
+#ifdef CYGPKG_FS_RAM_SIMPLE    
+
+    free( node );
+
+#else
+
+    block_free( (ramfs_block *)node );
+
+#endif    
+    
+}
+
+
+//==========================================================================
+// Ref count and nlink management
+
+// -------------------------------------------------------------------------
+// dec_refcnt()
+// Decrment the reference count on a node. If this makes the ref count
+// zero, and the number of links is either zero for a file or one for
+// a node, then this node is detached from the directory tree and can
+// be freed.
+
+static int dec_refcnt( ramfs_node *node )
+{
+    int err = ENOERR;
+    node->refcnt--;
+
+    if( node->refcnt == 0 &&
+        ((S_ISREG(node->mode) && node->nlink == 0 ) ||
+         (S_ISDIR(node->mode) && node->nlink == 1) )
+        )
+    {
+        // This node it now totally detached from the directory tree,
+        // so delete it.
+
+        if( S_ISDIR(node->mode) )
+        {
+            del_direntry( node, ".", 1 );
+            del_direntry( node, "..", 2 );
+        }
+        
+        err = freebuffer_node( node );
+
+        if( err == ENOERR )
+            free_node( node );
+    }
+
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// dec_nlink()
+// Decrement a node's link count. Since this has to do all the same
+// work as dec_refcnt() we implement this using that function by
+// essentially transferring the count to refcnt and then decrement
+// that.
+
+static int dec_nlink( ramfs_node *node )
+{
+    node->refcnt++;
+    
+    node->nlink--;
+
+    return dec_refcnt( node );
+}
+
+//==========================================================================
+// Directory operations
+
+// -------------------------------------------------------------------------
+// add_direntry()
+// Add an entry to a directory. This is added as a chain of entry
+// fragments until the name is exhausted.
+
+static int add_direntry( ramfs_node *dir,       // dir to add to
+                         const char *name,      // name to add
+                         int namelen,           // length of name
+                         ramfs_node *node       // node to reference
+                       )
+{
+    off_t pos = 0;
+    ramfs_dirent *d = NULL, *dp = NULL;
+    cyg_bool isfirst = true;
+
+    // Loop inserting fragments of the name into the directory until we
+    // have found a home for them all.
+    
+    while( namelen > 0 )
+    {
+        int fraglen = namelen;
+
+        if( fraglen > sizeof(d->name) )
+            fraglen = sizeof(d->name);
+
+        // Find a free fragment
+        for(;;)
+        {
+            cyg_uint8 *buf;
+            size_t size;
+            int err = findbuffer_node( dir, pos, &buf, &size, true );
+            if( err != ENOERR ) return err;
+
+            d = (ramfs_dirent *)buf;
+
+            if( size < sizeof(ramfs_dirent) || d->inuse )
+            {
+                pos += sizeof(ramfs_dirent);
+                continue;
+            }
+
+            break;
+        }
+
+        // d now points to a free dirent structure
+
+        d->node         = node;
+        d->inuse        = 1;
+        d->first        = isfirst;
+        d->namelen      = namelen;
+        d->fraglen      = fraglen;
+        if( dp ) dp->next = pos;
+
+        memcpy( d->name, name, fraglen );
+
+        name            += fraglen;
+        namelen         -= fraglen;
+        pos             += sizeof(ramfs_dirent);
+        dp              = d;
+        isfirst         = false;
+        
+    }
+
+    
+    d->last = 1;        // Mark last fragment
+    
+    // Update directory times
+    dir->mtime =
+    dir->ctime = cyg_timestamp();
+    
+    // Extend dir size if necessary
+    if( pos > dir->size )
+        dir->size = pos;
+    
+    // Count the new link
+    node->nlink++;
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// find_direntry()
+// Find a directory entry for the name and return a pointer to the first
+// entry fragment.
+
+static ramfs_dirent *find_direntry( ramfs_node *dir, const char *name, int namelen )
+{
+    ramfs_dirent *first = NULL;
+    off_t pos = 0;
+    int err;
+
+    // Loop over all the entries until a match is found or we run out
+    // of data.
+    while( pos < dir->size )
+    {
+        const char *frag = name;
+        ramfs_dirent *d;
+        cyg_uint8 *buf;
+        size_t size;
+        
+        // look for a first name fragment
+        for(;;)
+        {
+            err = findbuffer_node( dir, pos, &buf, &size, false );
+            if( err != ENOERR || size == 0)
+                return NULL;
+
+            d = (ramfs_dirent *)buf;
+
+            if( size < sizeof(ramfs_dirent) || !d->inuse || !d->first )
+            {
+                pos += sizeof(ramfs_dirent);
+                continue;
+            }
+
+            break;
+        }
+
+        // Here we have got a first fragment of a name, check it
+        // against the name we are looking for. First check that they
+        // are the same length.
+
+        if( d->namelen == namelen )
+        {
+            // We have a potential candidate here...
+            
+            first = d;      // Save it for later
+
+            // Now check that all the name fragments match
+            for(;;)
+            {
+                int fraglen = namelen-(frag-name);
+
+                if( fraglen > d->fraglen )
+                    fraglen = d->fraglen;
+
+                // compare strings, if different, look for another
+                if( memcmp( frag, d->name, fraglen ) != 0 )
+                    break;
+
+                frag        += fraglen;
+            
+                // If we are at the last fragment, then the whole name string
+                // has matched and we have a successful search.
+                
+                if( d->last )
+                        return first;
+
+                // Otherwise move on to next entry in chain
+                err = findbuffer_node( dir, d->next, &buf, &size, false );
+                if( err != ENOERR )
+                    return NULL;
+
+                d = (ramfs_dirent *)buf;
+
+            }
+        }
+
+        pos += sizeof(ramfs_dirent);        
+    }
+
+    return NULL;
+}
+
+// -------------------------------------------------------------------------
+// del_direntry()
+// Delete a named directory entry. Find it and then follow the chain
+// deleting the fragments as we go.
+
+static int del_direntry( ramfs_node *dir, const char *name, int namelen )
+{
+    ramfs_dirent *d = find_direntry( dir, name, namelen );
+    
+    if( d == NULL )
+        return ENOENT;
+
+    for(;;)
+    {
+        int err;
+        cyg_uint8 *buf;
+        size_t size;
+
+        d->inuse = 0;
+        if( d->last ) break;
+
+        err = findbuffer_node( dir, d->next, &buf, &size, false );
+        if( err != ENOERR )
+            return ENOENT;
+
+        d = (ramfs_dirent *)buf;
+    }
+
+    dec_nlink( d->node );
+    
+    return ENOERR;
+}
+
+//==========================================================================
+// Directory search
+
+// -------------------------------------------------------------------------
+// init_dirsearch()
+// Initialize a dirsearch object to start a search
+
+static void init_dirsearch( ramfs_dirsearch *ds,
+                            ramfs_node *dir,
+                            const char *name)
+{
+    ds->dir      = dir;
+    ds->path     = name;
+    ds->node     = dir;
+    ds->name     = name;
+    ds->namelen  = 0;
+    ds->last     = false;    
+}
+
+// -------------------------------------------------------------------------
+// find_entry()
+// Search a single directory for the next name in a path and update the
+// dirsearch object appropriately.
+
+static int find_entry( ramfs_dirsearch *ds )
+{
+    ramfs_node *dir = ds->dir;
+    const char *name = ds->path;
+    const char *n = name;
+    char namelen = 0;
+    ramfs_dirent *d;
+    
+    // check that we really have a directory
+    if( !S_ISDIR(dir->mode) )
+        return ENOTDIR;
+
+    // Isolate the next element of the path name. 
+    while( *n != '\0' && *n != '/' )
+        n++, namelen++;
+
+    // If we terminated on a NUL, set last flag.
+    if( *n == '\0' ) 
+        ds->last = true;
+
+    // update name in dirsearch object
+    ds->name = name;
+    ds->namelen = namelen;
+    
+    // Here we have the name and its length set up.
+    // Search the directory for a matching entry
+
+    d = find_direntry( dir, name, namelen );
+
+    if( d == NULL )
+        return ENOENT;
+
+    // pass back the node we have found
+    ds->node = d->node;
+
+    return ENOERR;
+
+}
+
+// -------------------------------------------------------------------------
+// ramfs_find()
+// Main interface to directory search code. This is used in all file
+// level operations to locate the object named by the pathname.
+
+static int ramfs_find( ramfs_dirsearch *d )
+{
+    int err;
+
+    // Short circuit empty paths
+    if( *(d->path) == '\0' )
+        return ENOERR;
+
+    // iterate down directory tree until we find the object
+    // we want.
+    for(;;)
+    {
+        err = find_entry( d );
+
+        if( err != ENOERR )
+            return err;
+
+        if( d->last )
+            return ENOERR;
+
+        // Update dirsearch object to search next directory.
+        d->dir = d->node;
+        d->path += d->namelen;
+        if( *(d->path) == '/' ) d->path++; // skip dirname separators
+    }
+}
+
+//==========================================================================
+// Pathconf support
+// This function provides support for pathconf() and fpathconf().
+
+static int ramfs_pathconf( ramfs_node *node, struct cyg_pathconf_info *info )
+{
+    int err = ENOERR;
+    
+    switch( info->name )
+    {
+    case _PC_LINK_MAX:
+        info->value = LINK_MAX;
+        break;
+        
+    case _PC_MAX_CANON:
+        info->value = -1;       // not supported
+        err = EINVAL;
+        break;
+        
+    case _PC_MAX_INPUT:
+        info->value = -1;       // not supported
+        err = EINVAL;        
+        break;
+        
+    case _PC_NAME_MAX:
+        info->value = NAME_MAX;
+        break;
+        
+    case _PC_PATH_MAX:
+        info->value = PATH_MAX;
+        break;        
+
+    case _PC_PIPE_BUF:
+        info->value = -1;       // not supported
+        err = EINVAL;        
+        break;        
+
+        
+    case _PC_ASYNC_IO:
+        info->value = -1;       // not supported
+        err = EINVAL;        
+        break;
+        
+    case _PC_CHOWN_RESTRICTED:
+        info->value = -1;       // not supported
+        err = EINVAL;
+        break;
+        
+    case _PC_NO_TRUNC:
+        info->value = 0;
+        break;
+        
+    case _PC_PRIO_IO:
+        info->value = 0;
+        break;
+        
+    case _PC_SYNC_IO:
+        info->value = 0;
+        break;
+        
+    case _PC_VDISABLE:
+        info->value = -1;       // not supported
+        err = EINVAL;        
+        break;
+        
+    default:
+        err = EINVAL;
+        break;
+    }
+
+    return err;
+}
+
+//==========================================================================
+// Filesystem operations
+
+// -------------------------------------------------------------------------
+// ramfs_mount()
+// Process a mount request. This mainly creates a root for the
+// filesystem.
+
+static int ramfs_mount    ( cyg_fstab_entry *fste, cyg_mtab_entry *mte )
+{
+    ramfs_node *root;
+    int err;
+    
+    // Allocate a node to be the root of this filesystem and initialize it.
+
+    root = alloc_node(__stat_mode_DIR);
+
+    if( root == NULL )
+        return ENOSPC;
+
+    // Add . and .. entries back to self.
+        
+    err = add_direntry( root, ".", 1, root );
+    if( err == ENOERR )
+        err = add_direntry( root, "..", 2, root );
+
+    if( err != ENOERR )
+    {
+        free_node( root );
+        return err;
+    }
+    
+    mte->root = (cyg_dir)root;
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_umount()
+// Unmount the filesystem. This will currently only succeed if the
+// filesystem is empty.
+
+static int ramfs_umount   ( cyg_mtab_entry *mte )
+{
+    ramfs_node *root = (ramfs_node *)mte->root;
+
+    // Check for open/inuse root
+    if( root->refcnt != 0 )
+        return EBUSY;
+
+    // Check that root directory is clear of extra links.
+    if( root->nlink != 2 )
+        return EBUSY;
+
+    // Just return it to free pool
+    free_node( root );
+
+    // Clear root pointer
+    mte->root = CYG_DIR_NULL;
+    
+    // That's all folks.
+        
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_open()
+// Open a file for reading or writing.
+
+static int ramfs_open     ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             int mode,  cyg_file *file )
+{
+
+    ramfs_dirsearch ds;
+    ramfs_node *node = NULL;
+    int err;
+
+    init_dirsearch( &ds, (ramfs_node *)dir, name );
+    
+    err = ramfs_find( &ds );
+
+    if( err == ENOENT )
+    {
+        if( ds.last && (mode & O_CREAT) )
+        {
+            // No node there, if the O_CREAT bit is set then we must
+            // create a new one. The dir and name fields of the dirsearch
+            // object will have been updated so we know where to put it.
+
+            node = alloc_node( __stat_mode_REG );
+
+            if( node == NULL )
+                return ENOSPC;
+
+            err = add_direntry( ds.dir, ds.name, ds.namelen, node );
+
+            if( err != ENOERR )
+            {
+                free_node( node );
+                return err;
+            }
+        
+            err = ENOERR;
+        }
+    }
+    else if( err == ENOERR )
+    {
+        // The node exists. If the O_CREAT and O_EXCL bits are set, we
+        // must fail the open.
+        
+        if( (mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) )
+            err = EEXIST;
+        else node = ds.node;
+    }
+    
+    if( err == ENOERR && (mode & O_TRUNC ) )
+    {
+        // If the O_TRUNC bit is set we must clean out the file data.
+
+        err = freebuffer_node( node );
+        node->size = 0;
+
+        // Update file times
+        node->ctime =
+        node->mtime = cyg_timestamp();
+    }
+
+    if( err != ENOERR ) return err;
+
+    // Check that we actually have a file here
+    if( S_ISDIR(node->mode) ) return EISDIR;
+
+    node->refcnt++;       // Count successful open
+    
+    // Initialize the file object
+    
+    file->f_flag        |= mode & CYG_FILE_MODE_MASK;
+    file->f_type        = CYG_FILE_TYPE_FILE;
+    file->f_ops         = &ramfs_fileops;
+    file->f_offset      = (mode&O_APPEND) ? node->size : 0;
+    file->f_data        = (CYG_ADDRWORD)node;
+    file->f_xops        = 0;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_unlink()
+// Remove a file link from its directory.
+
+static int ramfs_unlink   ( cyg_mtab_entry *mte, cyg_dir dir, const char *name )
+{
+    ramfs_dirsearch ds;
+    int err;
+
+    init_dirsearch( &ds, (ramfs_node *)dir, name );
+    
+    err = ramfs_find( &ds );
+
+    if( err != ENOERR ) return err;
+
+    // Cannot unlink directories, use rmdir() instead
+    if( S_ISDIR(ds.node->mode) )
+        return EPERM;
+
+    // Delete it from its directory
+    err = del_direntry( ds.dir, ds.name, ds.namelen );
+    
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_mkdir()
+// Create a new directory.
+
+static int ramfs_mkdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *name )
+{
+    ramfs_dirsearch ds;
+    ramfs_node *node = NULL;
+    int err;
+
+    init_dirsearch( &ds, (ramfs_node *)dir, name );
+    
+    err = ramfs_find( &ds );
+
+    if( err == ENOENT )
+    {
+        if( ds.last )
+        {
+            // The entry does not exist, and it is the last element in
+            // the pathname, so we can create it here.
+            int doterr, dotdoterr, direrr;
+        
+            node = alloc_node( __stat_mode_DIR );
+
+            if( node == NULL )
+                return ENOSPC;
+
+            // Add "." and ".." entries.
+            doterr = add_direntry( node, ".", 1, node );
+            dotdoterr = add_direntry( node, "..", 2, ds.dir );
+
+            // And add to parent directory.
+            direrr = add_direntry( ds.dir, ds.name, ds.namelen, node );
+
+            // check for any errors in that...
+            if( doterr+dotdoterr+direrr != ENOERR )
+            {
+                // For each of the add_direntry() calls that succeeded,
+                // we must now undo it.
+                
+                if( doterr == ENOERR )
+                    del_direntry( node, ".", 1 );
+                else err = doterr;
+
+                if( dotdoterr == ENOERR )
+                    del_direntry( node, "..", 2 );
+                else err = dotdoterr;
+
+                if( direrr == ENOERR )
+                    del_direntry( ds.dir, ds.name, ds.namelen );
+                else err = direrr;
+
+                // Free the data and the node itself.
+                freebuffer_node( node );
+                free_node( node );
+            }
+            else err = ENOERR;
+        }
+        // If this was not the last element, then and intermediate
+        // directory does not exist.
+    }
+    else
+    {
+        // If there we no error, something already exists with that
+        // name, so we cannot create another one.
+        
+        if( err == ENOERR )
+            err = EEXIST;
+    }
+
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_rmdir()
+// Remove a directory.
+
+static int ramfs_rmdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *name )
+{
+    ramfs_dirsearch ds;
+    int err;
+
+    init_dirsearch( &ds, (ramfs_node *)dir, name );
+    
+    err = ramfs_find( &ds );
+
+    if( err != ENOERR ) return err;
+
+    // Check that this is actually a directory.
+    if( !S_ISDIR(ds.node->mode) )
+        return EPERM;
+
+    // Delete the entry. This will adjust the link values
+    // accordingly and if the directory is now unreferenced,
+    // will cause it to be deleted.
+    
+    err = del_direntry( ds.dir, ds.name, ds.namelen );
+    
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_rename()
+// Rename a file/dir.
+
+static int ramfs_rename   ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
+                             cyg_dir dir2, const char *name2 )
+{
+    ramfs_dirsearch ds1, ds2;
+    int err;
+
+    init_dirsearch( &ds1, (ramfs_node *)dir1, name1 );
+    
+    err = ramfs_find( &ds1 );
+
+    if( err != ENOERR ) return err;
+
+    init_dirsearch( &ds2, (ramfs_node *)dir2, name2 );
+    
+    err = ramfs_find( &ds2 );
+
+    // Allow through renames to non-existent objects.
+    if( ds2.last && err == ENOENT )
+        ds2.node = NULL, err = ENOERR;
+    
+    if( err != ENOERR ) return err;
+
+    // Null rename, just return
+    if( ds1.node == ds2.node )
+        return ENOERR;
+
+    // First deal with any entry that is at the destination
+    if( ds2.node )
+    {
+        // Check that we are renaming like-for-like
+
+        if( !S_ISDIR(ds1.node->mode) && S_ISDIR(ds2.node->mode) )
+            return EISDIR;
+
+        if( S_ISDIR(ds1.node->mode) && !S_ISDIR(ds2.node->mode) )
+            return ENOTDIR;
+
+        // Now delete the destination directory entry
+        
+        err = del_direntry( ds2.dir, ds2.name, ds2.namelen );
+        
+        if( err != ENOERR ) return err;
+
+    }
+
+    // Now we know that there is no clashing node at the destination,
+    // make a new direntry at the destination and delete the old entry
+    // at the source.
+
+    err = add_direntry( ds2.dir, ds2.name, ds2.namelen, ds1.node );
+
+    if( err == ENOERR )
+        err = del_direntry( ds1.dir, ds1.name, ds1.namelen );
+
+    // Update directory times
+    if( err == ENOERR )
+        ds1.dir->ctime =
+        ds1.dir->mtime =
+        ds2.dir->ctime =
+        ds2.dir->mtime = cyg_timestamp();
+            
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_link()
+// Make a new directory entry for a file.
+
+static int ramfs_link     ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
+                             cyg_dir dir2, const char *name2, int type )
+{
+    ramfs_dirsearch ds1, ds2;
+    int err;
+
+    // Only do hard links for now in this filesystem
+    if( type != CYG_FSLINK_HARD )
+        return EINVAL;
+    
+    init_dirsearch( &ds1, (ramfs_node *)dir1, name1 );
+    
+    err = ramfs_find( &ds1 );
+
+    if( err != ENOERR ) return err;
+
+    init_dirsearch( &ds2, (ramfs_node *)dir2, name2 );
+    
+    err = ramfs_find( &ds2 );
+
+    // Don't allow links to existing objects
+    if( err == ENOERR ) return EEXIST;
+    
+    // Allow through links to non-existing terminal objects
+    if( ds2.last && err == ENOENT )
+        ds2.node = NULL, err = ENOERR;
+
+    if( err != ENOERR ) return err;
+    
+    // Now we know that there is no existing node at the destination,
+    // make a new direntry at the destination.
+
+    err = add_direntry( ds2.dir, ds2.name, ds2.namelen, ds1.node );
+
+    if( err == ENOERR )
+        ds1.node->ctime =
+        ds2.dir->ctime =
+        ds2.dir->mtime = cyg_timestamp();
+            
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_opendir()
+// Open a directory for reading.
+
+static int ramfs_opendir  ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             cyg_file *file )
+{
+    ramfs_dirsearch ds;
+    int err;
+
+    init_dirsearch( &ds, (ramfs_node *)dir, name );
+    
+    err = ramfs_find( &ds );
+
+    if( err != ENOERR ) return err;
+
+    // check it is really a directory.
+    if( !S_ISDIR(ds.node->mode) ) return ENOTDIR;
+
+    ds.node->refcnt++;       // Count successful open
+    
+    // Initialize the file object, setting the f_ops field to a
+    // special set of file ops.
+    
+    file->f_type        = CYG_FILE_TYPE_FILE;
+    file->f_ops         = &ramfs_dirops;
+    file->f_offset      = 0;
+    file->f_data        = (CYG_ADDRWORD)ds.node;
+    file->f_xops        = 0;
+        
+    return ENOERR;
+
+}
+
+// -------------------------------------------------------------------------
+// ramfs_chdir()
+// Change directory support.
+
+static int ramfs_chdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             cyg_dir *dir_out )
+{
+    if( dir_out != NULL )
+    {
+        // This is a request to get a new directory pointer in
+        // *dir_out.
+
+        ramfs_dirsearch ds;
+        int err;
+    
+        init_dirsearch( &ds, (ramfs_node *)dir, name );
+    
+        err = ramfs_find( &ds );
+
+        if( err != ENOERR ) return err;
+
+        // check it is a directory
+        if( !S_ISDIR(ds.node->mode) )
+            return ENOTDIR;
+        
+        // Increment ref count to keep this directory in existent
+        // while it is the current cdir.
+        ds.node->refcnt++;
+
+        // Pass it out
+        *dir_out = (cyg_dir)ds.node;
+    }
+    else
+    {
+        // If no output dir is required, this means that the mte and
+        // dir arguments are the current cdir setting and we should
+        // forget this fact.
+
+        ramfs_node *node = (ramfs_node *)dir;
+
+        // Just decrement directory reference count.
+        dec_refcnt( node );
+    }
+        
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_stat()
+// Get struct stat info for named object.
+
+static int ramfs_stat     ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             struct stat *buf)
+{
+    ramfs_dirsearch ds;
+    int err;
+
+    init_dirsearch( &ds, (ramfs_node *)dir, name );
+    
+    err = ramfs_find( &ds );
+
+    if( err != ENOERR ) return err;
+
+    // Fill in the status
+    buf->st_mode        = ds.node->mode;
+    buf->st_ino         = (ino_t)ds.node;
+    buf->st_dev         = 0;
+    buf->st_nlink       = ds.node->nlink;
+    buf->st_uid         = 0;
+    buf->st_gid         = 0;
+    buf->st_size        = ds.node->size;
+    buf->st_atime       = ds.node->atime;
+    buf->st_mtime       = ds.node->mtime;
+    buf->st_ctime       = ds.node->ctime;
+    
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_getinfo()
+// Getinfo. Currently only support pathconf().
+
+static int ramfs_getinfo  ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             int key, void *buf, int len )
+{
+    ramfs_dirsearch ds;
+    int err;
+
+    init_dirsearch( &ds, (ramfs_node *)dir, name );
+    
+    err = ramfs_find( &ds );
+
+    if( err != ENOERR ) return err;
+
+    switch( key )
+    {
+    case FS_INFO_CONF:
+        err = ramfs_pathconf( ds.node, (struct cyg_pathconf_info *)buf );
+        break;
+        
+    default:
+        err = EINVAL;
+    }
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_setinfo()
+// Setinfo. Nothing to support here at present.
+
+static int ramfs_setinfo  ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                             int key, void *buf, int len )
+{
+    // No setinfo keys supported at present
+    
+    return EINVAL;
+}
+
+
+//==========================================================================
+// File operations
+
+// -------------------------------------------------------------------------
+// ramfs_fo_read()
+// Read data from the file.
+
+static int ramfs_fo_read      (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
+{
+    ramfs_node *node = (ramfs_node *)fp->f_data;
+    int i;
+    off_t pos = fp->f_offset;
+    ssize_t resid = uio->uio_resid;
+
+    // Loop over the io vectors until there are none left
+    for( i = 0; i < uio->uio_iovcnt; i++ )
+    {
+        cyg_iovec *iov = &uio->uio_iov[i];
+        char *buf = (char *)iov->iov_base;
+        off_t len = iov->iov_len;
+
+        // Loop over each vector filling it with data from the file.
+        while( len > 0 && pos < node->size )
+        {
+            cyg_uint8 *fbuf;
+            size_t bsize;
+            off_t l = len;
+            int err;
+
+            // Get a pointer to the data at offset _pos_.
+            err = findbuffer_node( node, pos, &fbuf, &bsize, false );
+
+            if( err != ENOERR )
+                return err;
+
+            // adjust size to end of file if necessary
+            if( l > node->size-pos )
+                l = node->size-pos;
+            
+            // adjust size to the amount of contiguous data we can see
+            // at present.
+            if( l > bsize )
+                l = bsize;
+
+            // copy data out
+            memcpy( buf, fbuf, l );
+
+            // Update working vars
+            len -= l;
+            buf += l;
+            pos += l;
+            resid -= l;
+        }
+    }
+
+    // We successfully read some data, update the node's access time
+    // and update the file offset and transfer residue.
+    
+    node->atime = cyg_timestamp();
+
+    uio->uio_resid = resid;
+    fp->f_offset = pos;
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_fo_write()
+// Write data to file.
+
+static int ramfs_fo_write     (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
+{
+    ramfs_node *node = (ramfs_node *)fp->f_data;
+    off_t pos = fp->f_offset;
+    ssize_t resid = uio->uio_resid;    
+    int i;
+
+    // If the APPEND mode bit was supplied, force all writes to
+    // the end of the file.
+    if( fp->f_flag & CYG_FAPPEND )
+        pos = fp->f_offset = node->size;
+    
+    // Check that pos is within current file size, or at the very end.
+    if( pos < 0 || pos > node->size )
+        return EINVAL;
+
+    // Now loop over the iovecs until they are all done, or
+    // we get an error.
+    for( i = 0; i < uio->uio_iovcnt; i++ )
+    {
+        cyg_iovec *iov = &uio->uio_iov[i];
+        char *buf = (char *)iov->iov_base;
+        off_t len = iov->iov_len;
+
+        // loop over the vector writing it to the file until it has
+        // all been done.
+        while( len > 0 )
+        {
+            cyg_uint8 *fbuf;
+            size_t bsize;
+            off_t l = len;
+            int err;
+            
+            err = findbuffer_node( node, pos, &fbuf, &bsize, true );
+
+            // Stop writing if there is no more space in the file and
+            // indicate end of data.
+            if( err == ENOSPC )
+                break;
+            
+            if( err != ENOERR )
+                return err;
+            
+            // adjust size to this block
+            if( l > bsize )
+                l = bsize;
+
+            // copy data in
+            memcpy( fbuf, buf, l );
+
+            // Update working vars
+            len -= l;
+            buf += l;
+            pos += l;
+            resid -= l;
+        }
+    }
+
+    // We wrote some data successfully, update the modified and access
+    // times of the node, increase its size appropriately, and update
+    // the file offset and transfer residue.
+    node->mtime =
+    node->ctime = cyg_timestamp();
+    if( pos > node->size )
+        node->size = pos;    
+
+    uio->uio_resid = resid;
+    fp->f_offset = pos;
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_fo_lseek()
+// Seek to a new file position.
+
+static int ramfs_fo_lseek     (struct CYG_FILE_TAG *fp, off_t *apos, int whence )
+{
+    ramfs_node *node = (ramfs_node *)fp->f_data;
+    off_t pos = *apos;
+
+    switch( whence )
+    {
+    case SEEK_SET:
+        // Pos is already where we want to be.
+        break;
+
+    case SEEK_CUR:
+        // Add pos to current offset.
+        pos += fp->f_offset;
+        break;
+
+    case SEEK_END:
+        // Add pos to file size.
+        pos += node->size;
+        break;
+
+    default:
+        return EINVAL;
+    }
+    
+    // Check that pos is still within current file size, or at the
+    // very end.
+    if( pos < 0 || pos > node->size )
+        return EINVAL;
+
+    // All OK, set fp offset and return new position.
+    *apos = fp->f_offset = pos;
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_fo_ioctl()
+// Handle ioctls. Currently none are defined.
+
+static int ramfs_fo_ioctl     (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
+                                CYG_ADDRWORD data)
+{
+    // No Ioctls currenly defined.
+
+    return EINVAL;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_fo_fsync().
+// Force the file out to data storage.
+
+static int ramfs_fo_fsync     (struct CYG_FILE_TAG *fp, int mode )
+{
+    // Data is always permanently where it belongs, nothing to do
+    // here.
+  
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_fo_close()
+// Close a file. We just decrement the refcnt and let it go away if
+// that is all that is keeping it here.
+
+static int ramfs_fo_close     (struct CYG_FILE_TAG *fp)
+{
+    ramfs_node *node = (ramfs_node *)fp->f_data;
+
+    dec_refcnt( node );
+
+    fp->f_data = 0;     // zero data pointer
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+//ramfs_fo_fstat()
+// Get file status.
+
+static int ramfs_fo_fstat     (struct CYG_FILE_TAG *fp, struct stat *buf )
+{
+    ramfs_node *node = (ramfs_node *)fp->f_data;
+
+    // Fill in the status
+    buf->st_mode        = node->mode;
+    buf->st_ino         = (ino_t)node;
+    buf->st_dev         = 0;
+    buf->st_nlink       = node->nlink;
+    buf->st_uid         = 0;
+    buf->st_gid         = 0;
+    buf->st_size        = node->size;
+    buf->st_atime       = node->atime;
+    buf->st_mtime       = node->mtime;
+    buf->st_ctime       = node->ctime;
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_fo_getinfo()
+// Get info. Currently only supports fpathconf().
+
+static int ramfs_fo_getinfo   (struct CYG_FILE_TAG *fp, int key, void *buf, int len )
+{
+    ramfs_node *node = (ramfs_node *)fp->f_data;    
+    int err;
+
+    switch( key )
+    {
+    case FS_INFO_CONF:
+        err = ramfs_pathconf( node, (struct cyg_pathconf_info *)buf );
+        break;
+        
+    default:
+        err = EINVAL;
+    }
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_fo_setinfo()
+// Set info. Nothing supported here.
+
+static int ramfs_fo_setinfo   (struct CYG_FILE_TAG *fp, int key, void *buf, int len )
+{
+    // No setinfo key supported at present
+    
+    return ENOERR;
+}
+
+
+//==========================================================================
+// Directory operations
+
+// -------------------------------------------------------------------------
+// ramfs_fo_dirread()
+// Read a single directory entry from a file.
+
+static int ramfs_fo_dirread      (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
+{
+    ramfs_node *dir = (ramfs_node *)fp->f_data;
+    off_t pos = fp->f_offset;
+    int err = ENOERR;
+    struct dirent *ent = (struct dirent *)uio->uio_iov[0].iov_base;
+    char *nbuf = ent->d_name;
+    int nlen = sizeof(ent->d_name)-1;
+    off_t len = uio->uio_iov[0].iov_len;
+    ramfs_dirent *d = NULL;
+    cyg_uint8 *buf;
+    size_t size;
+        
+    if( len < sizeof(struct dirent) )
+        return EINVAL;    
+    
+    // look for a first name fragment
+
+    while( pos < dir->size )
+    {
+        err = findbuffer_node( dir, pos, &buf, &size, false );
+        if( err != ENOERR || size == 0)
+            break;
+
+        d = (ramfs_dirent *)buf;
+
+        if( size < sizeof(ramfs_dirent) || !d->inuse || !d->first )
+        {
+            pos += sizeof(ramfs_dirent);
+            continue;
+        }
+
+        break;
+    }
+
+    // Check we have not exceeded the size of the directory.
+    if( pos == dir->size )
+        return err;
+    
+    // Here we  have the first fragment of a directory entry.
+
+    for(;;)
+    {
+        int fraglen = d->fraglen;
+
+        // adjust to allow for remaining space in dirent struct
+        if( fraglen > nlen )
+            fraglen = nlen;
+        
+        memcpy( nbuf, d->name, fraglen);
+        nbuf += fraglen;
+        nlen -= fraglen;
+            
+        // if we hit the last entry, we have a successful transfer
+        if( d->last || nlen == 0)
+            break;
+
+        // Otherwise move on to next entry in chain
+        err = findbuffer_node( dir, d->next, &buf, &size, false );
+        if( err != ENOERR )
+            return err;
+
+        d = (ramfs_dirent *)buf;
+    }
+
+    // A successful read. Terminate the entry name with a NUL, set the
+    // residue and set the file offset to restart at the next
+    // directory entry.
+    
+    *nbuf = '\0';
+    uio->uio_resid -= sizeof(struct dirent);
+    fp->f_offset = pos+sizeof(ramfs_dirent);
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// ramfs_fo_dirlseek()
+// Seek directory to start.
+
+static int ramfs_fo_dirlseek     (struct CYG_FILE_TAG *fp, off_t *pos, int whence )
+{
+    // Only allow SEEK_SET to zero
+    
+    if( whence != SEEK_SET || *pos != 0)
+        return EINVAL;
+
+    *pos = fp->f_offset = 0;
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// EOF ramfs.c