OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/ecos-2.0/packages/fs/ram
    from Rev 1254 to Rev 1765
    Reverse comparison

Rev 1254 → Rev 1765

/v2_0/cdl/ramfs.cdl
0,0 → 1,192
# ====================================================================
#
# ramfs.cdl
#
# RAM Filesystem configuration data
#
# ====================================================================
#####ECOSGPLCOPYRIGHTBEGIN####
## -------------------------------------------
## This file is part of eCos, the Embedded Configurable Operating System.
## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
##
## eCos is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation; either version 2 or (at your option) any later version.
##
## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
## for more details.
##
## You should have received a copy of the GNU General Public License along
## with eCos; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
##
## As a special exception, if other files instantiate templates or use macros
## or inline functions from this file, or you compile this file and link it
## with other works to produce a work based on this file, this file does not
## by itself cause the resulting work to be covered by the GNU General Public
## License. However the source code for this file must still be made available
## in accordance with section (3) of the GNU General Public License.
##
## This exception does not invalidate any other reasons why a work based on
## this file might be covered by the GNU General Public License.
##
## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
## at http://sources.redhat.com/ecos/ecos-license/
## -------------------------------------------
#####ECOSGPLCOPYRIGHTEND####
# ====================================================================
######DESCRIPTIONBEGIN####
#
# Author(s): nickg
# Original data: nickg
# Contributors:
# Date: 2000-08-01
#
#####DESCRIPTIONEND####
#
# ====================================================================
 
cdl_package CYGPKG_FS_RAM {
display "RAM filesystem"
doc ref/fileio.html
include_dir cyg/ramfs
 
requires CYGPKG_IO_FILEIO
 
requires CYGPKG_ISOINFRA
requires CYGPKG_ERROR
requires CYGINT_ISO_ERRNO
requires CYGINT_ISO_ERRNO_CODES
 
implements CYGINT_IO_FILEIO_FS
 
compile -library=libextras.a ramfs.c
 
# ----------------------------------------------------------------------
# Simple allocation mechanism using malloc()
cdl_component CYGPKG_FS_RAM_SIMPLE {
display "Simple, malloc() based, implementation"
requires { CYGINT_ISO_MALLOC != 0 }
default_value 1
active_if !CYGPKG_FS_RAM_BLOCKS
 
cdl_option CYGNUM_RAMFS_REALLOC_INCREMENT {
display "Size of file data storage increment"
flavor data
default_value 256
legal_values 64 to 32768
description "This option controls the size of the increment to a file data
storage block."
}
 
}
 
# ----------------------------------------------------------------------
# Block based allocation, using either malloc() or a private block
# pool.
cdl_component CYGPKG_FS_RAM_BLOCKS {
display "Block-based RAM filesystem allocation"
default_value 0
active_if !CYGPKG_FS_RAM_SIMPLE
 
cdl_option CYGNUM_RAMFS_BLOCK_SIZE {
display "Size of file data storage block"
flavor data
default_value 256
legal_values 64 to 32768
description "This option controls the size of a data storage block."
}
 
cdl_option CYGNUM_RAMFS_BLOCKS_DIRECT {
display "Directly referenced data storage blocks"
flavor data
default_value 8
legal_values 0 to 32
description "This option controls the number of data storage blocks that
are referenced directly from a file or directory node."
}
 
cdl_option CYGNUM_RAMFS_BLOCKS_INDIRECT1 {
display "Single level indirect data storage blocks"
flavor data
default_value 1
legal_values 0 to 32
description "This option controls the number of single level indirect storage
blocks that are referenced from a file or directory node."
}
 
cdl_option CYGNUM_RAMFS_BLOCKS_INDIRECT2 {
display "Two level indirect data storage blocks"
flavor data
default_value 1
legal_values 0 to 32
description "This option controls the number of two level indirect storage
blocks that are referenced from a file or directory node."
}
 
cdl_component CYGPKG_FS_RAM_BLOCKS_ARRAY {
display "Use block array rather than malloc()"
default_value 0
description "This option controls whether the blocks are allocated from
an array of blocks rather from the heap using malloc()."
 
cdl_option CYGPKG_FS_RAM_BLOCKS_ARRAY_EXTERN {
display "Block array is external"
default_value 0
description "This option controls whether the block array is
defined by the RAMFS package or whether it is provided
by an external component. The latter option may be
useful when the RAM file system is to be put into a
special memory area."
}
 
cdl_option CYGPKG_FS_RAM_BLOCKS_ARRAY_NAME {
display "Name of external block array"
active_if CYGPKG_FS_RAM_BLOCKS_ARRAY_EXTERN
flavor data
default_value "cyg_ramfs_block_array"
description "This option controls what the symbol name of the external
block array will be."
}
 
cdl_option CYGNUM_FS_RAM_BLOCKS_ARRAY_SIZE {
display "Size of blocks array"
flavor data
default_value 128
legal_values 1 to 9999999999
description "The number of blocks in the array. The total size of
the array will be this value times the block size."
}
}
}
 
cdl_option CYGNUM_RAMFS_DIRENT_SIZE {
display "Directory entry size"
flavor data
default_value 32
legal_values 16 to { CYGNUM_RAMFS_BLOCK_SIZE ? CYGNUM_RAMFS_BLOCK_SIZE : 128 }
description "This option controls the number of two level indirect storage
blocks that are referenced from a file or directory node."
}
 
# ----------------------------------------------------------------
# Tests
 
cdl_option CYGPKG_FS_RAM_TESTS {
display "RAM FS tests"
flavor data
no_define
calculated { "tests/fileio1.c" }
description "
This option specifies the set of tests for the RAM FS package."
}
}
 
