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 |