Mercurial > ecos
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