# End of ramfs.cdl
/v2_0/tests/fileio1.c
0,0 → 1,651
//==========================================================================
//
// fileio1.c
//
// Test fileio system
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): nickg
// Contributors: nickg
// Date: 2000-05-25
// Purpose: Test fileio system
// Description: This test uses the testfs to check out the initialization
// and basic operation of the fileio system
//
//
//
//
//
//
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <pkgconf/hal.h>
#include <pkgconf/kernel.h>
#include <pkgconf/io_fileio.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 <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
 
#include <cyg/fileio/fileio.h>
 
#include <cyg/infra/testcase.h>
#include <cyg/infra/diag.h> // HAL polled output
 
//==========================================================================
 
#if 0
MTAB_ENTRY( ramfs_mte1,
"/",
"ramfs",
"",
0);
#endif
 
//==========================================================================
 
#define SHOW_RESULT( _fn, _res ) \
diag_printf("<FAIL>: " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):"");
 
//==========================================================================
 
#define IOSIZE 100
 
#define LONGNAME1 "long_file_name_that_should_take_up_more_than_one_directory_entry_1"
#define LONGNAME2 "long_file_name_that_should_take_up_more_than_one_directory_entry_2"
 
 
//==========================================================================
 
#ifndef CYGPKG_LIBC_STRING
 
char *strcat( char *s1, const char *s2 )
{
char *s = s1;
while( *s1 ) s1++;
while( (*s1++ = *s2++) != 0);
return s;
}
 
#endif
 
//==========================================================================
 
static void listdir( char *name, int statp, int numexpected, int *numgot )
{
int err;
DIR *dirp;
int num=0;
diag_printf("<INFO>: reading directory %s\n",name);
dirp = opendir( name );
if( dirp == NULL ) SHOW_RESULT( opendir, -1 );
 
for(;;)
{
struct dirent *entry = readdir( dirp );
if( entry == NULL )
break;
num++;
diag_printf("<INFO>: entry %14s",entry->d_name);
if( statp )
{
char fullname[PATH_MAX];
struct stat sbuf;
 
if( name[0] )
{
strcpy(fullname, name );
if( !(name[0] == '/' && name[1] == 0 ) )
strcat(fullname, "/" );
}
else fullname[0] = 0;
strcat(fullname, entry->d_name );
err = stat( fullname, &sbuf );
if( err < 0 )
{
if( errno == ENOSYS )
diag_printf(" <no status available>");
else SHOW_RESULT( stat, err );
}
else
{
diag_printf(" [mode %08x ino %08x nlink %d size %d]",
sbuf.st_mode,sbuf.st_ino,sbuf.st_nlink,sbuf.st_size);
}
}
 
diag_printf("\n");
}
 
err = closedir( dirp );
if( err < 0 ) SHOW_RESULT( stat, err );
if (numexpected >= 0 && num != numexpected)
CYG_TEST_FAIL("Wrong number of dir entries\n");
if ( numgot != NULL )
*numgot = num;
}
 
//==========================================================================
 
static void createfile( char *name, size_t size )
{
char buf[IOSIZE];
int fd;
ssize_t wrote;
int i;
int err;
 
diag_printf("<INFO>: create file %s size %d\n",name,size);
 
err = access( name, F_OK );
if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err );
for( i = 0; i < IOSIZE; i++ ) buf[i] = i%256;
fd = open( name, O_WRONLY|O_CREAT );
if( fd < 0 ) SHOW_RESULT( open, fd );
 
while( size > 0 )
{
ssize_t len = size;
if ( len > IOSIZE ) len = IOSIZE;
wrote = write( fd, buf, len );
if( wrote != len ) SHOW_RESULT( write, wrote );
 
size -= wrote;
}
 
err = close( fd );
if( err < 0 ) SHOW_RESULT( close, err );
}
 
//==========================================================================
 
#if 0
static void maxfile( char *name )
{
char buf[IOSIZE];
int fd;
ssize_t wrote;
int i;
int err;
size_t size = 0;
diag_printf("<INFO>: create maximal file %s\n",name);
 
err = access( name, F_OK );
if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err );
for( i = 0; i < IOSIZE; i++ ) buf[i] = i%256;
fd = open( name, O_WRONLY|O_CREAT );
if( fd < 0 ) SHOW_RESULT( open, fd );
 
do
{
wrote = write( fd, buf, IOSIZE );
if( wrote < 0 ) SHOW_RESULT( write, wrote );
 
size += wrote;
} while( wrote == IOSIZE );
 
diag_printf("<INFO>: file size == %d\n",size);
 
err = close( fd );
if( err < 0 ) SHOW_RESULT( close, err );
}
#endif
 
//==========================================================================
 
static void checkfile( char *name )
{
char buf[IOSIZE];
int fd;
ssize_t done;
int i;
int err;
off_t pos = 0;
 
diag_printf("<INFO>: check file %s\n",name);
err = access( name, F_OK );
if( err != 0 ) SHOW_RESULT( access, err );
 
fd = open( name, O_RDONLY );
if( fd < 0 ) SHOW_RESULT( open, fd );
 
for(;;)
{
done = read( fd, buf, IOSIZE );
if( done < 0 ) SHOW_RESULT( read, done );
 
if( done == 0 ) break;
 
for( i = 0; i < done; i++ )
if( buf[i] != i%256 )
{
diag_printf("buf[%d+%d](%02x) != %02x\n",pos,i,buf[i],i%256);
CYG_TEST_FAIL("Data read not equal to data written\n");
}
pos += done;
}
 
err = close( fd );
if( err < 0 ) SHOW_RESULT( close, err );
}
 
//==========================================================================
 
static void copyfile( char *name2, char *name1 )
{
 
int err;
char buf[IOSIZE];
int fd1, fd2;
ssize_t done, wrote;
 
diag_printf("<INFO>: copy file %s -> %s\n",name2,name1);
 
err = access( name1, F_OK );
if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err );
 
err = access( name2, F_OK );
if( err != 0 ) SHOW_RESULT( access, err );
fd1 = open( name1, O_WRONLY|O_CREAT );
if( fd1 < 0 ) SHOW_RESULT( open, fd1 );
 
fd2 = open( name2, O_RDONLY );
if( fd2 < 0 ) SHOW_RESULT( open, fd2 );
for(;;)
{
done = read( fd2, buf, IOSIZE );
if( done < 0 ) SHOW_RESULT( read, done );
 
if( done == 0 ) break;
 
wrote = write( fd1, buf, done );
if( wrote != done ) SHOW_RESULT( write, wrote );
 
if( wrote != done ) break;
}
 
err = close( fd1 );
if( err < 0 ) SHOW_RESULT( close, err );
 
err = close( fd2 );
if( err < 0 ) SHOW_RESULT( close, err );
}
 
//==========================================================================
 
static void comparefiles( char *name2, char *name1 )
{
int err;
char buf1[IOSIZE];
char buf2[IOSIZE];
int fd1, fd2;
ssize_t done1, done2;
int i;
 
diag_printf("<INFO>: compare files %s == %s\n",name2,name1);
 
err = access( name1, F_OK );
if( err != 0 ) SHOW_RESULT( access, err );
 
err = access( name1, F_OK );
if( err != 0 ) SHOW_RESULT( access, err );
fd1 = open( name1, O_RDONLY );
if( fd1 < 0 ) SHOW_RESULT( open, fd1 );
 
fd2 = open( name2, O_RDONLY );
if( fd2 < 0 ) SHOW_RESULT( open, fd2 );
for(;;)
{
done1 = read( fd1, buf1, IOSIZE );
if( done1 < 0 ) SHOW_RESULT( read, done1 );
 
done2 = read( fd2, buf2, IOSIZE );
if( done2 < 0 ) SHOW_RESULT( read, done2 );
 
if( done1 != done2 )
diag_printf("Files different sizes\n");
if( done1 == 0 ) break;
 
for( i = 0; i < done1; i++ )
if( buf1[i] != buf2[i] )
{
diag_printf("buf1[%d](%02x) != buf1[%d](%02x)\n",i,buf1[i],i,buf2[i]);
CYG_TEST_FAIL("Data in files not equal\n");
}
}
 
err = close( fd1 );
if( err < 0 ) SHOW_RESULT( close, err );
 
err = close( fd2 );
if( err < 0 ) SHOW_RESULT( close, err );
}
 
//==========================================================================
 
void checkcwd( const char *cwd )
{
static char cwdbuf[PATH_MAX];
char *ret;
 
ret = getcwd( cwdbuf, sizeof(cwdbuf));
if( ret == NULL ) SHOW_RESULT( getcwd, ret );
 
if( strcmp( cwdbuf, cwd ) != 0 )
{
diag_printf( "cwdbuf %s cwd %s\n",cwdbuf, cwd );
CYG_TEST_FAIL( "Current directory mismatch");
}
}
 
//==========================================================================
// main
 
int main( int argc, char **argv )
{
int err;
int existingdirents=-1;
 
CYG_TEST_INIT();
 
// --------------------------------------------------------------
 
err = mount( "", "/", "ramfs" );
if( err < 0 ) SHOW_RESULT( mount, err );
 
err = chdir( "/" );
if( err < 0 ) SHOW_RESULT( chdir, err );
 
checkcwd( "/" );
listdir( "/", true, -1, &existingdirents );
if ( existingdirents < 2 )
CYG_TEST_FAIL("Not enough dir entries\n");
 
// --------------------------------------------------------------
 
createfile( "/foo", 202 );
checkfile( "foo" );
copyfile( "foo", "fee");
checkfile( "fee" );
comparefiles( "foo", "/fee" );
diag_printf("<INFO>: mkdir bar\n");
err = mkdir( "/bar", 0 );
if( err < 0 ) SHOW_RESULT( mkdir, err );
 
listdir( "/" , true, existingdirents+3, NULL );
 
copyfile( "fee", "/bar/fum" );
checkfile( "bar/fum" );
comparefiles( "/fee", "bar/fum" );
 
diag_printf("<INFO>: cd bar\n");
err = chdir( "bar" );
if( err < 0 ) SHOW_RESULT( chdir, err );
 
checkcwd( "/bar" );
diag_printf("<INFO>: rename /foo bundy\n");
err = rename( "/foo", "bundy" );
if( err < 0 ) SHOW_RESULT( rename, err );
listdir( "/", true, existingdirents+2, NULL );
listdir( "" , true, 4, NULL );
 
checkfile( "/bar/bundy" );
comparefiles("/fee", "bundy" );
 
// --------------------------------------------------------------
 
createfile( LONGNAME1, 123 );
checkfile( LONGNAME1 );
copyfile( LONGNAME1, LONGNAME2 );
 
listdir( "", false, 6, NULL );
diag_printf("<INFO>: unlink " LONGNAME1 "\n");
err = unlink( LONGNAME1 );
if( err < 0 ) SHOW_RESULT( unlink, err );
 
diag_printf("<INFO>: unlink " LONGNAME2 "\n");
err = unlink( LONGNAME2 );
if( err < 0 ) SHOW_RESULT( unlink, err );
// --------------------------------------------------------------
 
diag_printf("<INFO>: unlink fee\n");
err = unlink( "/fee" );
if( err < 0 ) SHOW_RESULT( unlink, err );
 
diag_printf("<INFO>: unlink fum\n");
err = unlink( "fum" );
if( err < 0 ) SHOW_RESULT( unlink, err );
 
diag_printf("<INFO>: unlink /bar/bundy\n");
err = unlink( "/bar/bundy" );
if( err < 0 ) SHOW_RESULT( unlink, err );
 
diag_printf("<INFO>: cd /\n");
err = chdir( "/" );
if( err < 0 ) SHOW_RESULT( chdir, err );
 
checkcwd( "/" );
diag_printf("<INFO>: rmdir /bar\n");
err = rmdir( "/bar" );
if( err < 0 ) SHOW_RESULT( rmdir, err );
listdir( "/", false, existingdirents, NULL );
 
// --------------------------------------------------------------
 
diag_printf("<INFO>: mount /ram \n");
err = mount( "", "/ram", "ramfs" );
if( err < 0 ) SHOW_RESULT( mount, err );
 
createfile( "/ram/tinky", 456 );
copyfile( "/ram/tinky", "/ram/laalaa" );
checkfile( "/ram/tinky");
checkfile( "/ram/laalaa");
comparefiles( "/ram/tinky", "/ram/laalaa" );
 
diag_printf("<INFO>: cd /ram\n");
err = chdir( "/ram" );
if( err < 0 ) SHOW_RESULT( chdir, err );
 
checkcwd( "/ram" );
diag_printf("<INFO>: mkdir noonoo\n");
err = mkdir( "noonoo", 0 );
if( err < 0 ) SHOW_RESULT( mkdir, err );
 
listdir( "." , true, existingdirents+3, NULL);
 
diag_printf("<INFO>: cd noonoo\n");
err = chdir( "noonoo" );
if( err < 0 ) SHOW_RESULT( chdir, err );
 
checkcwd( "/ram/noonoo" );
createfile( "tinky", 678 );
checkfile( "tinky" );
 
createfile( "dipsy", 3456 );
checkfile( "dipsy" );
copyfile( "dipsy", "po" );
checkfile( "po" );
comparefiles( "dipsy", "po" );
 
listdir( ".", true, 5, NULL );
listdir( "", true, 5, NULL );
listdir( "..", true, existingdirents+3, NULL );
 
// --------------------------------------------------------------
 
diag_printf("<INFO>: unlink tinky\n");
err = unlink( "tinky" );
if( err < 0 ) SHOW_RESULT( unlink, err );
 
diag_printf("<INFO>: unlink dipsy\n");
err = unlink( "dipsy" );
if( err < 0 ) SHOW_RESULT( unlink, err );
 
diag_printf("<INFO>: unlink po\n");
err = unlink( "po" );
if( err < 0 ) SHOW_RESULT( unlink, err );
 
diag_printf("<INFO>: cd ..\n");
err = chdir( ".." );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/ram" );
diag_printf("<INFO>: rmdir noonoo\n");
err = rmdir( "noonoo" );
if( err < 0 ) SHOW_RESULT( rmdir, err );
 
// --------------------------------------------------------------
 
err = mkdir( "x", 0 );
if( err < 0 ) SHOW_RESULT( mkdir, err );
err = mkdir( "x/y", 0 );
if( err < 0 ) SHOW_RESULT( mkdir, err );
err = mkdir( "x/y/z", 0 );
if( err < 0 ) SHOW_RESULT( mkdir, err );
 
err = mkdir( "x/y/z/w", 0 );
if( err < 0 ) SHOW_RESULT( mkdir, err );
diag_printf("<INFO>: cd /ram/x/y/z/w\n");
err = chdir( "/ram/x/y/z/w" );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/ram/x/y/z/w" );
 
diag_printf("<INFO>: cd ..\n");
err = chdir( ".." );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/ram/x/y/z" );
diag_printf("<INFO>: cd .\n");
err = chdir( "." );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/ram/x/y/z" );
 
diag_printf("<INFO>: cd ../../y\n");
err = chdir( "../../y" );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/ram/x/y" );
 
diag_printf("<INFO>: cd ../..\n");
err = chdir( "../.." );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/ram" );
 
diag_printf("<INFO>: rmdir x/y/z/w\n");
err = rmdir( "x/y/z/w" );
if( err < 0 ) SHOW_RESULT( rmdir, err );
 
diag_printf("<INFO>: rmdir x/y/z\n");
err = rmdir( "x/y/z" );
if( err < 0 ) SHOW_RESULT( rmdir, err );
 
diag_printf("<INFO>: rmdir x/y\n");
err = rmdir( "x/y" );
if( err < 0 ) SHOW_RESULT( rmdir, err );
 
diag_printf("<INFO>: rmdir x\n");
err = rmdir( "x" );
if( err < 0 ) SHOW_RESULT( rmdir, err );
// --------------------------------------------------------------
diag_printf("<INFO>: unlink tinky\n");
err = unlink( "tinky" );
if( err < 0 ) SHOW_RESULT( unlink, err );
 
diag_printf("<INFO>: unlink laalaa\n");
err = unlink( "laalaa" );
if( err < 0 ) SHOW_RESULT( unlink, err );
 
diag_printf("<INFO>: cd /\n");
err = chdir( "/" );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/" );
diag_printf("<INFO>: umount /ram\n");
err = umount( "/ram" );
if( err < 0 ) SHOW_RESULT( umount, err );
CYG_TEST_PASS_FINISH("fileio1");
}
 
// -------------------------------------------------------------------------
// EOF fileio1.c
/v2_0/ChangeLog
0,0 → 1,73
2003-02-24 Jonathan Larmour <jifl@eCosCentric.com>
 
* cdl/ramfs.cdl: Fix doc link.
 
2002-12-06 Andrew Lunn <andrew.lunn@ascom.ch>
 
* cdl/ramfs.cdl: Implements the CYGINT_IO_FILEIO_FS interface
 
2002-01-25 Jonathan Larmour <jlarmour@redhat.com>
 
* tests/fileio1.c (main): Check in listdir that the number of
dirents is correct.
 
2001-07-26 Jonathan Larmour <jlarmour@redhat.com>
 
* src/ramfs.c (findbuffer_indirect1): Determine correct offset in
indirect block list.
(findbuffer_indirect2): Ditto.
(findbuffer_direct): Compare block index with number of blocks
correctly.
 
2000-10-05 Nick Garnett <nickg@cygnus.co.uk>
 
* tests/fileio1.c:
Extended to check getcwd() and chdir() functionality more fully.
 
2000-08-18 Nick Garnett <nickg@cygnus.co.uk>
 
* cdl/ramfs.cdl:
* src/ramfs.c:
* tests/fileio1.c:
Created this example RAM filesystem both as a usable filesystem
and as an example of how to build filesystems for the fileio
infrastructure.
 
 
 
//===========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//===========================================================================
 
/v2_0/src/ramfs.c
0,0 → 1,2395
//==========================================================================
//
// ramfs.c
//
// RAM file system
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####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*CYGNUM_RAMFS_BLOCK_SIZE,
(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*CYGNUM_RAMFS_BLOCK_SIZE,
(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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.