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

Subversion Repositories openrisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/rtos/ecos-2.0/packages/fs
    from Rev 27 to Rev 174
    Reverse comparison

Rev 27 → Rev 174

/ram/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
/ram/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
/ram/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####
//===========================================================================
 
/ram/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
/jffs2/v2_0/cdl/jffs2.cdl
0,0 → 1,123
# ====================================================================
#
# jffs2.cdl
#
# JFFS2 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): David Woodhouse, Dominic Ostrowski
# Original data: ported from JFFS2 by David Woodhouse
# Contributors: dominic.ostrowski@3glab.com
# Date: 2000-08-28
#
#####DESCRIPTIONEND####
#
# ====================================================================
 
cdl_package CYGPKG_FS_JFFS2 {
display "JFFS2 filesystem"
doc ref/fileio.html
include_dir ""
 
requires CYGPKG_IO_FILEIO
requires CYGPKG_IO_FLASH
requires CYGPKG_COMPRESS_ZLIB
requires CYGINT_ISO_MALLOC
 
requires CYGPKG_ISOINFRA
requires CYGPKG_ERROR
requires CYGINT_ISO_ERRNO
requires CYGINT_ISO_ERRNO_CODES
requires CYGPKG_IO_FLASH_BLOCK_DEVICE
requires CYGPKG_IO_FILEIO_INODE
requires CYGPKG_LINUX_COMPAT
requires CYGPKG_CRC
 
implements CYGINT_IO_FILEIO_FS
 
compile -library=libextras.a fs-ecos.c
compile build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c erase.c dir-ecos.c write.c gc.c read.c compr.c compr_zlib.c compr_rtime.c compr_rubin.c file-ecos.c
 
cdl_option CYGPKG_FS_JFFS2_CFLAGS_ADD {
display "Additional compiler flags"
flavor data
no_define
# We add -D__ECOS to trigger eCos-specific code in places.
# We add -Werror because I find it useful.
default_value { "-D__ECOS -nostdinc -iwithprefix include -Werror" }
description "
This option modifies the set of compiler flags for
building the JFFS2 package.
These flags are used in addition
to the set of global flags."
}
 
cdl_option CYGPKG_FS_JFFS2_CFLAGS_REMOVE {
display "Suppressed compiler flags"
flavor data
no_define
# We remove -Wpointer-arith so that some of the hacky Linux-compat code
# (in file.c) compiled. We can probably remove it when that's replaced
# properly.
default_value { "-Wpointer-arith" }
description "
This option modifies the set of compiler flags for
building the JFFS2 package. These flags are removed from
the set of global flags if present."
}
 
# ----------------------------------------------------------------
# Tests
 
cdl_option CYGPKG_FS_JFFS2_TESTS {
display "JFFS2 FS tests"
flavor data
no_define
calculated { "tests/fileio1.c" }
description "
This option specifies the set of tests for the JFFS2 FS package."
}
}
 
# End of jffs2.cdl
 
 
 
 
 
/jffs2/v2_0/tests/romfileio1.c
0,0 → 1,370
//==========================================================================
//
// 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, richard.panton@3glab.com
// 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 <cyg/io/flash.h>
 
#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
 
#include <pkgconf/fs_jffs2.h> // Address of JFFS2
 
//==========================================================================
 
/* MTAB_ENTRY( jffs2_mte1,
"/",
"jffs2",
"",
(CYG_ADDRWORD) CYGNUM_FS_JFFS2_BASE_ADDRESS ); */
 
 
//==========================================================================
 
#define SHOW_RESULT( _fn, _res ) \
diag_printf("<FAIL>: " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):"");
 
#define CHKFAIL_TYPE( _fn, _res, _type ) { \
if ( _res != -1 ) \
diag_printf("<FAIL>: " #_fn "() returned %d (expected -1)\n", _res); \
else if ( errno != _type ) \
diag_printf("<FAIL>: " #_fn "() failed with errno %d (%s),\n expected %d (%s)\n", errno, strerror(errno), _type, strerror(_type) ); \
}
 
//==========================================================================
 
#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 err;
DIR *dirp;
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;
 
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 );
}
 
//==========================================================================
 
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 );
}
 
//==========================================================================
// main
 
int main( int argc, char **argv )
{
int err;
// char address[16];
 
CYG_TEST_INIT();
 
// --------------------------------------------------------------
 
err = mount( CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1, "/", "jffs2" );
if ( err != ENOERR ) SHOW_RESULT( chdir, err );
diag_printf("<INFO>: JFFS2 root follows\n");
diag_printf("<INFO>: Note that dev cannot be stat()ed\n");
listdir( "/", true );
 
diag_printf("<INFO>: cd /etc\n" );
err = chdir( "/etc" );
if ( err < 0 ) SHOW_RESULT( chdir, err );
 
diag_printf("<INFO>: JFFS2 list of '' follows\n");
listdir( "", true );
 
diag_printf("<INFO>: JFFS2 list of /etc follows\n");
listdir( "/etc", true );
 
//diag_printf("<INFO>: JFFS2 list of . follows\n");
//listdir( ".", true );
err = mount( "", "/var", "ramfs" );
if( err < 0 ) SHOW_RESULT( mount, err );
 
copyfile( "/etc/passwd", "/var/passwd_copy" );
 
comparefiles( "/etc/passwd", "/var/passwd_copy" );
diag_printf("<INFO>: JFFS2 list of / follows\n");
diag_printf("<INFO>: Note that /var now gives stat() info for RAMFS\n");
listdir( "/", true );
 
diag_printf("<INFO>: Mount JFFS2 again onto /mnt\n");
//sprintf( address, "%p", (void*)CYGNUM_FS_JFFS2_BASE_ADDRESS );
err = mount( CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1, "/mnt", "jffs2" );
if( err < 0 ) SHOW_RESULT( mount, err );
 
comparefiles( "/etc/passwd", "/mnt/etc/passwd" );
 
 
err = mkdir( "/foo", 0 );
CHKFAIL_TYPE( mkdir, err, EROFS );
 
err = rename( "/var", "/tmp" ); // RAMFS is mounted here
CHKFAIL_TYPE( rename, err, EXDEV );
 
err = rename( "/var/passwd_copy", "/mnt/etc/passwd_copy" );
CHKFAIL_TYPE( rename, err, EXDEV );
 
err = rename( "/etc", "/tmp" );
CHKFAIL_TYPE( rename, err, EROFS );
 
diag_printf("<INFO>: cd /etc\n");
err = chdir( "/etc" );
if( err < 0 ) SHOW_RESULT( chdir, err );
 
err = chdir( "/mnt/etc" );
if( err < 0 ) SHOW_RESULT( chdir, err );
 
//listdir( ".", true );
 
diag_printf("<INFO>: unlink /tmp\n");
err = unlink( "/tmp" );
CHKFAIL_TYPE( unlink, err, EROFS );
 
diag_printf("<INFO>: mount random area\n");
//sprintf(address, "%p", (void*)(CYGNUM_FS_JFFS2_BASE_ADDRESS + 0x20000));
err = mount( "", "/tmp", "jffs2" );
SHOW_RESULT( mount, err );
 
err = umount( "/mnt" );
if( err < 0 ) SHOW_RESULT( umount, err );
 
err = umount( "/var" );
if( err < 0 ) SHOW_RESULT( umount, err );
 
err = umount( "/" );
if( err < 0 ) SHOW_RESULT( umount, err );
 
 
CYG_TEST_PASS_FINISH("fileio1");
}
 
// -------------------------------------------------------------------------
// EOF fileio1.c
/jffs2/v2_0/tests/fileio1.c
0,0 → 1,669
//==========================================================================
//
// 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 <cyg/io/flash.h>
 
#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
 
#include <pkgconf/fs_jffs2.h> // Address of JFFS2
 
//==========================================================================
 
#if 0
MTAB_ENTRY( jffs2_mte1,
"/",
"jffs2",
CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1,
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 i;
int existingdirents=-1;
 
CYG_TEST_INIT();
 
// --------------------------------------------------------------
 
err = mount( CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1, "/", "jffs2" );
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 /jffs2 \n");
err = mount( CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1, "/jffs2", "jffs2" );
if( err < 0 ) SHOW_RESULT( mount, err );
 
createfile( "/jffs2/tinky", 456 );
copyfile( "/jffs2/tinky", "/jffs2/laalaa" );
checkfile( "/jffs2/tinky");
checkfile( "/jffs2/laalaa");
comparefiles( "/jffs2/tinky", "/jffs2/laalaa" );
 
diag_printf("<INFO>: cd /jffs2\n");
err = chdir( "/jffs2" );
if( err < 0 ) SHOW_RESULT( chdir, err );
 
checkcwd( "/jffs2" );
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( "/jffs2/noonoo" );
createfile( "tinky", 678 );
checkfile( "tinky" );
 
createfile( "dipsy", 3456 );
checkfile( "dipsy" );
copyfile( "dipsy", "po" );
checkfile( "po" );
comparefiles( "dipsy", "po" );
 
 
/*for(i=0;i<2048;i++) {
diag_printf("<INFO>: churningchurningchurning................................ITERATION = %d\n", i);
createfile( "churningchurningchurning", 4096 );
diag_printf("<INFO>: unlink churningchurningchurning\n");
err = unlink( "churningchurningchurning" );
if( err < 0 ) SHOW_RESULT( unlink, err );
}*/
 
 
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( "/jffs2" );
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 /jffs2/x/y/z/w\n");
err = chdir( "/jffs2/x/y/z/w" );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/jffs2/x/y/z/w" );
 
diag_printf("<INFO>: cd ..\n");
err = chdir( ".." );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/jffs2/x/y/z" );
diag_printf("<INFO>: cd .\n");
err = chdir( "." );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/jffs2/x/y/z" );
 
diag_printf("<INFO>: cd ../../y\n");
err = chdir( "../../y" );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/jffs2/x/y" );
 
diag_printf("<INFO>: cd ../..\n");
err = chdir( "../.." );
if( err < 0 ) SHOW_RESULT( chdir, err );
checkcwd( "/jffs2" );
 
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 /jffs2\n");
err = umount( "/jffs2" );
if( err < 0 ) SHOW_RESULT( umount, err );
diag_printf("<INFO>: umount /\n");
err = umount( "/" );
if( err < 0 ) SHOW_RESULT( umount, err );
CYG_TEST_PASS_FINISH("fileio1");
}
 
// -------------------------------------------------------------------------
// EOF fileio1.c
/jffs2/v2_0/include/linux/jffs2.h
0,0 → 1,188
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in the
* jffs2 directory.
*
* $Id: jffs2.h,v 1.1.1.1 2004-02-14 13:29:21 phoenix Exp $
*
*/
 
#ifndef __LINUX_JFFS2_H__
#define __LINUX_JFFS2_H__
 
/* You must include something which defines the C99 uintXX_t types.
We don't do it from here because this file is used in too many
different environments. */
 
#define JFFS2_SUPER_MAGIC 0x72b6
 
/* Values we may expect to find in the 'magic' field */
#define JFFS2_OLD_MAGIC_BITMASK 0x1984
#define JFFS2_MAGIC_BITMASK 0x1985
#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */
#define JFFS2_EMPTY_BITMASK 0xffff
#define JFFS2_DIRTY_BITMASK 0x0000
 
/* We only allow a single char for length, and 0xFF is empty flash so
we don't want it confused with a real length. Hence max 254.
*/
#define JFFS2_MAX_NAME_LEN 254
 
/* How small can we sensibly write nodes? */
#define JFFS2_MIN_DATA_LEN 128
 
#define JFFS2_COMPR_NONE 0x00
#define JFFS2_COMPR_ZERO 0x01
#define JFFS2_COMPR_RTIME 0x02
#define JFFS2_COMPR_RUBINMIPS 0x03
#define JFFS2_COMPR_COPY 0x04
#define JFFS2_COMPR_DYNRUBIN 0x05
#define JFFS2_COMPR_ZLIB 0x06
/* Compatibility flags. */
#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
#define JFFS2_NODE_ACCURATE 0x2000
/* INCOMPAT: Fail to mount the filesystem */
#define JFFS2_FEATURE_INCOMPAT 0xc000
/* ROCOMPAT: Mount read-only */
#define JFFS2_FEATURE_ROCOMPAT 0x8000
/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
 
#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
 
// Maybe later...
//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
 
 
#define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at
mount time, don't wait for it to
happen later */
#define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific
compression type */
 
 
/* These can go once we've made sure we've caught all uses without
byteswapping */
 
typedef struct {
uint32_t v32;
} __attribute__((packed)) jint32_t;
 
typedef struct {
uint32_t m;
} __attribute__((packed)) jmode_t;
 
typedef struct {
uint16_t v16;
} __attribute__((packed)) jint16_t;
 
#define JFFS2_NATIVE_ENDIAN
 
/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
whatever OS we're actually running on here too. */
 
#if defined(JFFS2_NATIVE_ENDIAN)
#define cpu_to_je16(x) ((jint16_t){x})
#define cpu_to_je32(x) ((jint32_t){x})
#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
 
#define je16_to_cpu(x) ((x).v16)
#define je32_to_cpu(x) ((x).v32)
#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
#elif defined(JFFS2_BIG_ENDIAN)
#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
 
#define je16_to_cpu(x) (be16_to_cpu(x.v16))
#define je32_to_cpu(x) (be32_to_cpu(x.v32))
#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
#elif defined(JFFS2_LITTLE_ENDIAN)
#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
 
#define je16_to_cpu(x) (le16_to_cpu(x.v16))
#define je32_to_cpu(x) (le32_to_cpu(x.v32))
#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
#else
#error wibble
#endif
 
struct jffs2_unknown_node
{
/* All start like this */
jint16_t magic;
jint16_t nodetype;
jint32_t totlen; /* So we can skip over nodes we don't grok */
jint32_t hdr_crc;
} __attribute__((packed));
 
struct jffs2_raw_dirent
{
jint16_t magic;
jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */
jint32_t totlen;
jint32_t hdr_crc;
jint32_t pino;
jint32_t version;
jint32_t ino; /* == zero for unlink */
jint32_t mctime;
uint8_t nsize;
uint8_t type;
uint8_t unused[2];
jint32_t node_crc;
jint32_t name_crc;
uint8_t name[0];
} __attribute__((packed));
 
/* The JFFS2 raw inode structure: Used for storage on physical media. */
/* The uid, gid, atime, mtime and ctime members could be longer, but
are left like this for space efficiency. If and when people decide
they really need them extended, it's simple enough to add support for
a new type of raw node.
*/
struct jffs2_raw_inode
{
jint16_t magic; /* A constant magic number. */
jint16_t nodetype; /* == JFFS_NODETYPE_INODE */
jint32_t totlen; /* Total length of this node (inc data, etc.) */
jint32_t hdr_crc;
jint32_t ino; /* Inode number. */
jint32_t version; /* Version number. */
jmode_t mode; /* The file's type or mode. */
jint16_t uid; /* The file's owner. */
jint16_t gid; /* The file's group. */
jint32_t isize; /* Total resultant size of this inode (used for truncations) */
jint32_t atime; /* Last access time. */
jint32_t mtime; /* Last modification time. */
jint32_t ctime; /* Change time. */
jint32_t offset; /* Where to begin to write. */
jint32_t csize; /* (Compressed) data size */
jint32_t dsize; /* Size of the node's data. (after decompression) */
uint8_t compr; /* Compression algorithm used */
uint8_t usercompr; /* Compression algorithm requested by the user */
jint16_t flags; /* See JFFS2_INO_FLAG_* */
jint32_t data_crc; /* CRC for the (compressed) data. */
jint32_t node_crc; /* CRC for the raw inode (excluding data) */
uint8_t data[0];
} __attribute__((packed));
 
union jffs2_node_union {
struct jffs2_raw_inode i;
struct jffs2_raw_dirent d;
struct jffs2_unknown_node u;
};
 
#endif /* __LINUX_JFFS2_H__ */
/jffs2/v2_0/include/linux/jffs2_fs_i.h
0,0 → 1,46
/* $Id: jffs2_fs_i.h,v 1.1.1.1 2004-02-14 13:29:21 phoenix Exp $ */
 
#ifndef _JFFS2_FS_I
#define _JFFS2_FS_I
 
#include <linux/version.h>
#include <linux/rbtree.h>
 
struct jffs2_inode_info {
/* We need an internal semaphore similar to inode->i_sem.
Unfortunately, we can't used the existing one, because
either the GC would deadlock, or we'd have to release it
before letting GC proceed. Or we'd have to put ugliness
into the GC code so it didn't attempt to obtain the i_sem
for the inode(s) which are already locked */
struct semaphore sem;
 
/* The highest (datanode) version number used for this ino */
uint32_t highest_version;
 
/* List of data fragments which make up the file */
struct rb_root fragtree;
 
/* There may be one datanode which isn't referenced by any of the
above fragments, if it contains a metadata update but no actual
data - or if this is a directory inode */
/* This also holds the _only_ dnode for symlinks/device nodes,
etc. */
struct jffs2_full_dnode *metadata;
 
/* Directory entries */
struct jffs2_full_dirent *dents;
 
/* Some stuff we just have to keep in-core at all times, for each inode. */
struct jffs2_inode_cache *inocache;
 
uint16_t flags;
uint8_t usercompr;
#if !defined (__ECOS)
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
struct inode vfs_inode;
#endif
#endif
};
 
#endif /* _JFFS2_FS_I */
/jffs2/v2_0/include/linux/jffs2_fs_sb.h
0,0 → 1,99
/* $Id: jffs2_fs_sb.h,v 1.1.1.1 2004-02-14 13:29:21 phoenix Exp $ */
 
#ifndef _JFFS2_FS_SB
#define _JFFS2_FS_SB
 
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/completion.h>
#include <asm/semaphore.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/list.h>
 
#define JFFS2_SB_FLAG_RO 1
#define JFFS2_SB_FLAG_MOUNTING 2
 
/* A struct for the overall file system control. Pointers to
jffs2_sb_info structs are named `c' in the source code.
Nee jffs_control
*/
struct jffs2_sb_info {
struct mtd_info *mtd;
 
uint32_t highest_ino;
uint32_t checked_ino;
 
unsigned int flags;
 
struct task_struct *gc_task; /* GC task struct */
struct semaphore gc_thread_start; /* GC thread start mutex */
struct completion gc_thread_exit; /* GC thread exit completion port */
 
struct semaphore alloc_sem; /* Used to protect all the following
fields, and also to protect against
out-of-order writing of nodes.
And GC.
*/
uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER
(i.e. zero for OOB CLEANMARKER */
 
uint32_t flash_size;
uint32_t used_size;
uint32_t dirty_size;
uint32_t wasted_size;
uint32_t free_size;
uint32_t erasing_size;
uint32_t bad_size;
uint32_t sector_size;
uint32_t unchecked_size;
 
uint32_t nr_free_blocks;
uint32_t nr_erasing_blocks;
 
uint32_t nr_blocks;
struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks
* from the offset (blocks[ofs / sector_size]) */
struct jffs2_eraseblock *nextblock; /* The block we're currently filling */
 
struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */
 
struct list_head clean_list; /* Blocks 100% full of clean data */
struct list_head very_dirty_list; /* Blocks with lots of dirty space */
struct list_head dirty_list; /* Blocks with some dirty space */
struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */
struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */
struct list_head erasing_list; /* Blocks which are currently erasing */
struct list_head erase_pending_list; /* Blocks which need erasing now */
struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */
struct list_head free_list; /* Blocks which are free and ready to be used */
struct list_head bad_list; /* Bad blocks. */
struct list_head bad_used_list; /* Bad blocks with valid data in. */
 
spinlock_t erase_completion_lock; /* Protect free_list and erasing_list
against erase completion handler */
wait_queue_head_t erase_wait; /* For waiting for erases to complete */
 
wait_queue_head_t inocache_wq;
struct jffs2_inode_cache **inocache_list;
spinlock_t inocache_lock;
/* Sem to allow jffs2_garbage_collect_deletion_dirent to
drop the erase_completion_lock while it's holding a pointer
to an obsoleted node. I don't like this. Alternatives welcomed. */
struct semaphore erase_free_sem;
 
/* Write-behind buffer for NAND flash */
unsigned char *wbuf;
uint32_t wbuf_ofs;
uint32_t wbuf_len;
uint32_t wbuf_pagesize;
struct work_struct wbuf_task; /* task for timed wbuf flush */
struct timer_list wbuf_timer; /* timer for flushing wbuf */
 
/* OS-private pointer for getting back to master superblock info */
void *os_priv;
};
 
#endif /* _JFFS2_FB_SB */
/jffs2/v2_0/doc/readme.txt
0,0 → 1,27
This package is a port of the JFFS2 flash filing system to eCos. It has been
developed on the Compaq Ipaq, and has not been tested on any other device.
 
This code is subject to the original licensing terms, and additionally it
should be noted that this code is still in an early stage of development.
 
As this code will write to flash directly, caution should be exercised in
its use. It may cause areas of the flash chips essential to the operation of
the device to become corrupted.
 
 
Minor modifications are necessary to the the eCos flash drivers
 
io/flash/current/src/flash.c
 
devs/flash/intel/strata/current/src/flash_program_buf.c
 
to allow byte aligned rather than word aligned writes, and to ensure overwriting an existing
word is successful (these are supplied in jffs2/current/src).
 
 
Two test files are included fileio1.c (which performs the same tests as used for eCos RamFS),
and romfileio1.c (tests as eCos RomFS).
 
romfileio1.c requires that a jffs2 filesystem image jffs2.img is present at the desired mount point.
This image was prepared on Linux with the tools originating with JFFS2 source from
www.infradead.org
/jffs2/v2_0/ChangeLog
0,0 → 1,253
2003-02-24 Jonathan Larmour <jifl@eCosCentric.com>
 
* cdl/jffs2.cdl: Fix doc link.
 
2003-02-05 Jonathan Larmour <jifl@eCosCentric.com>
 
* cdl/jffs2.cdl: Remove unused flash geometry CDL options.
 
2003-02-04 Gary Thomas <gary@mlbassoc.com> on behalf of
2003-02-04 David Woodhouse <dwmw2@cambridge.redhat.com>
 
* src/write.c:
* src/scan.c:
* src/readinode.c:
* src/read.c:
* src/pushpull.h:
* src/os-ecos.h:
* src/nodemgmt.c:
* src/nodelist.h:
* src/nodelist.c:
* src/malloc-ecos.c:
* src/jffs2port.h:
* src/gc.c:
* src/file-ecos.c:
* src/erase.c:
* src/dir-ecos.c:
* src/compr_zlib.c:
* src/compr_rubin.c:
* src/compr_rtime.c:
* src/compr.c:
* src/build.c:
* cdl/jffs2.cdl: Update to latest public JFFS2 codebase.
 
* src/list.h:
* src/jffs2_fs_sb.h:
* src/jffs2_fs_i.h:
* src/jffs2.h:
* src/jffs2.c:
* src/crc32.h:
* src/background.c: Removed/renamed file(s).
 
* src/fs-ecos.c:
* include/linux/jffs2_fs_sb.h:
* include/linux/jffs2_fs_i.h:
* include/linux/jffs2.h: New file(s).
 
2002-12-06 Andrew Lunn <andrew.lunn@ascom.ch>
 
* cdl/jffs2.cdl: Implements the CYGINT_IO_FILEIO_FS interface.
 
2002-10-11 Andrew Lunn <andrew.lunn@ascom.ch>
 
* src/crc32.h (crc32): Use the CRC package for crc calculation
* tests/romfileio1.c (main): Pass the name of the device to mount
 
2002-05-20 Jonathan Larmour <jlarmour@redhat.com>
 
* src/LICENCE: New file. Contains license for JFFS2, now GPL+exception.
* src/background.c: Point at LICENSE file instead of existing text.
* src/build.c: Ditto.
* src/compr.c: Ditto.
* src/compr_rtime.c: Ditto.
* src/compr_rubin.c: Ditto.
* src/compr_zlib.c: Ditto.
* src/dir-ecos.c: Ditto.
* src/erase.c: Ditto.
* src/file-ecos.c: Ditto.
* src/gc.c: Ditto.
* src/jffs2.h: Ditto.
* src/list.h: Ditto.
* src/malloc-ecos.c: Ditto.
* src/nodelist.c: Ditto.
* src/nodelist.h: Ditto.
* src/nodemgmt.c: Ditto.
* src/os-ecos.h: Ditto.
* src/pushpull.h: Ditto.
* src/read.c: Ditto.
* src/readinode.c: Ditto.
* src/scan.c: Ditto.
* src/write.c: Ditto.
 
2002-01-28 David Woodhouse <dwmw2@cambridge.redhat.com>
 
* src/super-ecos.c: Removed.
* src/jffs2.c: Merge jffs2_write_super() and jffs2_put_super() into
the routines from which they were called, put jffs2_read_super()
in as a static function with a view to doing same RSN.
* src/jffs2port.h: Remove prototypes of functions that died.
* cdl/jffs2.cdl: Remove super-ecos.c
* src/dir-ecos.c src/write.c: Increase highest_version _before_
assigning to new node, not after.
2002-01-27 David Woodhouse <dwmw2@cambridge.redhat.com>
 
* src/read.c (jffs2_read_inode_range): Deal correctly with
non-page-aligned read requests. We have to deal with the
case where we want to read from somewhere other than the
beginning of a frag.
* src/jffs2.c (jffs2_fo_read): Use jffs2_read_inode_range
instead of jffs2_readpage.
 
2002-01-25 Jonathan Larmour <jlarmour@redhat.com>
 
* cdl/jffs2.cdl: We want CYGPKG_IO_FILEIO_INODE.
* src/dir-ecos.c (jffs2_symlink): Remove. eCos doesn't support symlinks.
(jffs2_mknod): Similar.
(jffs2_mkdir): Don't call d_instantiate - its a nop.
(jffs2_rename): Ditto.
* src/file-ecos.c (jffs2_commit_write): Don't set blocks.
* src/jffs2.c (jffs2_flash_writev): Rewrite to only write aligned
quantities to flash.
* src/jffs2port.h: Lots of decrufting.
* src/os-ecos.h: Ditto (a bit).
* src/readinode.c (jffs2_read_inode): Don't set blocks/blksize in inode.
* src/write.c (jffs2_new_inode): Ditto when __ECOS.
(jffs2_write_dnode): don't call writev with extra vectors
unnecessarily.
* src/super-ecos.c (jffs2_statfs): Remove - unused.
 
2002-01-25 David Woodhouse <dwmw2@cambridge.redhat.com>
 
* src/super-ecos.c: Cruftectomy.
* src/compr*.[ch] src/pushpull.h: Namespace cleanups merged from
mainstream sources. Bit push functions made inline.
* src/pushpull.c: Removed.
* cdl/jffs2.c: Remove reference to pushpull.c
* src/file-ecos.c: Cruftectomy. Lots of unused stuff here.
* src/jffs2.c src/jffs2port.h: Remove some functions from jffs2.c
which are no longer used, move some others to jffs2port.h as
inline functions so they don't pollute the namespace.
 
2002-01-24 Jonathan Larmour <jlarmour@redhat.com>
 
* tests/fileio1.c: Check in listdir that the number of dirents is
correct, taking into account existing files in case it's live.
 
* src/dir-ecos.c (jffs2_readdir): move to....
 
* src/jffs2.c (jffs2_fo_dirread): here. And fix the return code
in the process so it now works.
(filldir): Make inline and simpler.
* src/jffs2port.h: remove filldir related stuff.
 
2002-01-24 David Woodhouse <dwmw2@cambridge.redhat.com>
 
* src/dir-ecos.c: Cruftectomy. Remove most of the stuff that was
commented out. Remove jffs2_do_{create,link,unlink} to write.c
* src/write.c: Add jffs2_do_{create,link,unlink} as in mainline.
* src/nodelist.h: Prototypes for the above.
* src/jffs2port.h: Don't include <pkgconf/kernel.h>.
2002-01-23 Jonathan Larmour <jlarmour@redhat.com>
 
* src/jffs2.c (jffs2_mount): Allow multiple FSs, and integration
with flash block device.
(jffs2_flash_read): Use flash block device.
(jffs2_flash_erase): Ditto.
(jffs2_flash_write): Ditto.
(do_flash_init): Remove - now done by block device layer
* src/list.h: Remove and reimplement from scratch to avoid GPL.
* src/os-ecos.h: Keep flash block dev handle in superblock.
eCos does support obsoleting as it isn't NAND only.
* src/dir-ecos.c (jffs2_readdir): Return correct value on success.
Merge in changes mutatis mutandis from between v1.49 and v1.51 of
dir.c in main repository.
* cdl/jffs2.cdl: CYGPKG_MEMALLOC more accurately CYGINT_ISO_MALLOC.
Only jffs2.c needs to be in libextras.a
Requires Flash block devices as an alternative for hardcoding
the sector size, flash size and base address.
* src/super-ecos.c (jffs2_read_super): Use flash block device for
sector and flash sizes.
* tests/fileio1.c: mount using block device (defined by CDL).
No need to init here - done by flash block device layer.
2002-01-21 David Woodhouse <dwmw2@cambridge.redhat.com>
 
* src/read.c: Obtain inode lock around reading symlink target.
* src/dir-ecos.c: Fix the long-standing up() without down() in
jffs2_readdir() when only the '.' and '..' entries are read, from
v1.52 of master dir.c. Merge copyright date change from v1.50 - note
that the portability cleanups from v1.51 aren't yet merged.
* src/os-ecos.h: Add jffs2_can_mark_obsolete() and the macros
for getting at generic inode fields from a struct jffs2_inode_info*
* src/nodemgmt.c: Remove an #ifndef __ECOS and use
jffs2_can_mark_obsolete() instead.
* src/jffs2port.h: up() is cyg_drv_mutex_unlock() not _release()
* src/gc.c: Merge portability cleanups - get rid of a lot of
references to 'struct inode'. Also include the attempt at NAND
support in jffs2_garbage_collect_deletion_dirent().
2002-01-11 David Woodhouse <dwmw2@cambridge.redhat.com>
 
* src/jffs2port.h: Switch semaphore emulation to cyg_drv_mutex_t,
remove some kernel includes which seem gratuitous.
* cdl/jffs2.cdl: Require CYGPKG_MEMALLOC
* src/compr_zlib.c src/compr.c: Merge changes from mainline code
to make mkfs.jffs2 compile again.
2002-01-10 David Woodhouse <dwmw2@cambridge.redhat.com>
 
* src/jffs2.c: The 'mode' arg passed to jffs2_open() shouldn't
have been called 'mode'. It's 'oflags'. You have to make up a
mode for the newly-created file yourself.
* src/nodelist.h src/read.c: Fix jffs2_getlink() so it takes
portable arguments, not a dentry. Move it to read.c and symlink.c
becomes obsolete.
* src/symlink-ecos.c: Removed.
* cdl/jffs2.cdl: Remove symlink-ecos.c
 
2002-01-09 David Woodhouse <dwmw2@cambridge.redhat.com>
 
* Import updated JFFS2 sources into eCos tree.
 
2000-08-28 Dominic Ostrowski (dominic.ostrowski@3glab.com)
 
* started on port of JFFS2 using ramfs as a template
//===========================================================================
//####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####
//===========================================================================
 
/jffs2/v2_0/src/malloc-ecos.c
0,0 → 1,99
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: malloc-ecos.c,v 1.1.1.1 2004-02-14 13:29:20 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include "nodelist.h"
 
struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
{
return malloc(sizeof(struct jffs2_full_dirent) + namesize);
}
 
void jffs2_free_full_dirent(struct jffs2_full_dirent *x)
{
free(x);
}
 
struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
{
return malloc(sizeof(struct jffs2_full_dnode));
}
 
void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
{
free(x);
}
 
struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
{
return malloc(sizeof(struct jffs2_raw_dirent));
}
 
void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
{
free(x);
}
 
struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
{
return malloc(sizeof(struct jffs2_raw_inode));
}
 
void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
{
free(x);
}
 
struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void)
{
return malloc(sizeof(struct jffs2_tmp_dnode_info));
}
 
void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
{
free(x);
}
 
struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
{
return malloc(sizeof(struct jffs2_raw_node_ref));
}
 
void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
{
free(x);
}
 
struct jffs2_node_frag *jffs2_alloc_node_frag(void)
{
return malloc(sizeof(struct jffs2_node_frag));
}
 
void jffs2_free_node_frag(struct jffs2_node_frag *x)
{
free(x);
}
 
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
{
struct jffs2_inode_cache *ret = malloc(sizeof(struct jffs2_inode_cache));
D1(printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
return ret;
}
 
void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
{
D1(printk(KERN_DEBUG "Freeing inocache at %p\n", x));
free(x);
}
 
/jffs2/v2_0/src/build.c
0,0 → 1,258
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: build.c,v 1.1.1.1 2004-02-14 13:29:21 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include "nodelist.h"
 
int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
 
 
#define for_each_inode(i, c, ic) for (i=0; i<INOCACHE_HASHSIZE; i++) for (ic=c->inocache_list[i]; ic; ic=ic->next)
 
/* Scan plan:
- Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
- Scan directory tree from top down, setting nlink in inocaches
- Scan inocaches for inodes with nlink==0
*/
static int jffs2_build_filesystem(struct jffs2_sb_info *c)
{
int ret;
int i;
struct jffs2_inode_cache *ic;
 
/* First, scan the medium and build all the inode caches with
lists of physical nodes */
 
c->flags |= JFFS2_SB_FLAG_MOUNTING;
ret = jffs2_scan_medium(c);
c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
 
if (ret)
return ret;
 
D1(printk(KERN_DEBUG "Scanned flash completely\n"));
D1(jffs2_dump_block_lists(c));
 
/* Now scan the directory tree, increasing nlink according to every dirent found. */
for_each_inode(i, c, ic) {
D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
ret = jffs2_build_inode_pass1(c, ic);
if (ret) {
D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
return ret;
}
cond_resched();
}
D1(printk(KERN_DEBUG "Pass 1 complete\n"));
D1(jffs2_dump_block_lists(c));
 
/* Next, scan for inodes with nlink == 0 and remove them. If
they were directories, then decrement the nlink of their
children too, and repeat the scan. As that's going to be
a fairly uncommon occurrence, it's not so evil to do it this
way. Recursion bad. */
do {
D1(printk(KERN_DEBUG "Pass 2 (re)starting\n"));
ret = 0;
for_each_inode(i, c, ic) {
D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
if (ic->nlink)
continue;
/* XXX: Can get high latency here. Move the cond_resched() from the end of the loop? */
 
ret = jffs2_build_remove_unlinked_inode(c, ic);
if (ret)
break;
/* -EAGAIN means the inode's nlink was zero, so we deleted it,
and furthermore that it had children and their nlink has now
gone to zero too. So we have to restart the scan. */
}
D1(jffs2_dump_block_lists(c));
 
cond_resched();
} while(ret == -EAGAIN);
 
D1(printk(KERN_DEBUG "Pass 2 complete\n"));
/* Finally, we can scan again and free the dirent nodes and scan_info structs */
for_each_inode(i, c, ic) {
struct jffs2_full_dirent *fd;
D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
 
while(ic->scan_dents) {
fd = ic->scan_dents;
ic->scan_dents = fd->next;
jffs2_free_full_dirent(fd);
}
ic->scan_dents = NULL;
cond_resched();
}
D1(printk(KERN_DEBUG "Pass 3 complete\n"));
D1(jffs2_dump_block_lists(c));
 
/* Rotate the lists by some number to ensure wear levelling */
jffs2_rotate_lists(c);
 
return ret;
}
 
int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{
struct jffs2_full_dirent *fd;
 
D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
 
if (ic->ino > c->highest_ino)
c->highest_ino = ic->ino;
 
/* For each child, increase nlink */
for(fd=ic->scan_dents; fd; fd = fd->next) {
struct jffs2_inode_cache *child_ic;
if (!fd->ino)
continue;
/* XXX: Can get high latency here with huge directories */
 
child_ic = jffs2_get_ino_cache(c, fd->ino);
if (!child_ic) {
printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
fd->name, fd->ino, ic->ino);
continue;
}
 
if (child_ic->nlink++ && fd->type == DT_DIR) {
printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
if (fd->ino == 1 && ic->ino == 1) {
printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
}
/* What do we do about it? */
}
D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
/* Can't free them. We might need them in pass 2 */
}
return 0;
}
 
int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *fd;
int ret = 0;
 
D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
jffs2_mark_node_obsolete(c, raw);
}
 
if (ic->scan_dents) {
int whinged = 0;
D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino));
 
while(ic->scan_dents) {
struct jffs2_inode_cache *child_ic;
 
fd = ic->scan_dents;
ic->scan_dents = fd->next;
 
if (!fd->ino) {
/* It's a deletion dirent. Ignore it */
D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name));
jffs2_free_full_dirent(fd);
continue;
}
if (!whinged) {
whinged = 1;
printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
}
 
D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
fd->name, fd->ino));
child_ic = jffs2_get_ino_cache(c, fd->ino);
if (!child_ic) {
printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
jffs2_free_full_dirent(fd);
continue;
}
jffs2_free_full_dirent(fd);
child_ic->nlink--;
}
ret = -EAGAIN;
}
 
/*
We don't delete the inocache from the hash list and free it yet.
The erase code will do that, when all the nodes are completely gone.
*/
 
return ret;
}
 
int jffs2_do_mount_fs(struct jffs2_sb_info *c)
{
int i;
 
c->free_size = c->flash_size;
c->nr_blocks = c->flash_size / c->sector_size;
c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
if (!c->blocks)
return -ENOMEM;
for (i=0; i<c->nr_blocks; i++) {
INIT_LIST_HEAD(&c->blocks[i].list);
c->blocks[i].offset = i * c->sector_size;
c->blocks[i].free_size = c->sector_size;
c->blocks[i].dirty_size = 0;
c->blocks[i].wasted_size = 0;
c->blocks[i].unchecked_size = 0;
c->blocks[i].used_size = 0;
c->blocks[i].first_node = NULL;
c->blocks[i].last_node = NULL;
}
 
init_MUTEX(&c->alloc_sem);
init_MUTEX(&c->erase_free_sem);
init_waitqueue_head(&c->erase_wait);
init_waitqueue_head(&c->inocache_wq);
spin_lock_init(&c->erase_completion_lock);
spin_lock_init(&c->inocache_lock);
 
INIT_LIST_HEAD(&c->clean_list);
INIT_LIST_HEAD(&c->very_dirty_list);
INIT_LIST_HEAD(&c->dirty_list);
INIT_LIST_HEAD(&c->erasable_list);
INIT_LIST_HEAD(&c->erasing_list);
INIT_LIST_HEAD(&c->erase_pending_list);
INIT_LIST_HEAD(&c->erasable_pending_wbuf_list);
INIT_LIST_HEAD(&c->erase_complete_list);
INIT_LIST_HEAD(&c->free_list);
INIT_LIST_HEAD(&c->bad_list);
INIT_LIST_HEAD(&c->bad_used_list);
c->highest_ino = 1;
 
if (jffs2_build_filesystem(c)) {
D1(printk(KERN_DEBUG "build_fs failed\n"));
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
kfree(c->blocks);
return -EIO;
}
return 0;
}
/jffs2/v2_0/src/erase.c
0,0 → 1,415
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: erase.c,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/compiler.h>
#include <linux/crc32.h>
#include <linux/sched.h>
#include <linux/pagemap.h>
#include "nodelist.h"
 
struct erase_priv_struct {
struct jffs2_eraseblock *jeb;
struct jffs2_sb_info *c;
};
#ifndef __ECOS
static void jffs2_erase_callback(struct erase_info *);
#endif
static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 
void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
int ret;
#ifdef __ECOS
ret = jffs2_flash_erase(c, jeb);
if (!ret) {
jffs2_erase_succeeded(c, jeb);
return;
}
#else /* Linux */
struct erase_info *instr;
 
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
if (!instr) {
printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
spin_unlock(&c->erase_completion_lock);
return;
}
 
memset(instr, 0, sizeof(*instr));
 
instr->mtd = c->mtd;
instr->addr = jeb->offset;
instr->len = c->sector_size;
instr->callback = jffs2_erase_callback;
instr->priv = (unsigned long)(&instr[1]);
((struct erase_priv_struct *)instr->priv)->jeb = jeb;
((struct erase_priv_struct *)instr->priv)->c = c;
 
/* NAND , read out the fail counter, if possible */
if (!jffs2_can_mark_obsolete(c))
jffs2_nand_read_failcnt(c,jeb);
ret = c->mtd->erase(c->mtd, instr);
if (!ret)
return;
 
kfree(instr);
#endif /* __ECOS */
 
if (ret == -ENOMEM || ret == -EAGAIN) {
/* Erase failed immediately. Refile it on the list */
D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
spin_unlock(&c->erase_completion_lock);
return;
}
 
if (ret == -EROFS)
printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset);
else
printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
 
/* Note: This is almost identical to jffs2_erase_failed() except
for the fact that we used spin_lock() not spin_lock(). If
we could use spin_lock() from a BH, we could merge them.
Or if we abandon the idea that MTD drivers may call the erase
callback from a BH, I suppose :)
*/
jffs2_erase_failed(c, jeb);
}
 
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
{
struct jffs2_eraseblock *jeb;
 
down(&c->erase_free_sem);
 
spin_lock(&c->erase_completion_lock);
 
while (!list_empty(&c->erase_complete_list) ||
!list_empty(&c->erase_pending_list)) {
 
if (!list_empty(&c->erase_complete_list)) {
jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
list_del(&jeb->list);
spin_unlock(&c->erase_completion_lock);
jffs2_mark_erased_block(c, jeb);
 
} else if (!list_empty(&c->erase_pending_list)) {
jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
list_del(&jeb->list);
c->erasing_size += c->sector_size;
c->free_size -= jeb->free_size;
c->used_size -= jeb->used_size;
c->dirty_size -= jeb->dirty_size;
jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
jffs2_free_all_node_refs(c, jeb);
list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock);
 
jffs2_erase_block(c, jeb);
 
} else {
BUG();
}
 
/* Be nice */
cond_resched();
spin_lock(&c->erase_completion_lock);
}
 
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
 
up(&c->erase_free_sem);
}
 
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add_tail(&jeb->list, &c->erase_complete_list);
spin_unlock(&c->erase_completion_lock);
/* Ensure that kupdated calls us again to mark them clean */
jffs2_erase_pending_trigger(c);
}
 
static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->bad_size += c->sector_size;
list_del(&jeb->list);
list_add(&jeb->list, &c->bad_list);
c->nr_erasing_blocks--;
spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
}
 
#ifndef __ECOS
static void jffs2_erase_callback(struct erase_info *instr)
{
struct erase_priv_struct *priv = (void *)instr->priv;
 
if(instr->state != MTD_ERASE_DONE) {
printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
jffs2_erase_failed(priv->c, priv->jeb);
} else {
jffs2_erase_succeeded(priv->c, priv->jeb);
}
kfree(instr);
}
#endif /* !__ECOS */
 
/* Hmmm. Maybe we should accept the extra space it takes and make
this a standard doubly-linked list? */
static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
struct jffs2_raw_node_ref *ref, struct jffs2_eraseblock *jeb)
{
struct jffs2_inode_cache *ic = NULL;
struct jffs2_raw_node_ref **prev;
 
prev = &ref->next_in_ino;
 
/* Walk the inode's list once, removing any nodes from this eraseblock */
while (1) {
if (!(*prev)->next_in_ino) {
/* We're looking at the jffs2_inode_cache, which is
at the end of the linked list. Stash it and continue
from the beginning of the list */
ic = (struct jffs2_inode_cache *)(*prev);
prev = &ic->nodes;
continue;
}
 
if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
/* It's in the block we're erasing */
struct jffs2_raw_node_ref *this;
 
this = *prev;
*prev = this->next_in_ino;
this->next_in_ino = NULL;
 
if (this == ref)
break;
 
continue;
}
/* Not to be deleted. Skip */
prev = &((*prev)->next_in_ino);
}
 
/* PARANOIA */
if (!ic) {
printk(KERN_WARNING "inode_cache not found in remove_node_refs()!!\n");
return;
}
 
D1(printk(KERN_DEBUG "Removed nodes in range 0x%08x-0x%08x from ino #%u\n",
jeb->offset, jeb->offset + c->sector_size, ic->ino));
 
D2({
int i=0;
struct jffs2_raw_node_ref *this;
printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG);
 
this = ic->nodes;
while(this) {
printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this));
if (++i == 5) {
printk("\n" KERN_DEBUG);
i=0;
}
this = this->next_in_ino;
}
printk("\n");
});
 
if (ic->nodes == (void *)ic) {
D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
jffs2_del_ino_cache(c, ic);
jffs2_free_inode_cache(ic);
}
}
 
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_raw_node_ref *ref;
D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset));
while(jeb->first_node) {
ref = jeb->first_node;
jeb->first_node = ref->next_phys;
/* Remove from the inode-list */
if (ref->next_in_ino)
jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
/* else it was a non-inode node or already removed, so don't bother */
 
jffs2_free_raw_node_ref(ref);
}
jeb->last_node = NULL;
}
 
void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
{
OFNI_BS_2SFFJ(c)->s_dirt = 1;
}
 
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_raw_node_ref *marker_ref = NULL;
unsigned char *ebuf;
size_t retlen;
int ret;
 
if (!jffs2_cleanmarker_oob(c)) {
marker_ref = jffs2_alloc_raw_node_ref();
if (!marker_ref) {
printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
/* Stick it back on the list from whence it came and come back later */
jffs2_erase_pending_trigger(c);
spin_lock(&c->erase_completion_lock);
list_add(&jeb->list, &c->erase_complete_list);
spin_unlock(&c->erase_completion_lock);
return;
}
}
ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ebuf) {
printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
} else {
uint32_t ofs = jeb->offset;
 
D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
while(ofs < jeb->offset + c->sector_size) {
uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
int i;
 
ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
if (ret) {
printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
goto bad;
}
if (retlen != readlen) {
printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
goto bad;
}
for (i=0; i<readlen; i += sizeof(unsigned long)) {
/* It's OK. We know it's properly aligned */
unsigned long datum = *(unsigned long *)(&ebuf[i]);
if (datum + 1) {
printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
bad:
if (!jffs2_cleanmarker_oob(c))
jffs2_free_raw_node_ref(marker_ref);
else
jffs2_write_nand_badblock( c ,jeb );
kfree(ebuf);
bad2:
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->bad_size += c->sector_size;
 
list_add_tail(&jeb->list, &c->bad_list);
c->nr_erasing_blocks--;
spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
return;
}
}
ofs += readlen;
cond_resched();
}
kfree(ebuf);
}
/* Write the erase complete marker */
D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
if (jffs2_cleanmarker_oob(c)) {
 
if (jffs2_write_nand_cleanmarker(c, jeb))
goto bad2;
jeb->first_node = jeb->last_node = NULL;
 
jeb->free_size = c->sector_size;
jeb->used_size = 0;
jeb->dirty_size = 0;
jeb->wasted_size = 0;
} else {
struct jffs2_unknown_node marker = {
.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
.totlen = cpu_to_je32(c->cleanmarker_size)
};
 
marker.hdr_crc = cpu_to_je32(crc32(0, &marker, je32_to_cpu(marker.totlen) - 4));
 
ret = jffs2_flash_write(c, jeb->offset, je32_to_cpu(marker.totlen), &retlen, (char *)&marker);
if (ret) {
printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
jeb->offset, ret);
goto bad2;
}
if (retlen != je32_to_cpu(marker.totlen)) {
printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
jeb->offset, je32_to_cpu(marker.totlen), retlen);
goto bad2;
}
 
marker_ref->next_in_ino = NULL;
marker_ref->next_phys = NULL;
marker_ref->flash_offset = jeb->offset | REF_NORMAL;
marker_ref->totlen = PAD(je32_to_cpu(marker.totlen));
jeb->first_node = jeb->last_node = marker_ref;
jeb->free_size = c->sector_size - marker_ref->totlen;
jeb->used_size = marker_ref->totlen;
jeb->dirty_size = 0;
jeb->wasted_size = 0;
}
 
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->free_size += jeb->free_size;
c->used_size += jeb->used_size;
 
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
 
list_add_tail(&jeb->list, &c->free_list);
c->nr_erasing_blocks--;
c->nr_free_blocks++;
spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
}
 
/jffs2/v2_0/src/fs-ecos.c
0,0 → 1,2050
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by Dominic Ostrowski <dominic.ostrowski@3glab.com>
* Contributors: David Woodhouse, Nick Garnett, Richard Panton.
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: fs-ecos.c,v 1.1.1.1 2004-02-14 13:29:20 phoenix Exp $
*
*/
 
#include <linux/types.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include "jffs2port.h"
#include <linux/jffs2.h>
#include <linux/jffs2_fs_sb.h>
#include <linux/jffs2_fs_i.h>
#include <linux/pagemap.h>
#include "nodelist.h"
 
#include <errno.h>
#include <string.h>
#include <cyg/io/io.h>
#include <cyg/io/config_keys.h>
#include <cyg/io/flash.h>
 
//==========================================================================
// Forward definitions
 
// Filesystem operations
static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte);
static int jffs2_umount(cyg_mtab_entry * mte);
static int jffs2_open(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
int mode, cyg_file * fte);
static int jffs2_ops_unlink(cyg_mtab_entry * mte, cyg_dir dir,
const char *name);
static int jffs2_ops_mkdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name);
static int jffs2_ops_rmdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name);
static int jffs2_ops_rename(cyg_mtab_entry * mte, cyg_dir dir1,
const char *name1, cyg_dir dir2, const char *name2);
static int jffs2_ops_link(cyg_mtab_entry * mte, cyg_dir dir1, const char *name1,
cyg_dir dir2, const char *name2, int type);
static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
cyg_file * fte);
static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
cyg_dir * dir_out);
static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
struct stat *buf);
static int jffs2_getinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
int key, void *buf, int len);
static int jffs2_setinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
int key, void *buf, int len);
 
// File operations
static int jffs2_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int jffs2_fo_lseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence);
static int jffs2_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
CYG_ADDRWORD data);
static int jffs2_fo_fsync(struct CYG_FILE_TAG *fp, int mode);
static int jffs2_fo_close(struct CYG_FILE_TAG *fp);
static int jffs2_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf);
static int jffs2_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
int len);
static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
int len);
 
// Directory operations
static int jffs2_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int jffs2_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(jffs2_fste, "jffs2", 0,
CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILESYSTEM,
jffs2_mount,
jffs2_umount,
jffs2_open,
jffs2_ops_unlink,
jffs2_ops_mkdir,
jffs2_ops_rmdir,
jffs2_ops_rename,
jffs2_ops_link,
jffs2_opendir,
jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo);
 
// -------------------------------------------------------------------------
// File operations.
// This set of file operations are used for normal open files.
 
static cyg_fileops jffs2_fileops = {
jffs2_fo_read,
jffs2_fo_write,
jffs2_fo_lseek,
jffs2_fo_ioctl,
cyg_fileio_seltrue,
jffs2_fo_fsync,
jffs2_fo_close,
jffs2_fo_fstat,
jffs2_fo_getinfo,
jffs2_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 jffs2_dirops = {
jffs2_fo_dirread,
(cyg_fileop_write *) cyg_fileio_enosys,
jffs2_fo_dirlseek,
(cyg_fileop_ioctl *) cyg_fileio_enosys,
cyg_fileio_seltrue,
(cyg_fileop_fsync *) cyg_fileio_enosys,
jffs2_fo_close,
(cyg_fileop_fstat *) cyg_fileio_enosys,
(cyg_fileop_getinfo *) cyg_fileio_enosys,
(cyg_fileop_setinfo *) cyg_fileio_enosys
};
 
//==========================================================================
// STATIC VARIABLES !!!
 
static char read_write_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure
static char gc_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure
 
//==========================================================================
// Directory operations
 
struct jffs2_dirsearch {
struct inode *dir; // directory to search
const char *path; // path to follow
struct inode *node; // Node found
const char *name; // last name fragment used
int namelen; // name fragment length
cyg_bool last; // last name in path?
};
 
typedef struct jffs2_dirsearch jffs2_dirsearch;
 
//==========================================================================
// Ref count and nlink management
 
// -------------------------------------------------------------------------
// dec_refcnt()
// Decrment the reference count on an inode. If this makes the ref count
// zero, then this inode can be freed.
 
static int dec_refcnt(struct inode *node)
{
int err = ENOERR;
node->i_count--;
 
// In JFFS2 inode's are temporary in ram structures that are free'd when the usage i_count drops to 0
// The i_nlink however is managed by JFFS2 and is unrelated to usage
if (node->i_count == 0) {
// This inode is not in use, so delete it.
iput(node);
}
 
return err;
}
 
// FIXME: This seems like real cruft. Wouldn't it be better just to do the
// right thing?
static void icache_evict(struct inode *root_i, struct inode *i)
{
struct inode *cached_inode;
struct inode *next_inode;
 
D2(printf("icache_evict\n"));
// If this is an absolute search path from the root,
// remove all cached inodes with i_count of zero (these are only
// held where needed for dotdot filepaths)
if (i == root_i) {
for (cached_inode = root_i; cached_inode != NULL;
cached_inode = next_inode) {
next_inode = cached_inode->i_cache_next;
if (cached_inode->i_count == 0) {
cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next; // Prveious entry points ahead of us
if (cached_inode->i_cache_next != NULL)
cached_inode->i_cache_next->i_cache_prev = cached_inode->i_cache_prev; // Next entry points behind us
jffs2_clear_inode(cached_inode);
D2(printf
("free icache_evict inode %x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n",
cached_inode));
free(cached_inode);
}
}
}
}
 
//==========================================================================
// Directory search
 
// -------------------------------------------------------------------------
// init_dirsearch()
// Initialize a dirsearch object to start a search
 
static void init_dirsearch(jffs2_dirsearch * ds,
struct inode *dir, const char *name)
{
D2(printf("init_dirsearch name = %s\n", name));
D2(printf("init_dirsearch dir = %x\n", dir));
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(jffs2_dirsearch * ds)
{
unsigned long hash;
struct qstr this;
unsigned int c;
const char *hashname;
 
struct inode *dir = ds->dir;
const char *name = ds->path;
const char *n = name;
char namelen = 0;
struct inode *d;
 
D2(printf("find_entry\n"));
 
// check that we really have a directory
if (!S_ISDIR(dir->i_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;
 
if (name[0] == '.')
switch (namelen) {
default:
break;
case 2:
// Dot followed by not Dot, treat as any other name
if (name[1] != '.')
break;
// Dot Dot
// Move back up the search path
D2(printf("find_entry found ..\n"));
ds->node = ds->dir->i_parent;
if (ds->dir->i_count == 0) {
iput(ds->dir); // This inode may be evicted
ds->dir = NULL;
}
return ENOERR;
case 1:
// Dot is consumed
D2(printf("find_entry found .\n"));
ds->node = ds->dir;
return ENOERR;
}
// Here we have the name and its length set up.
// Search the directory for a matching entry
 
hashname = name;
this.name = hashname;
c = *(const unsigned char *) hashname;
 
hash = init_name_hash();
do {
hashname++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *) hashname;
} while (c && (c != '/'));
this.len = hashname - (const char *) this.name;
this.hash = end_name_hash(hash);
 
D2(printf("find_entry for name = %s\n", ds->path));
d = jffs2_lookup(dir, &this);
D2(printf("find_entry got dir = %x\n", d));
 
if (d == NULL)
return ENOENT;
 
// The back path for dotdot to follow
d->i_parent = dir;
// pass back the node we have found
ds->node = d;
 
return ENOERR;
 
}
 
// -------------------------------------------------------------------------
// jffs2_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 jffs2_find(jffs2_dirsearch * d)
{
int err;
 
D2(printf("jffs2_find for path =%s\n", d->path));
// 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;
 
// every inode traversed in the find is temporary and should be free'd
//iput(d->dir);
 
// 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 jffs2_pathconf(struct inode *node, struct cyg_pathconf_info *info)
{
int err = ENOERR;
D2(printf("jffs2_pathconf\n"));
 
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
 
// -------------------------------------------------------------------------
// jffs2_mount()
// Process a mount request. This mainly creates a root for the
// filesystem.
static int jffs2_read_super(struct super_block *sb)
{
struct jffs2_sb_info *c;
struct inode *root_i;
Cyg_ErrNo err;
cyg_uint32 len;
cyg_io_flash_getconfig_devsize_t ds;
cyg_io_flash_getconfig_blocksize_t bs;
 
D1(printk(KERN_DEBUG "jffs2: read_super\n"));
 
c = JFFS2_SB_INFO(sb);
 
len = sizeof (ds);
err = cyg_io_get_config(sb->s_dev,
CYG_IO_GET_CONFIG_FLASH_DEVSIZE, &ds, &len);
if (err != ENOERR) {
D1(printf
("jffs2: cyg_io_get_config failed to get dev size: %d\n",
err));
return err;
}
len = sizeof (bs);
bs.offset = 0;
err = cyg_io_get_config(sb->s_dev,
CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE, &bs, &len);
if (err != ENOERR) {
D1(printf
("jffs2: cyg_io_get_config failed to get block size: %d\n",
err));
return err;
}
 
c->sector_size = bs.block_size;
c->flash_size = ds.dev_size;
c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
 
err = jffs2_do_mount_fs(c);
if (err)
return -err;
 
D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
root_i = iget(sb, 1);
if (is_bad_inode(root_i)) {
D1(printk(KERN_WARNING "get root inode failed\n"));
err = EIO;
goto out_nodes;
}
 
D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
sb->s_root = d_alloc_root(root_i);
if (!sb->s_root) {
err = ENOMEM;
goto out_root_i;
}
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = JFFS2_SUPER_MAGIC;
 
return 0;
 
out_root_i:
iput(root_i);
out_nodes:
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
free(c->blocks);
 
return err;
}
 
static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte)
{
extern cyg_mtab_entry mtab[], mtab_end;
struct super_block *jffs2_sb = NULL;
struct jffs2_sb_info *c;
cyg_mtab_entry *m;
cyg_io_handle_t t;
Cyg_ErrNo err;
 
D2(printf("jffs2_mount\n"));
 
err = cyg_io_lookup(mte->devname, &t);
if (err != ENOERR)
return -err;
 
// Iterate through the mount table to see if we're mounted
// FIXME: this should be done better - perhaps if the superblock
// can be stored as an inode in the icache.
for (m = &mtab[0]; m != &mtab_end; m++) {
// stop if there are more than the configured maximum
if (m - &mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) {
m = &mtab_end;
break;
}
if (m->valid && strcmp(m->fsname, "jffs2") == 0 &&
strcmp(m->devname, mte->devname) == 0) {
jffs2_sb = (struct super_block *) m->data;
}
}
 
if (jffs2_sb == NULL) {
jffs2_sb = malloc(sizeof (struct super_block));
 
if (jffs2_sb == NULL)
return ENOMEM;
 
c = JFFS2_SB_INFO(jffs2_sb);
memset(jffs2_sb, 0, sizeof (struct super_block));
jffs2_sb->s_dev = t;
 
c->inocache_list = malloc(sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE);
if (!c->inocache_list) {
free(jffs2_sb);
return ENOMEM;
}
memset(c->inocache_list, 0, sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE);
 
err = jffs2_read_super(jffs2_sb);
 
if (err) {
free(jffs2_sb);
free(c->inocache_list);
return err;
}
 
jffs2_sb->s_root->i_parent = jffs2_sb->s_root; // points to itself, no dotdot paths above mountpoint
jffs2_sb->s_root->i_cache_prev = NULL; // root inode, so always null
jffs2_sb->s_root->i_cache_next = NULL;
jffs2_sb->s_root->i_count = 1; // Ensures the root inode is always in ram until umount
 
D2(printf("jffs2_mount erasing pending blocks\n"));
jffs2_erase_pending_blocks(c);
}
mte->data = (CYG_ADDRWORD) jffs2_sb;
 
jffs2_sb->s_mount_count++;
mte->root = (cyg_dir) jffs2_sb->s_root;
D2(printf("jffs2_mounted superblock at %x\n", mte->root));
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_umount()
// Unmount the filesystem.
 
static int jffs2_umount(cyg_mtab_entry * mte)
{
struct inode *root = (struct inode *) mte->root;
struct super_block *jffs2_sb = root->i_sb;
struct jffs2_sb_info *c = JFFS2_SB_INFO(jffs2_sb);
 
D2(printf("jffs2_umount\n"));
 
// Decrement the mount count
jffs2_sb->s_mount_count--;
 
// Only really umount if this is the only mount
if (jffs2_sb->s_mount_count == 0) {
 
// Check for open/inuse root or any cached inodes
//if( root->i_count != 1 || root->i_cache_next != NULL) // root icount was set to 1 on mount
if (root->i_cache_next != NULL) // root icount was set to 1 on mount
return EBUSY;
 
dec_refcnt(root); // Time to free the root inode
 
//Clear root inode
//root_i = NULL;
 
// Clean up the super block and root inode
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
free(c->blocks);
free(c->inocache_list);
free(jffs2_sb);
// Clear root pointer
mte->root = CYG_DIR_NULL;
mte->fs->data = 0; // fstab entry, visible to all mounts. No current mount
// That's all folks.
D2(printf("jffs2_umount No current mounts\n"));
}
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_open()
// Open a file for reading or writing.
 
static int jffs2_open(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
int mode, cyg_file * file)
{
 
jffs2_dirsearch ds;
struct inode *node = NULL;
int err;
 
D2(printf("jffs2_open\n"));
 
icache_evict((struct inode *) mte->root, (struct inode *) dir);
 
init_dirsearch(&ds, (struct inode *) dir, name);
 
err = jffs2_find(&ds);
 
if (err == ENOENT) {
if (ds.last && (mode & O_CREAT)) {
unsigned long hash;
struct qstr this;
unsigned int c;
const char *hashname;
 
// 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.
 
hashname = ds.name;
this.name = hashname;
c = *(const unsigned char *) hashname;
 
hash = init_name_hash();
do {
hashname++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *) hashname;
} while (c && (c != '/'));
this.len = hashname - (const char *) this.name;
this.hash = end_name_hash(hash);
 
err = jffs2_create(ds.dir, &this, S_IRUGO|S_IXUGO|S_IWUSR|S_IFREG, &node);
 
if (err != 0) {
//Possible orphaned inode on the flash - but will be gc'd
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)) {
struct jffs2_inode_info *f = JFFS2_INODE_INFO(node);
struct jffs2_sb_info *c = JFFS2_SB_INFO(node->i_sb);
// If the O_TRUNC bit is set we must clean out the file data.
 
node->i_size = 0;
jffs2_truncate_fraglist(c, &f->fragtree, 0);
// Update file times
node->i_ctime = node->i_mtime = cyg_timestamp();
}
 
if (err != ENOERR)
return err;
 
// Check that we actually have a file here
if (S_ISDIR(node->i_mode))
return EISDIR;
 
node->i_count++; // 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 = &jffs2_fileops;
file->f_offset = (mode & O_APPEND) ? node->i_size : 0;
file->f_data = (CYG_ADDRWORD) node;
file->f_xops = 0;
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_ops_unlink()
// Remove a file link from its directory.
 
static int jffs2_ops_unlink(cyg_mtab_entry * mte, cyg_dir dir, const char *name)
{
unsigned long hash;
struct qstr this;
unsigned int c;
const char *hashname;
jffs2_dirsearch ds;
int err;
 
D2(printf("jffs2_ops_unlink\n"));
 
icache_evict((struct inode *) mte->root, (struct inode *) dir);
 
init_dirsearch(&ds, (struct inode *) dir, name);
 
err = jffs2_find(&ds);
 
if (err != ENOERR)
return err;
 
// Cannot unlink directories, use rmdir() instead
if (S_ISDIR(ds.node->i_mode))
return EPERM;
 
// Delete it from its directory
 
hashname = ds.name;
this.name = hashname;
c = *(const unsigned char *) hashname;
 
hash = init_name_hash();
do {
hashname++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *) hashname;
} while (c && (c != '/'));
this.len = hashname - (const char *) this.name;
this.hash = end_name_hash(hash);
 
err = jffs2_unlink(ds.dir, ds.node, &this);
 
return err;
}
 
// -------------------------------------------------------------------------
// jffs2_ops_mkdir()
// Create a new directory.
 
static int jffs2_ops_mkdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name)
{
jffs2_dirsearch ds;
struct inode *node = NULL;
int err;
 
D2(printf("jffs2_ops_mkdir\n"));
 
icache_evict((struct inode *) mte->root, (struct inode *) dir);
 
init_dirsearch(&ds, (struct inode *) dir, name);
 
err = jffs2_find(&ds);
 
if (err == ENOENT) {
if (ds.last) {
unsigned long hash;
struct qstr this;
unsigned int c;
const char *hashname;
// The entry does not exist, and it is the last element in
// the pathname, so we can create it here.
 
hashname = ds.name;
this.name = hashname;
c = *(const unsigned char *) hashname;
 
hash = init_name_hash();
do {
hashname++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *) hashname;
} while (c && (c != '/'));
this.len = hashname - (const char *) this.name;
this.hash = end_name_hash(hash);
 
err = jffs2_mkdir(ds.dir, &this, 0, &node);
 
if (err != 0)
return ENOSPC;
 
}
// 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;
}
 
// -------------------------------------------------------------------------
// jffs2_ops_rmdir()
// Remove a directory.
 
static int jffs2_ops_rmdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name)
{
unsigned long hash;
struct qstr this;
unsigned int c;
const char *hashname;
jffs2_dirsearch ds;
int err;
 
D2(printf("jffs2_ops_rmdir\n"));
 
icache_evict((struct inode *) mte->root, (struct inode *) dir);
 
init_dirsearch(&ds, (struct inode *) dir, name);
 
err = jffs2_find(&ds);
 
if (err != ENOERR)
return err;
 
// Check that this is actually a directory.
if (!S_ISDIR(ds.node->i_mode))
return EPERM;
 
// Delete the entry.
hashname = ds.name;
this.name = hashname;
c = *(const unsigned char *) hashname;
 
hash = init_name_hash();
do {
hashname++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *) hashname;
} while (c && (c != '/'));
this.len = hashname - (const char *) this.name;
this.hash = end_name_hash(hash);
 
err = jffs2_rmdir(ds.dir, ds.node, &this);
 
return err;
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_ops_rename()
// Rename a file/dir.
 
static int jffs2_ops_rename(cyg_mtab_entry * mte, cyg_dir dir1,
const char *name1, cyg_dir dir2, const char *name2)
{
unsigned long hash;
struct qstr this1, this2;
unsigned int c;
const char *hashname;
jffs2_dirsearch ds1, ds2;
int err;
 
D2(printf("jffs2_ops_rename\n"));
 
init_dirsearch(&ds1, (struct inode *) dir1, name1);
 
err = jffs2_find(&ds1);
 
if (err != ENOERR)
return err;
 
init_dirsearch(&ds2, (struct inode *) dir2, name2);
 
err = jffs2_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;
 
hashname = ds1.name;
this1.name = hashname;
c = *(const unsigned char *) hashname;
 
hash = init_name_hash();
do {
hashname++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *) hashname;
} while (c && (c != '/'));
this1.len = hashname - (const char *) this1.name;
this1.hash = end_name_hash(hash);
 
hashname = ds2.name;
this2.name = hashname;
c = *(const unsigned char *) hashname;
 
hash = init_name_hash();
do {
hashname++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *) hashname;
} while (c && (c != '/'));
this2.len = hashname - (const char *) this2.name;
this2.hash = end_name_hash(hash);
 
// 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->i_mode) && S_ISDIR(ds2.node->i_mode))
return EISDIR;
 
if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode))
return ENOTDIR;
 
// Now delete the destination directory entry
 
err = jffs2_unlink(ds2.dir, ds2.node, &this2);
 
if (err != 0)
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 = jffs2_rename(ds1.dir, ds1.node, &this1, ds2.dir, &this2);
 
// Update directory times
if (err == 0)
ds1.dir->i_ctime =
ds1.dir->i_mtime =
ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
 
return err;
}
 
// -------------------------------------------------------------------------
// jffs2_ops_link()
// Make a new directory entry for a file.
 
static int jffs2_ops_link(cyg_mtab_entry * mte, cyg_dir dir1, const char *name1,
cyg_dir dir2, const char *name2, int type)
{
unsigned long hash;
struct qstr this;
unsigned int c;
const char *hashname;
jffs2_dirsearch ds1, ds2;
int err;
 
D2(printf("jffs2_ops_link\n"));
 
// Only do hard links for now in this filesystem
if (type != CYG_FSLINK_HARD)
return EINVAL;
 
init_dirsearch(&ds1, (struct inode *) dir1, name1);
 
err = jffs2_find(&ds1);
 
if (err != ENOERR)
return err;
 
init_dirsearch(&ds2, (struct inode *) dir2, name2);
 
err = jffs2_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.
 
hashname = ds2.name;
this.name = hashname;
c = *(const unsigned char *) hashname;
 
hash = init_name_hash();
do {
hashname++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *) hashname;
} while (c && (c != '/'));
this.len = hashname - (const char *) this.name;
this.hash = end_name_hash(hash);
 
err = jffs2_link(ds2.dir, ds1.node, &this);
 
if (err == 0)
ds1.node->i_ctime =
ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp();
 
return err;
}
 
// -------------------------------------------------------------------------
// jffs2_opendir()
// Open a directory for reading.
 
static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
cyg_file * file)
{
jffs2_dirsearch ds;
int err;
 
D2(printf("jffs2_opendir\n"));
 
icache_evict((struct inode *) mte->root, (struct inode *) dir);
 
init_dirsearch(&ds, (struct inode *) dir, name);
 
err = jffs2_find(&ds);
 
if (err != ENOERR)
return err;
 
// check it is really a directory.
if (!S_ISDIR(ds.node->i_mode))
return ENOTDIR;
 
ds.node->i_count++; // 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 = &jffs2_dirops;
file->f_offset = 0;
file->f_data = (CYG_ADDRWORD) ds.node;
file->f_xops = 0;
 
return ENOERR;
 
}
 
// -------------------------------------------------------------------------
// jffs2_chdir()
// Change directory support.
 
static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
cyg_dir * dir_out)
{
D2(printf("jffs2_chdir\n"));
 
if (dir_out != NULL) {
// This is a request to get a new directory pointer in
// *dir_out.
 
jffs2_dirsearch ds;
int err;
 
icache_evict((struct inode *) mte->root, (struct inode *) dir);
 
init_dirsearch(&ds, (struct inode *) dir, name);
 
err = jffs2_find(&ds);
 
if (err != ENOERR)
return err;
 
// check it is a directory
if (!S_ISDIR(ds.node->i_mode))
return ENOTDIR;
 
// Increment ref count to keep this directory in existance
// while it is the current cdir.
ds.node->i_count++;
 
// 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.
 
struct inode *node = (struct inode *) dir;
 
// Just decrement directory reference count.
dec_refcnt(node);
}
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_stat()
// Get struct stat info for named object.
 
static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
struct stat *buf)
{
jffs2_dirsearch ds;
int err;
 
D2(printf("jffs2_stat\n"));
 
icache_evict((struct inode *) mte->root, (struct inode *) dir);
 
init_dirsearch(&ds, (struct inode *) dir, name);
 
err = jffs2_find(&ds);
 
if (err != ENOERR)
return err;
 
// Fill in the status
buf->st_mode = ds.node->i_mode;
buf->st_ino = (ino_t) ds.node;
buf->st_dev = 0;
buf->st_nlink = ds.node->i_nlink;
buf->st_uid = 0;
buf->st_gid = 0;
buf->st_size = ds.node->i_size;
buf->st_atime = ds.node->i_atime;
buf->st_mtime = ds.node->i_mtime;
buf->st_ctime = ds.node->i_ctime;
 
return err;
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_getinfo()
// Getinfo. Currently only support pathconf().
 
static int jffs2_getinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
int key, void *buf, int len)
{
jffs2_dirsearch ds;
int err;
 
D2(printf("jffs2_getinfo\n"));
 
icache_evict((struct inode *) mte->root, (struct inode *) dir);
 
init_dirsearch(&ds, (struct inode *) dir, name);
 
err = jffs2_find(&ds);
 
if (err != ENOERR)
return err;
 
switch (key) {
case FS_INFO_CONF:
err = jffs2_pathconf(ds.node, (struct cyg_pathconf_info *) buf);
break;
 
default:
err = EINVAL;
}
return err;
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_setinfo()
// Setinfo. Nothing to support here at present.
 
static int jffs2_setinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name,
int key, void *buf, int len)
{
// No setinfo keys supported at present
 
D2(printf("jffs2_setinfo\n"));
 
return EINVAL;
}
 
//==========================================================================
// File operations
 
// -------------------------------------------------------------------------
// jffs2_fo_read()
// Read data from the file.
 
static int jffs2_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
{
struct inode *inode = (struct inode *) fp->f_data;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
int i;
ssize_t resid = uio->uio_resid;
off_t pos = fp->f_offset;
 
down(&f->sem);
 
// Loop over the io vectors until there are none left
for (i = 0; i < uio->uio_iovcnt && pos < inode->i_size; i++) {
int ret;
cyg_iovec *iov = &uio->uio_iov[i];
off_t len = min(iov->iov_len, inode->i_size - pos);
 
D2(printf("jffs2_fo_read inode size %d\n", inode->i_size));
 
ret =
jffs2_read_inode_range(c, f,
(unsigned char *) iov->iov_base, pos,
len);
if (ret) {
D1(printf
("jffs2_fo_read(): read_inode_range failed %d\n",
ret));
uio->uio_resid = resid;
up(&f->sem);
return -ret;
}
resid -= len;
pos += len;
}
 
// We successfully read some data, update the node's access time
// and update the file offset and transfer residue.
 
inode->i_atime = cyg_timestamp();
 
uio->uio_resid = resid;
fp->f_offset = pos;
 
up(&f->sem);
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_fo_write()
// Write data to file.
 
static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
{
struct page write_page;
off_t page_start_pos;
struct inode *node = (struct inode *) fp->f_data;
off_t pos = fp->f_offset;
ssize_t resid = uio->uio_resid;
int i;
 
memset(&read_write_buffer, 0, PAGE_CACHE_SIZE);
write_page.virtual = &read_write_buffer;
 
// 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->i_size;
 
// Check that pos is within current file size, or at the very end.
if (pos < 0 || pos > node->i_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;
size_t writtenlen;
off_t l = len;
int err;
 
write_page.index = 0;
 
page_start_pos = pos;
while (page_start_pos >= (PAGE_CACHE_SIZE)) {
write_page.index++;
page_start_pos -= PAGE_CACHE_SIZE;
}
 
if (l > PAGE_CACHE_SIZE - page_start_pos)
l = PAGE_CACHE_SIZE - page_start_pos;
 
D2(printf
("jffs2_fo_write write_page.index %d\n",
write_page.index));
D2(printf
("jffs2_fo_write page_start_pos %d\n",
page_start_pos));
D2(printf("jffs2_fo_write transfer size %d\n", l));
 
err =
jffs2_prepare_write(node, &write_page,
page_start_pos,
page_start_pos + l);
 
if (err != 0)
return err;
 
// copy data in
memcpy(&read_write_buffer[page_start_pos], buf, l);
 
writtenlen =
jffs2_commit_write(node, &write_page,
page_start_pos,
page_start_pos + l);
 
if (writtenlen != l)
return ENOSPC;
 
// 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->i_mtime = node->i_ctime = cyg_timestamp();
if (pos > node->i_size)
node->i_size = pos;
 
uio->uio_resid = resid;
fp->f_offset = pos;
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_fo_lseek()
// Seek to a new file position.
 
static int jffs2_fo_lseek(struct CYG_FILE_TAG *fp, off_t * apos, int whence)
{
struct inode *node = (struct inode *) fp->f_data;
off_t pos = *apos;
 
D2(printf("jffs2_fo_lseek\n"));
 
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->i_size;
break;
 
default:
return EINVAL;
}
 
// Check that pos is still within current file size, or at the
// very end.
if (pos < 0 || pos > node->i_size)
return EINVAL;
 
// All OK, set fp offset and return new position.
*apos = fp->f_offset = pos;
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_fo_ioctl()
// Handle ioctls. Currently none are defined.
 
static int jffs2_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
CYG_ADDRWORD data)
{
// No Ioctls currenly defined.
 
D2(printf("jffs2_fo_ioctl\n"));
 
return EINVAL;
}
 
// -------------------------------------------------------------------------
// jffs2_fo_fsync().
// Force the file out to data storage.
 
static int jffs2_fo_fsync(struct CYG_FILE_TAG *fp, int mode)
{
// Data is always permanently where it belongs, nothing to do
// here.
 
D2(printf("jffs2_fo_fsync\n"));
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_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 jffs2_fo_close(struct CYG_FILE_TAG *fp)
{
struct inode *node = (struct inode *) fp->f_data;
 
D2(printf("jffs2_fo_close\n"));
 
dec_refcnt(node);
 
fp->f_data = 0; // zero data pointer
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
//jffs2_fo_fstat()
// Get file status.
 
static int jffs2_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf)
{
struct inode *node = (struct inode *) fp->f_data;
 
D2(printf("jffs2_fo_fstat\n"));
 
// Fill in the status
buf->st_mode = node->i_mode;
buf->st_ino = (ino_t) node;
buf->st_dev = 0;
buf->st_nlink = node->i_nlink;
buf->st_uid = 0;
buf->st_gid = 0;
buf->st_size = node->i_size;
buf->st_atime = node->i_atime;
buf->st_mtime = node->i_mtime;
buf->st_ctime = node->i_ctime;
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_fo_getinfo()
// Get info. Currently only supports fpathconf().
 
static int jffs2_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
int len)
{
struct inode *node = (struct inode *) fp->f_data;
int err;
 
D2(printf("jffs2_fo_getinfo\n"));
 
switch (key) {
case FS_INFO_CONF:
err = jffs2_pathconf(node, (struct cyg_pathconf_info *) buf);
break;
 
default:
err = EINVAL;
}
return err;
 
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_fo_setinfo()
// Set info. Nothing supported here.
 
static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf,
int len)
{
// No setinfo key supported at present
 
D2(printf("jffs2_fo_setinfo\n"));
 
return ENOERR;
}
 
//==========================================================================
// Directory operations
 
// -------------------------------------------------------------------------
// jffs2_fo_dirread()
// Read a single directory entry from a file.
 
static __inline void filldir(char *nbuf, int nlen, const char *name, int namlen)
{
int len = nlen < namlen ? nlen : namlen;
memcpy(nbuf, name, len);
nbuf[len] = '\0';
}
 
static int jffs2_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
{
struct inode *d_inode = (struct inode *) fp->f_data;
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;
struct jffs2_inode_info *f;
struct jffs2_sb_info *c;
struct inode *inode = d_inode;
struct jffs2_full_dirent *fd;
unsigned long offset, curofs;
int found = 1;
 
if (len < sizeof (struct dirent))
return EINVAL;
 
D1(printk
(KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", d_inode->i_ino));
 
f = JFFS2_INODE_INFO(inode);
c = JFFS2_SB_INFO(inode->i_sb);
 
offset = fp->f_offset;
 
if (offset == 0) {
D1(printk
(KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino));
filldir(nbuf, nlen, ".", 1);
goto out;
}
if (offset == 1) {
filldir(nbuf, nlen, "..", 2);
goto out;
}
 
curofs = 1;
down(&f->sem);
for (fd = f->dents; fd; fd = fd->next) {
 
curofs++;
/* First loop: curofs = 2; offset = 2 */
if (curofs < offset) {
D2(printk
(KERN_DEBUG
"Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n",
fd->name, fd->ino, fd->type, curofs, offset));
continue;
}
if (!fd->ino) {
D2(printk
(KERN_DEBUG "Skipping deletion dirent \"%s\"\n",
fd->name));
offset++;
continue;
}
D2(printk
(KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset,
fd->name, fd->ino, fd->type));
filldir(nbuf, nlen, fd->name, strlen(fd->name));
goto out_sem;
}
/* Reached the end of the directory */
found = 0;
out_sem:
up(&f->sem);
out:
fp->f_offset = ++offset;
if (found) {
uio->uio_resid -= sizeof (struct dirent);
}
return ENOERR;
}
 
// -------------------------------------------------------------------------
// jffs2_fo_dirlseek()
// Seek directory to start.
 
static int jffs2_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence)
{
// Only allow SEEK_SET to zero
 
D2(printf("jffs2_fo_dirlseek\n"));
 
if (whence != SEEK_SET || *pos != 0)
return EINVAL;
 
*pos = fp->f_offset = 0;
 
return ENOERR;
}
 
//==========================================================================
//
// Called by JFFS2
// ===============
//
//
//==========================================================================
 
struct page *read_cache_page(unsigned long index,
int (*filler) (void *, struct page *), void *data)
{
// Only called in gc.c jffs2_garbage_collect_dnode
// but gets a real page for the specified inode
 
int err;
struct page *gc_page = malloc(sizeof (struct page));
 
printf("read_cache_page\n");
memset(&gc_buffer, 0, PAGE_CACHE_SIZE);
 
if (gc_page != NULL) {
gc_page->virtual = &gc_buffer;
gc_page->index = index;
 
err = filler(data, gc_page);
if (err < 0) {
free(gc_page);
gc_page = NULL;
}
}
 
return gc_page;
}
 
void page_cache_release(struct page *pg)
{
 
// Only called in gc.c jffs2_garbage_collect_dnode
// but should free the page malloc'd by read_cache_page
 
printf("page_cache_release\n");
free(pg);
}
 
struct inode *new_inode(struct super_block *sb)
{
 
// Only called in write.c jffs2_new_inode
// Always adds itself to inode cache
 
struct inode *inode;
struct inode *cached_inode;
 
inode = malloc(sizeof (struct inode));
if (inode == NULL)
return 0;
 
D2(printf
("malloc new_inode %x ####################################\n",
inode));
 
memset(inode, 0, sizeof (struct inode));
inode->i_sb = sb;
inode->i_ino = 1;
inode->i_count = 0; //1; // Let ecos manage the open count
 
inode->i_nlink = 1; // Let JFFS2 manage the link count
inode->i_size = 0;
 
inode->i_cache_next = NULL; // Newest inode, about to be cached
 
// Add to the icache
for (cached_inode = sb->s_root; cached_inode != NULL;
cached_inode = cached_inode->i_cache_next) {
if (cached_inode->i_cache_next == NULL) {
cached_inode->i_cache_next = inode; // Current last in cache points to newcomer
inode->i_cache_prev = cached_inode; // Newcomer points back to last
break;
}
}
 
return inode;
}
 
struct inode *iget(struct super_block *sb, cyg_uint32 ino)
{
 
// Substitute for iget drops straight through to reading the
// inode from disk if it is not in the inode cache
 
// Called in super.c jffs2_read_super, dir.c jffs2_lookup,
// and gc.c jffs2_garbage_collect_pass
 
// Must first check for cached inode
// If this fails let new_inode create one
 
struct inode *inode;
 
D2(printf("iget\n"));
 
// Check for this inode in the cache
for (inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) {
if (inode->i_ino == ino)
return inode;
}
inode = NULL;
 
// Not cached, so malloc it
inode = new_inode(sb);
if (inode == NULL)
return 0;
 
inode->i_ino = ino;
jffs2_read_inode(inode);
 
return inode;
}
 
void iput(struct inode *i)
{
 
// Called in dec_refcnt, jffs2_find
// (and jffs2_open and jffs2_ops_mkdir?)
// super.c jffs2_read_super,
// and gc.c jffs2_garbage_collect_pass
 
struct inode *cached_inode;
 
D2(printf
("free iput inode %x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n", i));
if (i && i->i_count) {
/* Added by dwmw2. iget/iput in Linux track the use count,
don't just unconditionally free it */
printf("iput called for used inode\n");
return;
}
if (i != NULL) {
// Remove from the icache
for (cached_inode = i->i_sb->s_root; cached_inode != NULL;
cached_inode = cached_inode->i_cache_next) {
if (cached_inode == i) {
cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next; // Prveious entry points ahead of us
if (cached_inode->i_cache_next != NULL)
cached_inode->i_cache_next->i_cache_prev = cached_inode->i_cache_prev; // Next entry points behind us
break;
}
}
// inode has been seperated from the cache
jffs2_clear_inode(i);
free(i);
}
}
 
static int return_EIO(void)
{
return -EIO;
}
 
#define EIO_ERROR ((void *) (return_EIO))
 
void make_bad_inode(struct inode *inode)
{
 
// In readinode.c JFFS2 checks whether the inode has appropriate
// content for its marked type
 
D2(printf("make_bad_inode\n"));
 
inode->i_mode = S_IFREG;
inode->i_atime = inode->i_mtime = inode->i_ctime = cyg_timestamp();
inode->i_op = EIO_ERROR;
inode->i_fop = EIO_ERROR;
}
 
int is_bad_inode(struct inode *inode)
{
 
// Called in super.c jffs2_read_super,
// and gc.c jffs2_garbage_collect_pass
 
D2(printf("is_bad_inode\n"));
 
return (inode->i_op == EIO_ERROR);
/*if(i == NULL)
return 1;
return 0; */
}
 
cyg_bool jffs2_flash_read(struct jffs2_sb_info * c,
cyg_uint32 read_buffer_offset, const size_t size,
size_t * return_size, char *write_buffer)
{
Cyg_ErrNo err;
cyg_uint32 len = size;
struct super_block *sb = OFNI_BS_2SFFJ(c);
 
//D2(printf("FLASH READ\n"));
//D2(printf("read address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + read_buffer_offset));
//D2(printf("write address = %x\n", write_buffer));
//D2(printf("size = %x\n", size));
err = cyg_io_bread(sb->s_dev, write_buffer, &len, read_buffer_offset);
 
*return_size = (size_t) len;
return (err != ENOERR);
}
 
cyg_bool jffs2_flash_write(struct jffs2_sb_info * c,
cyg_uint32 write_buffer_offset, const size_t size,
size_t * return_size, char *read_buffer)
{
 
Cyg_ErrNo err;
cyg_uint32 len = size;
struct super_block *sb = OFNI_BS_2SFFJ(c);
 
// D2(printf("FLASH WRITE ENABLED!!!\n"));
// D2(printf("write address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + write_buffer_offset));
// D2(printf("read address = %x\n", read_buffer));
// D2(printf("size = %x\n", size));
 
err = cyg_io_bwrite(sb->s_dev, read_buffer, &len, write_buffer_offset);
*return_size = (size_t) len;
 
return (err != ENOERR);
}
 
int
jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
unsigned long count, loff_t to, size_t * retlen)
{
unsigned long i;
size_t totlen = 0, thislen;
int ret = 0;
 
for (i = 0; i < count; i++) {
// writes need to be aligned but the data we're passed may not be
// Observation suggests most unaligned writes are small, so we
// optimize for that case.
 
if (((vecs[i].iov_len & (sizeof (int) - 1))) ||
(((unsigned long) vecs[i].
iov_base & (sizeof (unsigned long) - 1)))) {
// are there iov's after this one? Or is it so much we'd need
// to do multiple writes anyway?
if ((i + 1) < count || vecs[i].iov_len > 256) {
// cop out and malloc
unsigned long j;
ssize_t sizetomalloc = 0, totvecsize = 0;
char *cbuf, *cbufptr;
 
for (j = i; j < count; j++)
totvecsize += vecs[j].iov_len;
 
// pad up in case unaligned
sizetomalloc = totvecsize + sizeof (int) - 1;
sizetomalloc &= ~(sizeof (int) - 1);
cbuf = (char *) malloc(sizetomalloc);
// malloc returns aligned memory
if (!cbuf) {
ret = -ENOMEM;
goto writev_out;
}
cbufptr = cbuf;
for (j = i; j < count; j++) {
memcpy(cbufptr, vecs[j].iov_base,
vecs[j].iov_len);
cbufptr += vecs[j].iov_len;
}
ret =
jffs2_flash_write(c, to, sizetomalloc,
&thislen, cbuf);
if (thislen > totvecsize) // in case it was aligned up
thislen = totvecsize;
totlen += thislen;
free(cbuf);
goto writev_out;
} else {
// otherwise optimize for the common case
int buf[256 / sizeof (int)]; // int, so int aligned
size_t lentowrite;
 
lentowrite = vecs[i].iov_len;
// pad up in case its unaligned
lentowrite += sizeof (int) - 1;
lentowrite &= ~(sizeof (int) - 1);
memcpy(buf, vecs[i].iov_base, lentowrite);
 
ret =
jffs2_flash_write(c, to, lentowrite,
&thislen, (char *) &buf);
if (thislen > vecs[i].iov_len)
thislen = vecs[i].iov_len;
} // else
} else
ret =
jffs2_flash_write(c, to, vecs[i].iov_len, &thislen,
vecs[i].iov_base);
totlen += thislen;
if (ret || thislen != vecs[i].iov_len)
break;
to += vecs[i].iov_len;
}
writev_out:
if (retlen)
*retlen = totlen;
 
return ret;
}
 
cyg_bool jffs2_flash_erase(struct jffs2_sb_info * c,
struct jffs2_eraseblock * jeb)
{
cyg_io_flash_getconfig_erase_t e;
void *err_addr;
Cyg_ErrNo err;
cyg_uint32 len = sizeof (e);
struct super_block *sb = OFNI_BS_2SFFJ(c);
 
e.offset = jeb->offset;
e.len = c->sector_size;
e.err_address = &err_addr;
 
// D2(printf("FLASH ERASE ENABLED!!!\n"));
// D2(printf("erase address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + jeb->offset));
// D2(printf("size = %x\n", c->sector_size));
 
err = cyg_io_get_config(sb->s_dev, CYG_IO_GET_CONFIG_FLASH_ERASE,
&e, &len);
 
return (err != ENOERR || e.flasherr != 0);
}
 
// -------------------------------------------------------------------------
// EOF jffs2.c
void jffs2_clear_inode (struct inode *inode)
{
/* We can forget about this inode for now - drop all
* the nodelists associated with it, etc.
*/
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 
D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
 
jffs2_do_clear_inode(c, f);
}
 
 
/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
fill in the raw_inode while you're at it. */
struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
{
struct inode *inode;
struct super_block *sb = dir_i->i_sb;
struct jffs2_sb_info *c;
struct jffs2_inode_info *f;
int ret;
 
D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
 
c = JFFS2_SB_INFO(sb);
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
 
f = JFFS2_INODE_INFO(inode);
jffs2_init_inode_info(f);
 
memset(ri, 0, sizeof(*ri));
/* Set OS-specific defaults for new inodes */
ri->uid = ri->gid = cpu_to_je16(0);
ri->mode = cpu_to_jemode(mode);
ret = jffs2_do_new_inode (c, f, mode, ri);
if (ret) {
make_bad_inode(inode);
iput(inode);
return ERR_PTR(ret);
}
inode->i_nlink = 1;
inode->i_ino = je32_to_cpu(ri->ino);
inode->i_mode = jemode_to_cpu(ri->mode);
inode->i_gid = je16_to_cpu(ri->gid);
inode->i_uid = je16_to_cpu(ri->uid);
inode->i_atime = inode->i_ctime = inode->i_mtime = cyg_timestamp();
ri->atime = ri->mtime = ri->ctime = cpu_to_je32(inode->i_mtime);
 
inode->i_size = 0;
 
insert_inode_hash(inode);
 
return inode;
}
 
 
void jffs2_read_inode (struct inode *inode)
{
struct jffs2_inode_info *f;
struct jffs2_sb_info *c;
struct jffs2_raw_inode latest_node;
int ret;
 
D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
 
f = JFFS2_INODE_INFO(inode);
c = JFFS2_SB_INFO(inode->i_sb);
 
jffs2_init_inode_info(f);
ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
 
if (ret) {
make_bad_inode(inode);
up(&f->sem);
return;
}
inode->i_mode = jemode_to_cpu(latest_node.mode);
inode->i_uid = je16_to_cpu(latest_node.uid);
inode->i_gid = je16_to_cpu(latest_node.gid);
inode->i_size = je32_to_cpu(latest_node.isize);
inode->i_atime = je32_to_cpu(latest_node.atime);
inode->i_mtime = je32_to_cpu(latest_node.mtime);
inode->i_ctime = je32_to_cpu(latest_node.ctime);
 
inode->i_nlink = f->inocache->nlink;
up(&f->sem);
 
D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
}
/jffs2/v2_0/src/compr_zlib.c
0,0 → 1,177
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: compr_zlib.c,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $
*
*/
 
#if !defined(__KERNEL__) && !defined(__ECOS)
#error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
#endif
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mtd/compatmac.h> /* for min() */
#include <linux/slab.h>
#include <linux/zlib.h>
#include <linux/zutil.h>
#include <asm/semaphore.h>
#include "nodelist.h"
 
/* Plan: call deflate() with avail_in == *sourcelen,
avail_out = *dstlen - 12 and flush == Z_FINISH.
If it doesn't manage to finish, call it again with
avail_in == 0 and avail_out set to the remaining 12
bytes for it to clean up.
Q: Is 12 bytes sufficient?
*/
#define STREAM_END_SPACE 12
 
static DECLARE_MUTEX(deflate_sem);
static DECLARE_MUTEX(inflate_sem);
static z_stream inf_strm, def_strm;
 
#ifdef __KERNEL__ /* Linux-only */
int __init jffs2_zlib_init(void)
{
def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
if (!def_strm.workspace) {
printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
return -ENOMEM;
}
D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
if (!inf_strm.workspace) {
printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
vfree(def_strm.workspace);
return -ENOMEM;
}
D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
return 0;
}
 
void jffs2_zlib_exit(void)
{
vfree(def_strm.workspace);
vfree(inf_strm.workspace);
}
#endif /* __KERNEL__ */
 
int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
int ret;
 
if (*dstlen <= STREAM_END_SPACE)
return -1;
 
down(&deflate_sem);
 
if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
printk(KERN_WARNING "deflateInit failed\n");
up(&deflate_sem);
return -1;
}
 
def_strm.next_in = data_in;
def_strm.total_in = 0;
def_strm.next_out = cpage_out;
def_strm.total_out = 0;
 
while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) {
def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE);
def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out);
D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
def_strm.avail_in, def_strm.avail_out));
ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n",
def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out));
if (ret != Z_OK) {
D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
zlib_deflateEnd(&def_strm);
up(&deflate_sem);
return -1;
}
}
def_strm.avail_out += STREAM_END_SPACE;
def_strm.avail_in = 0;
ret = zlib_deflate(&def_strm, Z_FINISH);
zlib_deflateEnd(&def_strm);
 
if (ret != Z_STREAM_END) {
D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
ret = -1;
goto out;
}
 
if (def_strm.total_out >= def_strm.total_in) {
D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n",
def_strm.total_in, def_strm.total_out));
ret = -1;
goto out;
}
 
D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
def_strm.total_in, def_strm.total_out));
 
*dstlen = def_strm.total_out;
*sourcelen = def_strm.total_in;
ret = 0;
out:
up(&deflate_sem);
return ret;
}
 
void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen)
{
int ret;
int wbits = MAX_WBITS;
 
down(&inflate_sem);
 
inf_strm.next_in = data_in;
inf_strm.avail_in = srclen;
inf_strm.total_in = 0;
inf_strm.next_out = cpage_out;
inf_strm.avail_out = destlen;
inf_strm.total_out = 0;
 
/* If it's deflate, and it's got no preset dictionary, then
we can tell zlib to skip the adler32 check. */
if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
((data_in[0] & 0x0f) == Z_DEFLATED) &&
!(((data_in[0]<<8) + data_in[1]) % 31)) {
 
D2(printk(KERN_DEBUG "inflate skipping adler32\n"));
wbits = -((data_in[0] >> 4) + 8);
inf_strm.next_in += 2;
inf_strm.avail_in -= 2;
} else {
/* Let this remain D1 for now -- it should never happen */
D1(printk(KERN_DEBUG "inflate not skipping adler32\n"));
}
 
 
if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
printk(KERN_WARNING "inflateInit failed\n");
up(&inflate_sem);
return;
}
 
while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
;
if (ret != Z_STREAM_END) {
printk(KERN_NOTICE "inflate returned %d\n", ret);
}
zlib_inflateEnd(&inf_strm);
up(&inflate_sem);
}
/jffs2/v2_0/src/dir-ecos.c
0,0 → 1,375
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: dir-ecos.c,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/crc32.h>
#include "nodelist.h"
 
/***********************************************************************/
 
 
/* We keep the dirent list sorted in increasing order of name hash,
and we use the same hash function as the dentries. Makes this
nice and simple
*/
struct inode *jffs2_lookup(struct inode *dir_i, struct qstr *d_name)
{
struct jffs2_inode_info *dir_f;
struct jffs2_sb_info *c;
struct jffs2_full_dirent *fd = NULL, *fd_list;
uint32_t ino = 0;
struct inode *inode = NULL;
 
D1(printk("jffs2_lookup()\n"));
 
dir_f = JFFS2_INODE_INFO(dir_i);
c = JFFS2_SB_INFO(dir_i->i_sb);
 
down(&dir_f->sem);
 
/* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */
for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= d_name->hash; fd_list = fd_list->next) {
if (fd_list->nhash == d_name->hash &&
(!fd || fd_list->version > fd->version) &&
strlen(fd_list->name) == d_name->len &&
!strncmp(fd_list->name, d_name->name, d_name->len)) {
fd = fd_list;
}
}
if (fd)
ino = fd->ino;
up(&dir_f->sem);
if (ino) {
inode = iget(dir_i->i_sb, ino);
if (!inode) {
printk("iget() failed for ino #%u\n", ino);
return (ERR_PTR(-EIO));
}
}
 
return inode;
}
 
/***********************************************************************/
 
 
 
int jffs2_create(struct inode *dir_i, struct qstr *d_name, int mode,
struct inode **new_i)
{
struct jffs2_raw_inode *ri;
struct jffs2_inode_info *f, *dir_f;
struct jffs2_sb_info *c;
struct inode *inode;
int ret;
 
ri = jffs2_alloc_raw_inode();
if (!ri)
return -ENOMEM;
c = JFFS2_SB_INFO(dir_i->i_sb);
 
D1(printk(KERN_DEBUG "jffs2_create()\n"));
 
inode = jffs2_new_inode(dir_i, mode, ri);
 
if (IS_ERR(inode)) {
D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
jffs2_free_raw_inode(ri);
return PTR_ERR(inode);
}
 
f = JFFS2_INODE_INFO(inode);
dir_f = JFFS2_INODE_INFO(dir_i);
 
ret = jffs2_do_create(c, dir_f, f, ri,
d_name->name, d_name->len);
 
if (ret) {
jffs2_clear_inode(inode);
make_bad_inode(inode);
iput(inode);
jffs2_free_raw_inode(ri);
return ret;
}
 
jffs2_free_raw_inode(ri);
 
D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d)\n",
inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink));
*new_i = inode;
return 0;
}
 
/***********************************************************************/
 
 
int jffs2_unlink(struct inode *dir_i, struct inode *d_inode, struct qstr *d_name)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb);
struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(d_inode);
int ret;
 
ret = jffs2_do_unlink(c, dir_f, d_name->name,
d_name->len, dead_f);
if (dead_f->inocache)
d_inode->i_nlink = dead_f->inocache->nlink;
return ret;
}
/***********************************************************************/
 
 
int jffs2_link (struct inode *old_d_inode, struct inode *dir_i, struct qstr *d_name)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(old_d_inode->i_sb);
struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_d_inode);
struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
int ret;
 
/* XXX: This is ugly */
uint8_t type = (old_d_inode->i_mode & S_IFMT) >> 12;
if (!type) type = DT_REG;
 
ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, d_name->name, d_name->len);
 
if (!ret) {
down(&f->sem);
old_d_inode->i_nlink = ++f->inocache->nlink;
up(&f->sem);
}
return ret;
}
 
int jffs2_mkdir (struct inode *dir_i, struct qstr *d_name, int mode, struct inode **new_i)
{
struct jffs2_inode_info *f, *dir_f;
struct jffs2_sb_info *c;
struct inode *inode;
struct jffs2_raw_inode *ri;
struct jffs2_raw_dirent *rd;
struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd;
int namelen;
uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret;
 
mode |= S_IFDIR;
 
ri = jffs2_alloc_raw_inode();
if (!ri)
return -ENOMEM;
c = JFFS2_SB_INFO(dir_i->i_sb);
 
/* Try to reserve enough space for both node and dirent.
* Just the node will do for now, though
*/
namelen = d_name->len;
ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
 
if (ret) {
jffs2_free_raw_inode(ri);
return ret;
}
 
inode = jffs2_new_inode(dir_i, mode, ri);
 
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
jffs2_complete_reservation(c);
return PTR_ERR(inode);
}
 
f = JFFS2_INODE_INFO(inode);
 
ri->data_crc = cpu_to_je32(0);
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen);
 
jffs2_free_raw_inode(ri);
 
if (IS_ERR(fn)) {
/* Eeek. Wave bye bye */
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
obsoleted by the first data write
*/
f->metadata = fn;
up(&f->sem);
 
/* Work out where to put the dirent node now. */
writtenlen = PAD(writtenlen);
phys_ofs += writtenlen;
alloclen -= writtenlen;
 
if (alloclen < sizeof(*rd)+namelen) {
/* Not enough space left in this chunk. Get some more */
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
/* Eep. */
jffs2_clear_inode(inode);
return ret;
}
}
rd = jffs2_alloc_raw_dirent();
if (!rd) {
/* Argh. Now we treat it like a normal delete */
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
return -ENOMEM;
}
 
dir_f = JFFS2_INODE_INFO(dir_i);
down(&dir_f->sem);
 
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
 
rd->pino = cpu_to_je32(dir_i->i_ino);
rd->version = cpu_to_je32(++dir_f->highest_version);
rd->ino = cpu_to_je32(inode->i_ino);
rd->mctime = cpu_to_je32(cyg_timestamp());
rd->nsize = namelen;
rd->type = DT_DIR;
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, d_name->name, namelen));
 
fd = jffs2_write_dirent(c, dir_f, rd, d_name->name, namelen, phys_ofs, &writtenlen);
jffs2_complete_reservation(c);
jffs2_free_raw_dirent(rd);
if (IS_ERR(fd)) {
/* dirent failed to write. Delete the inode normally
as if it were the final unlink() */
up(&dir_f->sem);
jffs2_clear_inode(inode);
return PTR_ERR(fd);
}
 
/* Link the fd into the inode's list, obsoleting an old
one if necessary. */
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
up(&dir_f->sem);
 
*new_i = inode;
return 0;
}
 
int jffs2_rmdir (struct inode *dir_i, struct inode *d_inode, struct qstr *d_name)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode);
struct jffs2_full_dirent *fd;
 
for (fd = f->dents ; fd; fd = fd->next) {
if (fd->ino)
return EPERM; //-ENOTEMPTY;
}
return jffs2_unlink(dir_i, d_inode, d_name);
}
 
int jffs2_rename (struct inode *old_dir_i, struct inode *d_inode, struct qstr *old_d_name,
struct inode *new_dir_i, struct qstr *new_d_name)
{
int ret;
struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
struct jffs2_inode_info *victim_f = NULL;
uint8_t type;
 
#if 0 /* FIXME -- this really doesn't belong in individual file systems.
The fileio code ought to do this for us, or at least part of it */
if (new_dentry->d_inode) {
if (S_ISDIR(d_inode->i_mode) &&
!S_ISDIR(new_dentry->d_inode->i_mode)) {
/* Cannot rename directory over non-directory */
return -EINVAL;
}
 
victim_f = JFFS2_INODE_INFO(new_dentry->d_inode);
 
if (S_ISDIR(new_dentry->d_inode->i_mode)) {
struct jffs2_full_dirent *fd;
 
if (!S_ISDIR(d_inode->i_mode)) {
/* Cannot rename non-directory over directory */
return -EINVAL;
}
down(&victim_f->sem);
for (fd = victim_f->dents; fd; fd = fd->next) {
if (fd->ino) {
up(&victim_f->sem);
return -ENOTEMPTY;
}
}
up(&victim_f->sem);
}
}
#endif
 
/* XXX: We probably ought to alloc enough space for
both nodes at the same time. Writing the new link,
then getting -ENOSPC, is quite bad :)
*/
 
/* Make a hard link */
/* XXX: This is ugly */
type = (d_inode->i_mode & S_IFMT) >> 12;
if (!type) type = DT_REG;
 
ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i),
d_inode->i_ino, type,
new_d_name->name, new_d_name->len);
 
if (ret)
return ret;
 
if (victim_f) {
/* There was a victim. Kill it off nicely */
/* Don't oops if the victim was a dirent pointing to an
inode which didn't exist. */
if (victim_f->inocache) {
down(&victim_f->sem);
victim_f->inocache->nlink--;
up(&victim_f->sem);
}
}
 
/* Unlink the original */
ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
old_d_name->name, old_d_name->len, NULL);
 
if (ret) {
/* Oh shit. We really ought to make a single node which can do both atomically */
struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode);
down(&f->sem);
if (f->inocache)
d_inode->i_nlink = f->inocache->nlink++;
up(&f->sem);
 
printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
}
return ret;
}
 
/jffs2/v2_0/src/file-ecos.c
0,0 → 1,214
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: file-ecos.c,v 1.1.1.1 2004-02-14 13:29:20 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include "nodelist.h"
 
int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
unsigned char *pg_buf;
int ret;
 
D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
 
if (!PageLocked(pg))
PAGE_BUG(pg);
 
pg_buf = (char *)kmap(pg);
/* FIXME: Can kmap fail? */
 
ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
 
if (ret) {
ClearPageUptodate(pg);
SetPageError(pg);
} else {
SetPageUptodate(pg);
ClearPageError(pg);
}
 
flush_dcache_page(pg);
kunmap(pg);
 
D1(printk(KERN_DEBUG "readpage finished\n"));
return 0;
}
 
int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg)
{
int ret = jffs2_do_readpage_nolock(inode, pg);
UnlockPage(pg);
return ret;
}
 
 
//int jffs2_readpage (struct file *filp, struct page *pg)
int jffs2_readpage (struct inode *d_inode, struct page *pg)
{
// struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode);
int ret;
// down(&f->sem);
ret = jffs2_do_readpage_unlock(d_inode, pg);
// up(&f->sem);
return ret;
}
 
//int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end)
int jffs2_prepare_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end)
{
struct inode *inode = d_inode;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
int ret = 0;
 
D1(printk(KERN_DEBUG "jffs2_prepare_write()\n"));
 
if (pageofs > inode->i_size) {
/* Make new hole frag from old EOF to new page */
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_raw_inode ri;
struct jffs2_full_dnode *fn;
uint32_t phys_ofs, alloc_len;
D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
(unsigned int)inode->i_size, pageofs));
 
ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL);
if (ret)
return ret;
 
down(&f->sem);
memset(&ri, 0, sizeof(ri));
 
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri.totlen = cpu_to_je32(sizeof(ri));
ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
 
ri.ino = cpu_to_je32(f->inocache->ino);
ri.version = cpu_to_je32(++f->highest_version);
ri.mode = cpu_to_jemode(inode->i_mode);
ri.uid = cpu_to_je16(inode->i_uid);
ri.gid = cpu_to_je16(inode->i_gid);
 
ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
ri.atime = ri.ctime = ri.mtime = cpu_to_je32(cyg_timestamp());
ri.offset = cpu_to_je32(inode->i_size);
ri.dsize = cpu_to_je32(pageofs - inode->i_size);
ri.csize = cpu_to_je32(0);
ri.compr = JFFS2_COMPR_ZERO;
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
ri.data_crc = cpu_to_je32(0);
fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL);
jffs2_complete_reservation(c);
if (IS_ERR(fn)) {
ret = PTR_ERR(fn);
up(&f->sem);
return ret;
}
ret = jffs2_add_full_dnode_to_inode(c, f, fn);
if (f->metadata) {
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
}
if (ret) {
D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
jffs2_mark_node_obsolete(c, fn->raw);
jffs2_free_full_dnode(fn);
up(&f->sem);
return ret;
}
inode->i_size = pageofs;
up(&f->sem);
}
 
/* Read in the page if it wasn't already present, unless it's a whole page */
// eCos has no concept of uptodate and by default always reads pages afresh
if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
down(&f->sem);
ret = jffs2_do_readpage_nolock(inode, pg);
up(&f->sem);
}
D1(printk(KERN_DEBUG "end prepare_write()\n"));
return ret;
}
 
//int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end)
int jffs2_commit_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end)
{
/* Actually commit the write from the page cache page we're looking at.
* For now, we write the full page out each time. It sucks, but it's simple
*/
struct inode *inode = d_inode;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_raw_inode *ri;
int ret = 0;
uint32_t writtenlen = 0;
 
D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d\n",
inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end));
 
 
ri = jffs2_alloc_raw_inode();
if (!ri) {
D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
return -ENOMEM;
}
 
/* Set the fields that the generic jffs2_write_inode_range() code can't find */
ri->ino = cpu_to_je32(inode->i_ino);
ri->mode = cpu_to_jemode(inode->i_mode);
ri->uid = cpu_to_je16(inode->i_uid);
ri->gid = cpu_to_je16(inode->i_gid);
ri->isize = cpu_to_je32((uint32_t)inode->i_size);
ri->atime = ri->ctime = ri->mtime = cpu_to_je32(cyg_timestamp());
 
ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start,
(pg->index << PAGE_CACHE_SHIFT) + start,
end - start, &writtenlen);
 
if (ret) {
/* There was an error writing. */
SetPageError(pg);
}
 
if (writtenlen) {
if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen;
inode->i_ctime = inode->i_mtime = je32_to_cpu(ri->ctime);
}
}
 
jffs2_free_raw_inode(ri);
 
if (start+writtenlen < end) {
/* generic_file_write has written more to the page cache than we've
actually written to the medium. Mark the page !Uptodate so that
it gets reread */
D1(printk(KERN_DEBUG "jffs2_commit_write(): Not all bytes written. Marking page !uptodate\n"));
SetPageError(pg);
ClearPageUptodate(pg);
}
 
D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret));
return writtenlen?writtenlen:ret;
}
/jffs2/v2_0/src/nodemgmt.c
0,0 → 1,679
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodemgmt.c,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/compiler.h>
#include <linux/sched.h> /* For cond_resched() */
#include "nodelist.h"
 
/**
* jffs2_reserve_space - request physical space to write nodes to flash
* @c: superblock info
* @minsize: Minimum acceptable size of allocation
* @ofs: Returned value of node offset
* @len: Returned value of allocation length
* @prio: Allocation type - ALLOC_{NORMAL,DELETION}
*
* Requests a block of physical space on the flash. Returns zero for success
* and puts 'ofs' and 'len' into the appriopriate place, or returns -ENOSPC
* or other error if appropriate.
*
* If it returns zero, jffs2_reserve_space() also downs the per-filesystem
* allocation semaphore, to prevent more than one allocation from being
* active at any time. The semaphore is later released by jffs2_commit_allocation()
*
* jffs2_reserve_space() may trigger garbage collection in order to make room
* for the requested allocation.
*/
 
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
 
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
{
int ret = -EAGAIN;
int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
/* align it */
minsize = PAD(minsize);
 
if (prio == ALLOC_DELETION)
blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION;
 
D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
down(&c->alloc_sem);
 
D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
 
spin_lock(&c->erase_completion_lock);
 
/* this needs a little more thought */
while(ret == -EAGAIN) {
while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
int ret;
 
up(&c->alloc_sem);
if (c->dirty_size + c->unchecked_size < c->sector_size) {
D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < sector size 0x%08x, returning -ENOSPC\n",
c->dirty_size, c->unchecked_size, c->sector_size));
spin_unlock(&c->erase_completion_lock);
return -ENOSPC;
}
D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
spin_unlock(&c->erase_completion_lock);
ret = jffs2_garbage_collect_pass(c);
if (ret)
return ret;
 
cond_resched();
 
if (signal_pending(current))
return -EINTR;
 
down(&c->alloc_sem);
spin_lock(&c->erase_completion_lock);
}
 
ret = jffs2_do_reserve_space(c, minsize, ofs, len);
if (ret) {
D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
}
}
spin_unlock(&c->erase_completion_lock);
if (ret)
up(&c->alloc_sem);
return ret;
}
 
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
{
int ret = -EAGAIN;
minsize = PAD(minsize);
 
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
 
spin_lock(&c->erase_completion_lock);
while(ret == -EAGAIN) {
ret = jffs2_do_reserve_space(c, minsize, ofs, len);
if (ret) {
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
}
}
spin_unlock(&c->erase_completion_lock);
return ret;
}
 
/* Called with alloc sem _and_ erase_completion_lock */
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
{
struct jffs2_eraseblock *jeb = c->nextblock;
restart:
if (jeb && minsize > jeb->free_size) {
/* Skip the end of this block and file it as having some dirty space */
/* If there's a pending write to it, flush now */
if (c->wbuf_len) {
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
jffs2_flush_wbuf(c, 1);
spin_lock(&c->erase_completion_lock);
/* We know nobody's going to have changed nextblock. Just continue */
}
c->wasted_size += jeb->free_size;
c->free_size -= jeb->free_size;
jeb->wasted_size += jeb->free_size;
jeb->free_size = 0;
/* Check, if we have a dirty block now, or if it was dirty already */
if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) {
c->dirty_size += jeb->wasted_size;
c->wasted_size -= jeb->wasted_size;
jeb->dirty_size += jeb->wasted_size;
jeb->wasted_size = 0;
if (VERYDIRTY(c, jeb->dirty_size)) {
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
list_add_tail(&jeb->list, &c->very_dirty_list);
} else {
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
list_add_tail(&jeb->list, &c->dirty_list);
}
} else {
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
list_add_tail(&jeb->list, &c->clean_list);
}
c->nextblock = jeb = NULL;
}
if (!jeb) {
struct list_head *next;
/* Take the next block off the 'free' list */
 
if (list_empty(&c->free_list)) {
 
DECLARE_WAITQUEUE(wait, current);
if (!c->nr_erasing_blocks &&
!list_empty(&c->erasable_list)) {
struct jffs2_eraseblock *ejeb;
 
ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list);
list_del(&ejeb->list);
list_add_tail(&ejeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
jffs2_erase_pending_trigger(c);
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n",
ejeb->offset));
}
 
if (!c->nr_erasing_blocks &&
!list_empty(&c->erasable_pending_wbuf_list)) {
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
/* c->nextblock is NULL, no update to c->nextblock allowed */
spin_unlock(&c->erase_completion_lock);
jffs2_flush_wbuf(c, 1);
spin_lock(&c->erase_completion_lock);
/* Have another go. It'll be on the erasable_list now */
return -EAGAIN;
}
 
if (!c->nr_erasing_blocks) {
/* Ouch. We're in GC, or we wouldn't have got here.
And there's no space left. At all. */
printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no",
list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
return -ENOSPC;
}
/* Make sure this can't deadlock. Someone has to start the erases
of erase_pending blocks */
#ifdef __ECOS
/* In eCos, we don't have a handy kernel thread doing the erases for
us. We do them ourselves right now. */
jffs2_erase_pending_blocks(c);
#else
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&c->erase_wait, &wait);
D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
c->nr_erasing_blocks, list_empty(&c->erasable_list)?"yes":"no",
list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
if (!list_empty(&c->erase_pending_list)) {
D1(printk(KERN_DEBUG "Triggering pending erases\n"));
jffs2_erase_pending_trigger(c);
}
spin_unlock(&c->erase_completion_lock);
schedule();
remove_wait_queue(&c->erase_wait, &wait);
spin_lock(&c->erase_completion_lock);
if (signal_pending(current)) {
return -EINTR;
}
#endif
/* An erase may have failed, decreasing the
amount of free space available. So we must
restart from the beginning */
return -EAGAIN;
}
 
next = c->free_list.next;
list_del(next);
c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
c->nr_free_blocks--;
 
if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
goto restart;
}
}
/* OK, jeb (==c->nextblock) is now pointing at a block which definitely has
enough space */
*ofs = jeb->offset + (c->sector_size - jeb->free_size);
*len = jeb->free_size;
 
if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
!jeb->first_node->next_in_ino) {
/* Only node in it beforehand was a CLEANMARKER node (we think).
So mark it obsolete now that there's going to be another node
in the block. This will reduce used_size to zero but We've
already set c->nextblock so that jffs2_mark_node_obsolete()
won't try to refile it to the dirty_list.
*/
spin_unlock(&c->erase_completion_lock);
jffs2_mark_node_obsolete(c, jeb->first_node);
spin_lock(&c->erase_completion_lock);
}
 
D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
return 0;
}
 
/**
* jffs2_add_physical_node_ref - add a physical node reference to the list
* @c: superblock info
* @new: new node reference to add
* @len: length of this physical node
* @dirty: dirty flag for new node
*
* Should only be used to report nodes for which space has been allocated
* by jffs2_reserve_space.
*
* Must be called with the alloc_sem held.
*/
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
{
struct jffs2_eraseblock *jeb;
uint32_t len = new->totlen;
 
jeb = &c->blocks[new->flash_offset / c->sector_size];
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
#if 1
if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
printk(KERN_WARNING "argh. node added in wrong place\n");
jffs2_free_raw_node_ref(new);
return -EINVAL;
}
#endif
spin_lock(&c->erase_completion_lock);
 
if (!jeb->first_node)
jeb->first_node = new;
if (jeb->last_node)
jeb->last_node->next_phys = new;
jeb->last_node = new;
 
jeb->free_size -= len;
c->free_size -= len;
if (ref_obsolete(new)) {
jeb->dirty_size += len;
c->dirty_size += len;
} else {
jeb->used_size += len;
c->used_size += len;
}
 
if (!jeb->free_size && !jeb->dirty_size) {
/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
if (c->wbuf_len) {
/* Flush the last write in the block if it's outstanding */
spin_unlock(&c->erase_completion_lock);
jffs2_flush_wbuf(c, 1);
spin_lock(&c->erase_completion_lock);
}
 
list_add_tail(&jeb->list, &c->clean_list);
c->nextblock = NULL;
}
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
 
spin_unlock(&c->erase_completion_lock);
 
return 0;
}
 
 
void jffs2_complete_reservation(struct jffs2_sb_info *c)
{
D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n"));
jffs2_garbage_collect_trigger(c);
up(&c->alloc_sem);
}
 
void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
{
struct jffs2_eraseblock *jeb;
int blocknr;
struct jffs2_unknown_node n;
int ret;
size_t retlen;
 
if(!ref) {
printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
return;
}
if (ref_obsolete(ref)) {
D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref)));
return;
}
blocknr = ref->flash_offset / c->sector_size;
if (blocknr >= c->nr_blocks) {
printk(KERN_NOTICE "raw node at 0x%08x is off the end of device!\n", ref->flash_offset);
BUG();
}
jeb = &c->blocks[blocknr];
 
spin_lock(&c->erase_completion_lock);
 
if (ref_flags(ref) == REF_UNCHECKED) {
D1(if (unlikely(jeb->unchecked_size < ref->totlen)) {
printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
BUG();
})
D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
jeb->unchecked_size -= ref->totlen;
c->unchecked_size -= ref->totlen;
} else {
D1(if (unlikely(jeb->used_size < ref->totlen)) {
printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
BUG();
})
D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
jeb->used_size -= ref->totlen;
c->used_size -= ref->totlen;
}
 
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) {
D1(printk("Dirtying\n"));
jeb->dirty_size += ref->totlen + jeb->wasted_size;
c->dirty_size += ref->totlen + jeb->wasted_size;
c->wasted_size -= jeb->wasted_size;
jeb->wasted_size = 0;
} else {
D1(printk("Wasting\n"));
jeb->wasted_size += ref->totlen;
c->wasted_size += ref->totlen;
}
ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
ACCT_SANITY_CHECK(c, jeb);
 
D1(ACCT_PARANOIA_CHECK(jeb));
 
if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
/* Mount in progress. Don't muck about with the block
lists because they're not ready yet, and don't actually
obliterate nodes that look obsolete. If they weren't
marked obsolete on the flash at the time they _became_
obsolete, there was probably a reason for that. */
spin_unlock(&c->erase_completion_lock);
return;
}
 
if (jeb == c->nextblock) {
D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
} else if (!jeb->used_size && !jeb->unchecked_size) {
if (jeb == c->gcblock) {
D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset));
c->gcblock = NULL;
} else {
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
list_del(&jeb->list);
}
if (c->wbuf_len) {
D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
#if 0 /* This check was added to allow us to find places where we added nodes to the lists
after dropping the alloc_sem, and it did that just fine. But it also caused us to
lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise
have needed to. So I suspect it's outlived its usefulness. Thomas? */
 
/* We've changed the rules slightly. After
writing a node you now mustn't drop the
alloc_sem before you've finished all the
list management - this is so that when we
get here, we know that no other nodes have
been written, and the above check on wbuf
is valid - wbuf_len is nonzero IFF the node
which obsoletes this node is still in the
wbuf.
 
So we BUG() if that new rule is broken, to
make sure we catch it and fix it.
*/
if (!down_trylock(&c->alloc_sem)) {
up(&c->alloc_sem);
printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n");
BUG();
}
#endif
} else {
if (jiffies & 127) {
/* Most of the time, we just erase it immediately. Otherwise we
spend ages scanning it on mount, etc. */
D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
list_add_tail(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
jffs2_erase_pending_trigger(c);
} else {
/* Sometimes, however, we leave it elsewhere so it doesn't get
immediately reused, and we spread the load a bit. */
D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
list_add_tail(&jeb->list, &c->erasable_list);
}
}
D1(printk(KERN_DEBUG "Done OK\n"));
} else if (jeb == c->gcblock) {
D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
} else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - ref->totlen)) {
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
list_del(&jeb->list);
D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
list_add_tail(&jeb->list, &c->dirty_list);
} else if (VERYDIRTY(c, jeb->dirty_size) &&
!VERYDIRTY(c, jeb->dirty_size - ref->totlen)) {
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
list_del(&jeb->list);
D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
list_add_tail(&jeb->list, &c->very_dirty_list);
} else {
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
}
 
spin_unlock(&c->erase_completion_lock);
 
if (!jffs2_can_mark_obsolete(c))
return;
if (jffs2_is_readonly(c))
return;
 
D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
if (ret) {
printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
return;
}
if (retlen != sizeof(n)) {
printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
return;
}
if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) {
printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref->totlen);
return;
}
if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
return;
}
/* XXX FIXME: This is ugly now */
n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE);
ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
if (ret) {
printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
return;
}
if (retlen != sizeof(n)) {
printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
return;
}
}
 
#if CONFIG_JFFS2_FS_DEBUG > 0
void jffs2_dump_block_lists(struct jffs2_sb_info *c)
{
 
 
printk(KERN_DEBUG "jffs2_dump_block_lists:\n");
printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size);
printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size);
printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE);
 
if (c->nextblock) {
printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size);
} else {
printk(KERN_DEBUG "nextblock: NULL\n");
}
if (c->gcblock) {
printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size);
} else {
printk(KERN_DEBUG "gcblock: NULL\n");
}
if (list_empty(&c->clean_list)) {
printk(KERN_DEBUG "clean_list: empty\n");
} else {
struct list_head *this;
int numblocks = 0;
uint32_t dirty = 0;
 
list_for_each(this, &c->clean_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
numblocks ++;
dirty += jeb->wasted_size;
printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks);
}
if (list_empty(&c->very_dirty_list)) {
printk(KERN_DEBUG "very_dirty_list: empty\n");
} else {
struct list_head *this;
int numblocks = 0;
uint32_t dirty = 0;
 
list_for_each(this, &c->very_dirty_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
numblocks ++;
dirty += jeb->dirty_size;
printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
numblocks, dirty, dirty / numblocks);
}
if (list_empty(&c->dirty_list)) {
printk(KERN_DEBUG "dirty_list: empty\n");
} else {
struct list_head *this;
int numblocks = 0;
uint32_t dirty = 0;
 
list_for_each(this, &c->dirty_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
numblocks ++;
dirty += jeb->dirty_size;
printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
numblocks, dirty, dirty / numblocks);
}
if (list_empty(&c->erasable_list)) {
printk(KERN_DEBUG "erasable_list: empty\n");
} else {
struct list_head *this;
 
list_for_each(this, &c->erasable_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
}
if (list_empty(&c->erasing_list)) {
printk(KERN_DEBUG "erasing_list: empty\n");
} else {
struct list_head *this;
 
list_for_each(this, &c->erasing_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
}
if (list_empty(&c->erase_pending_list)) {
printk(KERN_DEBUG "erase_pending_list: empty\n");
} else {
struct list_head *this;
 
list_for_each(this, &c->erase_pending_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
}
if (list_empty(&c->erasable_pending_wbuf_list)) {
printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n");
} else {
struct list_head *this;
 
list_for_each(this, &c->erasable_pending_wbuf_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erase_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
}
if (list_empty(&c->free_list)) {
printk(KERN_DEBUG "free_list: empty\n");
} else {
struct list_head *this;
 
list_for_each(this, &c->free_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
}
if (list_empty(&c->bad_list)) {
printk(KERN_DEBUG "bad_list: empty\n");
} else {
struct list_head *this;
 
list_for_each(this, &c->bad_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
}
if (list_empty(&c->bad_used_list)) {
printk(KERN_DEBUG "bad_used_list: empty\n");
} else {
struct list_head *this;
 
list_for_each(this, &c->bad_used_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
}
}
}
#endif /* CONFIG_JFFS2_FS_DEBUG */
/jffs2/v2_0/src/read.c
0,0 → 1,248
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: read.c,v 1.1.1.1 2004-02-14 13:29:20 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/crc32.h>
#include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
 
int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
{
struct jffs2_raw_inode *ri;
size_t readlen;
uint32_t crc;
unsigned char *decomprbuf = NULL;
unsigned char *readbuf = NULL;
int ret = 0;
 
ri = jffs2_alloc_raw_inode();
if (!ri)
return -ENOMEM;
 
ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
if (ret) {
jffs2_free_raw_inode(ri);
printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret);
return ret;
}
if (readlen != sizeof(*ri)) {
jffs2_free_raw_inode(ri);
printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n",
ref_offset(fd->raw), sizeof(*ri), readlen);
return -EIO;
}
crc = crc32(0, ri, sizeof(*ri)-8);
 
D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
ref_offset(fd->raw), je32_to_cpu(ri->node_crc),
crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
je32_to_cpu(ri->offset), buf));
if (crc != je32_to_cpu(ri->node_crc)) {
printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n",
je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
ret = -EIO;
goto out_ri;
}
/* There was a bug where we wrote hole nodes out with csize/dsize
swapped. Deal with it */
if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) &&
je32_to_cpu(ri->csize)) {
ri->dsize = ri->csize;
ri->csize = cpu_to_je32(0);
}
 
D1(if(ofs + len > je32_to_cpu(ri->dsize)) {
printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
len, ofs, je32_to_cpu(ri->dsize));
ret = -EINVAL;
goto out_ri;
});
 
if (ri->compr == JFFS2_COMPR_ZERO) {
memset(buf, 0, len);
goto out_ri;
}
 
/* Cases:
Reading whole node and it's uncompressed - read directly to buffer provided, check CRC.
Reading whole node and it's compressed - read into comprbuf, check CRC and decompress to buffer provided
Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy
Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy
*/
if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) {
readbuf = buf;
} else {
readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL);
if (!readbuf) {
ret = -ENOMEM;
goto out_ri;
}
}
if (ri->compr != JFFS2_COMPR_NONE) {
if (len < je32_to_cpu(ri->dsize)) {
decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
if (!decomprbuf) {
ret = -ENOMEM;
goto out_readbuf;
}
} else {
decomprbuf = buf;
}
} else {
decomprbuf = readbuf;
}
 
D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
readbuf));
ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri),
je32_to_cpu(ri->csize), &readlen, readbuf);
 
if (!ret && readlen != je32_to_cpu(ri->csize))
ret = -EIO;
if (ret)
goto out_decomprbuf;
 
crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
if (crc != je32_to_cpu(ri->data_crc)) {
printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n",
je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
ret = -EIO;
goto out_decomprbuf;
}
D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc));
if (ri->compr != JFFS2_COMPR_NONE) {
D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf));
ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
if (ret) {
printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
goto out_decomprbuf;
}
}
 
if (len < je32_to_cpu(ri->dsize)) {
memcpy(buf, decomprbuf+ofs, len);
}
out_decomprbuf:
if(decomprbuf != buf && decomprbuf != readbuf)
kfree(decomprbuf);
out_readbuf:
if(readbuf != buf)
kfree(readbuf);
out_ri:
jffs2_free_raw_inode(ri);
 
return ret;
}
 
int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
unsigned char *buf, uint32_t offset, uint32_t len)
{
uint32_t end = offset + len;
struct jffs2_node_frag *frag;
int ret;
 
D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
f->inocache->ino, offset, offset+len));
 
frag = jffs2_lookup_node_frag(&f->fragtree, offset);
 
/* XXX FIXME: Where a single physical node actually shows up in two
frags, we read it twice. Don't do that. */
/* Now we're pointing at the first frag which overlaps our page */
while(offset < end) {
D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end));
if (!frag || frag->ofs > offset) {
uint32_t holesize = end - offset;
if (frag) {
D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset));
holesize = min(holesize, frag->ofs - offset);
D1(jffs2_print_frag_list(f));
}
D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
memset(buf, 0, holesize);
buf += holesize;
offset += holesize;
continue;
} else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) {
D1(printk(KERN_NOTICE "Eep. Overlap in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
f->inocache->ino, frag->ofs, offset));
D1(jffs2_print_frag_list(f));
memset(buf, 0, end - offset);
return -EIO;
} else if (!frag->node) {
uint32_t holeend = min(end, frag->ofs + frag->size);
D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
memset(buf, 0, holeend - offset);
buf += holeend - offset;
offset = holeend;
frag = frag_next(frag);
continue;
} else {
uint32_t readlen;
uint32_t fragofs; /* offset within the frag to start reading */
fragofs = offset - frag->ofs;
readlen = min(frag->size - fragofs, end - offset);
D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
frag->ofs+fragofs, frag->ofs+fragofs+readlen,
ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
D2(printk(KERN_DEBUG "node read done\n"));
if (ret) {
D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
memset(buf, 0, readlen);
return ret;
}
buf += readlen;
offset += readlen;
frag = frag_next(frag);
D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
}
}
return 0;
}
 
/* Core function to read symlink target. */
char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
{
char *buf;
int ret;
 
down(&f->sem);
 
if (!f->metadata) {
printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino);
up(&f->sem);
return ERR_PTR(-EINVAL);
}
buf = kmalloc(f->metadata->size+1, GFP_USER);
if (!buf) {
up(&f->sem);
return ERR_PTR(-ENOMEM);
}
buf[f->metadata->size]=0;
 
ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size);
 
up(&f->sem);
 
if (ret) {
kfree(buf);
return ERR_PTR(ret);
}
return buf;
}
/jffs2/v2_0/src/os-ecos.h
0,0 → 1,252
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: os-ecos.h,v 1.1.1.1 2004-02-14 13:29:18 phoenix Exp $
*
*/
 
#ifndef __JFFS2_OS_ECOS_H__
#define __JFFS2_OS_ECOS_H__
 
#include <cyg/io/io.h>
#include <sys/types.h>
#include <asm/atomic.h>
#include <linux/stat.h>
#include "jffs2port.h"
 
#define CONFIG_JFFS2_FS_DEBUG 0
 
static inline uint32_t os_to_jffs2_mode(uint32_t osmode)
{
uint32_t jmode = ((osmode & S_IRUSR)?00400:0) |
((osmode & S_IWUSR)?00200:0) |
((osmode & S_IXUSR)?00100:0) |
((osmode & S_IRGRP)?00040:0) |
((osmode & S_IWGRP)?00020:0) |
((osmode & S_IXGRP)?00010:0) |
((osmode & S_IROTH)?00004:0) |
((osmode & S_IWOTH)?00002:0) |
((osmode & S_IXOTH)?00001:0);
 
switch (osmode & S_IFMT) {
case S_IFSOCK:
return jmode | 0140000;
case S_IFLNK:
return jmode | 0120000;
case S_IFREG:
return jmode | 0100000;
case S_IFBLK:
return jmode | 0060000;
case S_IFDIR:
return jmode | 0040000;
case S_IFCHR:
return jmode | 0020000;
case S_IFIFO:
return jmode | 0010000;
case S_ISUID:
return jmode | 0004000;
case S_ISGID:
return jmode | 0002000;
#ifdef S_ISVTX
case S_ISVTX:
return jmode | 0001000;
#endif
}
printf("os_to_jffs2_mode() cannot convert 0x%x\n", osmode);
BUG();
return 0;
}
 
static inline uint32_t jffs2_to_os_mode (uint32_t jmode)
{
uint32_t osmode = ((jmode & 00400)?S_IRUSR:0) |
((jmode & 00200)?S_IWUSR:0) |
((jmode & 00100)?S_IXUSR:0) |
((jmode & 00040)?S_IRGRP:0) |
((jmode & 00020)?S_IWGRP:0) |
((jmode & 00010)?S_IXGRP:0) |
((jmode & 00004)?S_IROTH:0) |
((jmode & 00002)?S_IWOTH:0) |
((jmode & 00001)?S_IXOTH:0);
 
switch(jmode & 00170000) {
case 0140000:
return osmode | S_IFSOCK;
case 0120000:
return osmode | S_IFLNK;
case 0100000:
return osmode | S_IFREG;
case 0060000:
return osmode | S_IFBLK;
case 0040000:
return osmode | S_IFDIR;
case 0020000:
return osmode | S_IFCHR;
case 0010000:
return osmode | S_IFIFO;
case 0004000:
return osmode | S_ISUID;
case 0002000:
return osmode | S_ISGID;
#ifdef S_ISVTX
case 0001000:
return osmode | S_ISVTX;
#endif
}
printf("jffs2_to_os_mode() cannot convert 0x%x\n", osmode);
BUG();
return 0;
}
 
/* Read-only operation not currently implemented on eCos */
#define jffs2_is_readonly(c) (0)
 
/* NAND flash not currently supported on eCos */
#define jffs2_can_mark_obsolete(c) (1)
 
#define JFFS2_INODE_INFO(i) (&(i)->jffs2_i)
#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->jffs2_i)) ) )
#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
 
/* FIXME: eCos doesn't hav a concept of device major/minor numbers */
#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
 
 
//#define ITIME(sec) (sec)
//#define I_SEC(x) (x)
#define get_seconds cyg_timestamp
 
struct inode {
//struct list_head i_hash;
//struct list_head i_list;
struct list_head i_dentry;
 
cyg_uint32 i_ino;
atomic_t i_count;
//kdev_t i_dev;
mode_t i_mode;
nlink_t i_nlink;
uid_t i_uid;
gid_t i_gid;
kdev_t i_rdev;
off_t i_size;
time_t i_atime;
time_t i_mtime;
time_t i_ctime;
unsigned long i_blksize;
unsigned long i_blocks;
//unsigned long i_version;
//struct semaphore i_sem;
//struct semaphore i_zombie;
struct inode_operations *i_op;
struct file_operations *i_fop; // former ->i_op->default_file_ops
struct super_block *i_sb;
//wait_queue_head_t i_wait;
//struct file_lock *i_flock;
//struct address_space *i_mapping;
//struct address_space i_data;
//struct dquot *i_dquot[MAXQUOTAS];
//struct pipe_inode_info *i_pipe;
//struct block_device *i_bdev;
 
//unsigned long i_state;
 
unsigned int i_flags;
//unsigned char i_sock;
 
atomic_t i_writecount;
//unsigned int i_attr_flags;
//uint32_t i_generation;
struct jffs2_inode_info jffs2_i;
 
struct inode *i_parent;
 
struct inode *i_cache_prev;
struct inode *i_cache_next;
};
 
#define JFFS2_SB_INFO(sb) (&(sb)->jffs2_sb)
 
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->jffs2_sb)) ) )
 
struct super_block {
unsigned long s_blocksize;
unsigned char s_blocksize_bits;
unsigned char s_dirt;
//struct super_operations *s_op;
unsigned long s_flags;
unsigned long s_magic;
//struct dentry *s_root;
struct inode *s_root;
struct jffs2_sb_info jffs2_sb;
unsigned long s_mount_count;
cyg_io_handle_t s_dev;
};
 
#define sleep_on_spinunlock(wq, sl) do { ; } while(0)
#define EBADFD 32767
 
/* background.c */
static inline void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
{
/* We don't have a GC thread in eCos (yet) */
}
 
/* dir.c */
extern struct file_operations jffs2_dir_operations;
extern struct inode_operations jffs2_dir_inode_operations;
 
/* file.c */
extern struct file_operations jffs2_file_operations;
extern struct inode_operations jffs2_file_inode_operations;
extern struct address_space_operations jffs2_file_address_operations;
int jffs2_null_fsync(struct file *, struct dentry *, int);
int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
//int jffs2_readpage (struct file *, struct page *);
int jffs2_readpage (struct inode *d_inode, struct page *pg);
//int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
int jffs2_prepare_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end);
//int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
int jffs2_commit_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end);
 
#ifndef CONFIG_JFFS2_FS_NAND
#define jffs2_can_mark_obsolete(c) (1)
#define jffs2_cleanmarker_oob(c) (0)
#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
 
#define jffs2_flush_wbuf(c, flag) do { ; } while(0)
#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
#define jffs2_flash_writev jffs2_flash_writev
#define jffs2_wbuf_timeout NULL
#define jffs2_wbuf_process NULL
#else
#error no nand yet
#endif
struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
void jffs2_clear_inode (struct inode *inode);
void jffs2_read_inode (struct inode *inode);
 
static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
{
memset(f, 0, sizeof(*f));
init_MUTEX_LOCKED(&f->sem);
}
 
#endif /* __JFFS2_OS_ECOS_H__ */
/jffs2/v2_0/src/compr_rubin.c
0,0 → 1,325
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by Arjan van de Ven <arjanv@redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: compr_rubin.c,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $
*
*/
 
#include <linux/string.h>
#include <linux/types.h>
#include "compr_rubin.h"
#include "histo_mips.h"
 
 
 
static void init_rubin(struct rubin_state *rs, int div, int *bits)
{
int c;
 
rs->q = 0;
rs->p = (long) (2 * UPPER_BIT_RUBIN);
rs->bit_number = (long) 0;
rs->bit_divider = div;
for (c=0; c<8; c++)
rs->bits[c] = bits[c];
}
 
 
static int encode(struct rubin_state *rs, long A, long B, int symbol)
{
 
long i0, i1;
int ret;
 
while ((rs->q >= UPPER_BIT_RUBIN) || ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) {
rs->bit_number++;
ret = pushbit(&rs->pp, (rs->q & UPPER_BIT_RUBIN) ? 1 : 0, 0);
if (ret)
return ret;
rs->q &= LOWER_BITS_RUBIN;
rs->q <<= 1;
rs->p <<= 1;
}
i0 = A * rs->p / (A + B);
if (i0 <= 0) {
i0 = 1;
}
if (i0 >= rs->p) {
i0 = rs->p - 1;
}
i1 = rs->p - i0;
 
if (symbol == 0)
rs->p = i0;
else {
rs->p = i1;
rs->q += i0;
}
return 0;
}
 
 
static void end_rubin(struct rubin_state *rs)
{
 
int i;
 
for (i = 0; i < RUBIN_REG_SIZE; i++) {
pushbit(&rs->pp, (UPPER_BIT_RUBIN & rs->q) ? 1 : 0, 1);
rs->q &= LOWER_BITS_RUBIN;
rs->q <<= 1;
}
}
 
 
static void init_decode(struct rubin_state *rs, int div, int *bits)
{
init_rubin(rs, div, bits);
 
/* behalve lower */
rs->rec_q = 0;
 
for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE; rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp)))
;
}
 
static void __do_decode(struct rubin_state *rs, unsigned long p, unsigned long q)
{
register unsigned long lower_bits_rubin = LOWER_BITS_RUBIN;
unsigned long rec_q;
int c, bits = 0;
 
/*
* First, work out how many bits we need from the input stream.
* Note that we have already done the initial check on this
* loop prior to calling this function.
*/
do {
bits++;
q &= lower_bits_rubin;
q <<= 1;
p <<= 1;
} while ((q >= UPPER_BIT_RUBIN) || ((p + q) <= UPPER_BIT_RUBIN));
 
rs->p = p;
rs->q = q;
 
rs->bit_number += bits;
 
/*
* Now get the bits. We really want this to be "get n bits".
*/
rec_q = rs->rec_q;
do {
c = pullbit(&rs->pp);
rec_q &= lower_bits_rubin;
rec_q <<= 1;
rec_q += c;
} while (--bits);
rs->rec_q = rec_q;
}
 
static int decode(struct rubin_state *rs, long A, long B)
{
unsigned long p = rs->p, q = rs->q;
long i0, threshold;
int symbol;
 
if (q >= UPPER_BIT_RUBIN || ((p + q) <= UPPER_BIT_RUBIN))
__do_decode(rs, p, q);
 
i0 = A * rs->p / (A + B);
if (i0 <= 0) {
i0 = 1;
}
if (i0 >= rs->p) {
i0 = rs->p - 1;
}
 
threshold = rs->q + i0;
symbol = rs->rec_q >= threshold;
if (rs->rec_q >= threshold) {
rs->q += i0;
i0 = rs->p - i0;
}
 
rs->p = i0;
 
return symbol;
}
 
 
 
static int out_byte(struct rubin_state *rs, unsigned char byte)
{
int i, ret;
struct rubin_state rs_copy;
rs_copy = *rs;
 
for (i=0;i<8;i++) {
ret = encode(rs, rs->bit_divider-rs->bits[i],rs->bits[i],byte&1);
if (ret) {
/* Failed. Restore old state */
*rs = rs_copy;
return ret;
}
byte=byte>>1;
}
return 0;
}
 
static int in_byte(struct rubin_state *rs)
{
int i, result = 0, bit_divider = rs->bit_divider;
 
for (i = 0; i < 8; i++)
result |= decode(rs, bit_divider - rs->bits[i], rs->bits[i]) << i;
 
return result;
}
 
 
 
static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen)
{
int outpos = 0;
int pos=0;
struct rubin_state rs;
 
init_pushpull(&rs.pp, cpage_out, *dstlen * 8, 0, 32);
 
init_rubin(&rs, bit_divider, bits);
while (pos < (*sourcelen) && !out_byte(&rs, data_in[pos]))
pos++;
end_rubin(&rs);
 
if (outpos > pos) {
/* We failed */
return -1;
}
/* Tell the caller how much we managed to compress,
* and how much space it took */
outpos = (pushedbits(&rs.pp)+7)/8;
if (outpos >= pos)
return -1; /* We didn't actually compress */
*sourcelen = pos;
*dstlen = outpos;
return 0;
}
#if 0
/* _compress returns the compressed size, -1 if bigger */
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
}
#endif
int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
int bits[8];
unsigned char histo[256];
int i;
int ret;
uint32_t mysrclen, mydstlen;
 
mysrclen = *sourcelen;
mydstlen = *dstlen - 8;
 
if (*dstlen <= 12)
return -1;
 
memset(histo, 0, 256);
for (i=0; i<mysrclen; i++) {
histo[data_in[i]]++;
}
memset(bits, 0, sizeof(int)*8);
for (i=0; i<256; i++) {
if (i&128)
bits[7] += histo[i];
if (i&64)
bits[6] += histo[i];
if (i&32)
bits[5] += histo[i];
if (i&16)
bits[4] += histo[i];
if (i&8)
bits[3] += histo[i];
if (i&4)
bits[2] += histo[i];
if (i&2)
bits[1] += histo[i];
if (i&1)
bits[0] += histo[i];
}
 
for (i=0; i<8; i++) {
bits[i] = (bits[i] * 256) / mysrclen;
if (!bits[i]) bits[i] = 1;
if (bits[i] > 255) bits[i] = 255;
cpage_out[i] = bits[i];
}
 
ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen, &mydstlen);
if (ret)
return ret;
 
/* Add back the 8 bytes we took for the probabilities */
mydstlen += 8;
 
if (mysrclen <= mydstlen) {
/* We compressed */
return -1;
}
 
*sourcelen = mysrclen;
*dstlen = mydstlen;
return 0;
}
 
static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
unsigned char *page_out, uint32_t srclen, uint32_t destlen)
{
int outpos = 0;
struct rubin_state rs;
init_pushpull(&rs.pp, cdata_in, srclen, 0, 0);
init_decode(&rs, bit_divider, bits);
while (outpos < destlen) {
page_out[outpos++] = in_byte(&rs);
}
}
 
 
void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen)
{
rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
}
 
void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen)
{
int bits[8];
int c;
 
for (c=0; c<8; c++)
bits[c] = data_in[c];
 
rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
}
/jffs2/v2_0/src/compr.c
0,0 → 1,138
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by Arjan van de Ven <arjanv@redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: compr.c,v 1.1.1.1 2004-02-14 13:29:18 phoenix Exp $
*
*/
 
#if defined(__KERNEL__) || defined (__ECOS)
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/types.h>
#else
#define KERN_DEBUG
#define KERN_NOTICE
#define KERN_WARNING
#define printk printf
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#endif
 
#include <linux/jffs2.h>
 
int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
 
 
/* jffs2_compress:
* @data: Pointer to uncompressed data
* @cdata: Pointer to buffer for compressed data
* @datalen: On entry, holds the amount of data available for compression.
* On exit, expected to hold the amount of data actually compressed.
* @cdatalen: On entry, holds the amount of space available for compressed
* data. On exit, expected to hold the actual size of the compressed
* data.
*
* Returns: Byte to be stored with data indicating compression type used.
* Zero is used to show that the data could not be compressed - the
* compressed version was actually larger than the original.
*
* If the cdata buffer isn't large enough to hold all the uncompressed data,
* jffs2_compress should compress as much as will fit, and should set
* *datalen accordingly to show the amount of data which were compressed.
*/
unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *datalen, uint32_t *cdatalen)
{
int ret;
 
ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_ZLIB;
}
#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
ret = jffs2_dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_DYNRUBIN;
}
#endif
#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_RUBINMIPS;
}
#endif
/* rtime does manage to recompress already-compressed data */
ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_RTIME;
}
#if 0
/* We don't need to copy. Let the caller special-case the COMPR_NONE case. */
/* If we get here, no compression is going to work */
/* But we might want to use the fragmentation part -- Arjan */
memcpy(cpage_out,data_in,min(*datalen,*cdatalen));
if (*datalen > *cdatalen)
*datalen = *cdatalen;
#endif
return JFFS2_COMPR_NONE; /* We failed to compress */
 
}
 
 
int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
{
switch (comprtype) {
case JFFS2_COMPR_NONE:
/* This should be special-cased elsewhere, but we might as well deal with it */
memcpy(data_out, cdata_in, datalen);
break;
 
case JFFS2_COMPR_ZERO:
memset(data_out, 0, datalen);
break;
 
case JFFS2_COMPR_ZLIB:
jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
break;
 
case JFFS2_COMPR_RTIME:
jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
break;
 
case JFFS2_COMPR_RUBINMIPS:
#if 0 /* Disabled 23/9/1 */
jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
#else
printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
#endif
break;
case JFFS2_COMPR_DYNRUBIN:
#if 1 /* Phase this one out */
jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
#else
printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
#endif
break;
 
default:
printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
return -EIO;
}
return 0;
}
/jffs2/v2_0/src/compr_rtime.c
0,0 → 1,104
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by Arjan van de Ven <arjanv@redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: compr_rtime.c,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $
*
*
* Very simple lz77-ish encoder.
*
* Theory of operation: Both encoder and decoder have a list of "last
* occurances" for every possible source-value; after sending the
* first source-byte, the second byte indicated the "run" length of
* matches
*
* The algorithm is intended to only send "whole bytes", no bit-messing.
*
*/
 
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/string.h>
 
/* _compress returns the compressed size, -1 if bigger */
int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
int positions[256];
int outpos = 0;
int pos=0;
 
memset(positions,0,sizeof(positions));
while (pos < (*sourcelen) && outpos <= (*dstlen)-2) {
int backpos, runlen=0;
unsigned char value;
value = data_in[pos];
 
cpage_out[outpos++] = data_in[pos++];
backpos = positions[value];
positions[value]=pos;
while ((backpos < pos) && (pos < (*sourcelen)) &&
(data_in[pos]==data_in[backpos++]) && (runlen<255)) {
pos++;
runlen++;
}
cpage_out[outpos++] = runlen;
}
 
if (outpos >= pos) {
/* We failed */
return -1;
}
/* Tell the caller how much we managed to compress, and how much space it took */
*sourcelen = pos;
*dstlen = outpos;
return 0;
}
 
 
void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen)
{
int positions[256];
int outpos = 0;
int pos=0;
memset(positions,0,sizeof(positions));
while (outpos<destlen) {
unsigned char value;
int backoffs;
int repeat;
value = data_in[pos++];
cpage_out[outpos++] = value; /* first the verbatim copied byte */
repeat = data_in[pos++];
backoffs = positions[value];
positions[value]=outpos;
if (repeat) {
if (backoffs + repeat >= outpos) {
while(repeat) {
cpage_out[outpos++] = cpage_out[backoffs++];
repeat--;
}
} else {
memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat);
outpos+=repeat;
}
}
}
}
 
 
/jffs2/v2_0/src/pushpull.h
0,0 → 1,72
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: pushpull.h,v 1.1.1.1 2004-02-14 13:29:20 phoenix Exp $
*
*/
 
#ifndef __PUSHPULL_H__
#define __PUSHPULL_H__
 
#include <linux/errno.h>
 
struct pushpull {
unsigned char *buf;
unsigned int buflen;
unsigned int ofs;
unsigned int reserve;
};
 
 
static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
{
pp->buf = buf;
pp->buflen = buflen;
pp->ofs = ofs;
pp->reserve = reserve;
}
 
static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
{
if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
return -ENOSPC;
}
 
if (bit) {
pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
}
else {
pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
}
pp->ofs++;
 
return 0;
}
 
static inline int pushedbits(struct pushpull *pp)
{
return pp->ofs;
}
 
static inline int pullbit(struct pushpull *pp)
{
int bit;
 
bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1;
 
pp->ofs++;
return bit;
}
 
static inline int pulledbits(struct pushpull *pp)
{
return pp->ofs;
}
 
#endif /* __PUSHPULL_H__ */
/jffs2/v2_0/src/nodelist.c
0,0 → 1,640
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodelist.c,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/mtd/mtd.h>
#include <linux/rbtree.h>
#include <linux/crc32.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include "nodelist.h"
 
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
{
struct jffs2_full_dirent **prev = list;
D1(printk(KERN_DEBUG "jffs2_add_fd_to_list( %p, %p (->%p))\n", new, list, *list));
 
while ((*prev) && (*prev)->nhash <= new->nhash) {
if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) {
/* Duplicate. Free one */
if (new->version < (*prev)->version) {
D1(printk(KERN_DEBUG "Eep! Marking new dirent node obsolete\n"));
D1(printk(KERN_DEBUG "New dirent is \"%s\"->ino #%u. Old is \"%s\"->ino #%u\n", new->name, new->ino, (*prev)->name, (*prev)->ino));
jffs2_mark_node_obsolete(c, new->raw);
jffs2_free_full_dirent(new);
} else {
D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) obsolete\n", (*prev)->ino));
new->next = (*prev)->next;
jffs2_mark_node_obsolete(c, ((*prev)->raw));
jffs2_free_full_dirent(*prev);
*prev = new;
}
goto out;
}
prev = &((*prev)->next);
}
new->next = *prev;
*prev = new;
 
out:
D2(while(*list) {
printk(KERN_DEBUG "Dirent \"%s\" (hash 0x%08x, ino #%u\n", (*list)->name, (*list)->nhash, (*list)->ino);
list = &(*list)->next;
});
}
 
/* Put a new tmp_dnode_info into the list, keeping the list in
order of increasing version
*/
void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
{
struct jffs2_tmp_dnode_info **prev = list;
while ((*prev) && (*prev)->version < tn->version) {
prev = &((*prev)->next);
}
tn->next = (*prev);
*prev = tn;
}
 
static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
{
struct jffs2_tmp_dnode_info *next;
 
while (tn) {
next = tn;
tn = tn->next;
jffs2_free_full_dnode(next->fn);
jffs2_free_tmp_dnode_info(next);
}
}
 
static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
{
struct jffs2_full_dirent *next;
 
while (fd) {
next = fd->next;
jffs2_free_full_dirent(fd);
fd = next;
}
}
 
 
/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
with this ino, returning the former in order of version */
 
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
uint32_t *highest_version, uint32_t *latest_mctime,
uint32_t *mctime_ver)
{
struct jffs2_raw_node_ref *ref = f->inocache->nodes;
struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
struct jffs2_full_dirent *fd, *ret_fd = NULL;
 
union jffs2_node_union node;
size_t retlen;
int err;
 
*mctime_ver = 0;
 
D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino));
if (!f->inocache->nodes) {
printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino);
}
 
spin_lock(&c->erase_completion_lock);
 
for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) {
/* Work out whether it's a data node or a dirent node */
if (ref_obsolete(ref)) {
/* FIXME: On NAND flash we may need to read these */
D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
continue;
}
/* We can hold a pointer to a non-obsolete node without the spinlock,
but _obsolete_ nodes may disappear at any time, if the block
they're in gets erased */
spin_unlock(&c->erase_completion_lock);
 
cond_resched();
 
/* FIXME: point() */
err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, ref->totlen, sizeof(node)), &retlen, (void *)&node);
if (err) {
printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
goto free_out;
}
 
/* Check we've managed to read at least the common node header */
if (retlen < min_t(uint32_t, ref->totlen, sizeof(node.u))) {
printk(KERN_WARNING "short read in get_inode_nodes()\n");
err = -EIO;
goto free_out;
}
switch (je16_to_cpu(node.u.nodetype)) {
case JFFS2_NODETYPE_DIRENT:
D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
if (ref_flags(ref) == REF_UNCHECKED) {
printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref));
BUG();
}
if (retlen < sizeof(node.d)) {
printk(KERN_WARNING "short read in get_inode_nodes()\n");
err = -EIO;
goto free_out;
}
if (je32_to_cpu(node.d.version) > *highest_version)
*highest_version = je32_to_cpu(node.d.version);
if (ref_obsolete(ref)) {
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n",
ref_offset(ref));
BUG();
}
fd = jffs2_alloc_full_dirent(node.d.nsize+1);
if (!fd) {
err = -ENOMEM;
goto free_out;
}
memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1);
fd->raw = ref;
fd->version = je32_to_cpu(node.d.version);
fd->ino = je32_to_cpu(node.d.ino);
fd->type = node.d.type;
 
/* Pick out the mctime of the latest dirent */
if(fd->version > *mctime_ver) {
*mctime_ver = fd->version;
*latest_mctime = je32_to_cpu(node.d.mctime);
}
 
/* memcpy as much of the name as possible from the raw
dirent we've already read from the flash
*/
if (retlen > sizeof(struct jffs2_raw_dirent))
memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
/* Do we need to copy any more of the name directly
from the flash?
*/
if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
/* FIXME: point() */
int already = retlen - sizeof(struct jffs2_raw_dirent);
err = jffs2_flash_read(c, (ref_offset(ref)) + retlen,
node.d.nsize - already, &retlen, &fd->name[already]);
if (!err && retlen != node.d.nsize - already)
err = -EIO;
if (err) {
printk(KERN_WARNING "Read remainder of name in jffs2_get_inode_nodes(): error %d\n", err);
jffs2_free_full_dirent(fd);
goto free_out;
}
}
fd->nhash = full_name_hash(fd->name, node.d.nsize);
fd->next = NULL;
/* Wheee. We now have a complete jffs2_full_dirent structure, with
the name in it and everything. Link it into the list
*/
D1(printk(KERN_DEBUG "Adding fd \"%s\", ino #%u\n", fd->name, fd->ino));
jffs2_add_fd_to_list(c, fd, &ret_fd);
break;
 
case JFFS2_NODETYPE_INODE:
D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
if (retlen < sizeof(node.i)) {
printk(KERN_WARNING "read too short for dnode\n");
err = -EIO;
goto free_out;
}
if (je32_to_cpu(node.i.version) > *highest_version)
*highest_version = je32_to_cpu(node.i.version);
D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version));
 
if (ref_obsolete(ref)) {
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n",
ref_offset(ref));
BUG();
}
 
/* If we've never checked the CRCs on this node, check them now. */
if (ref_flags(ref) == REF_UNCHECKED) {
uint32_t crc;
struct jffs2_eraseblock *jeb;
 
crc = crc32(0, &node, sizeof(node.i)-8);
if (crc != je32_to_cpu(node.i.node_crc)) {
printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(ref), je32_to_cpu(node.i.node_crc), crc);
jffs2_mark_node_obsolete(c, ref);
spin_lock(&c->erase_completion_lock);
continue;
}
 
if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) {
unsigned char *buf=NULL;
uint32_t pointed = 0;
#ifndef __ECOS
if (c->mtd->point) {
err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
&retlen, &buf);
if (!err && retlen < je32_to_cpu(node.i.csize)) {
D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
} else if (err){
D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
} else
pointed = 1; /* succefully pointed to device */
}
#endif
if(!pointed){
buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
if (!buf)
return -ENOMEM;
err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
&retlen, buf);
if (!err && retlen != je32_to_cpu(node.i.csize))
err = -EIO;
if (err) {
kfree(buf);
return err;
}
}
crc = crc32(0, buf, je32_to_cpu(node.i.csize));
if(!pointed)
kfree(buf);
#ifndef __ECOS
else
c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
#endif
 
if (crc != je32_to_cpu(node.i.data_crc)) {
printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(ref), je32_to_cpu(node.i.data_crc), crc);
jffs2_mark_node_obsolete(c, ref);
spin_lock(&c->erase_completion_lock);
continue;
}
}
 
/* Mark the node as having been checked and fix the accounting accordingly */
spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[ref->flash_offset / c->sector_size];
jeb->used_size += ref->totlen;
jeb->unchecked_size -= ref->totlen;
c->used_size += ref->totlen;
c->unchecked_size -= ref->totlen;
 
/* If node covers at least a whole page, or if it starts at the
beginning of a page and runs to the end of the file, or if
it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
 
If it's actually overlapped, it'll get made NORMAL (or OBSOLETE)
when the overlapping node(s) get added to the tree anyway.
*/
if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) ||
( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) &&
(je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) {
D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref)));
ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
} else {
D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref)));
ref->flash_offset = ref_offset(ref) | REF_NORMAL;
}
spin_unlock(&c->erase_completion_lock);
}
 
tn = jffs2_alloc_tmp_dnode_info();
if (!tn) {
D1(printk(KERN_DEBUG "alloc tn failed\n"));
err = -ENOMEM;
goto free_out;
}
 
tn->fn = jffs2_alloc_full_dnode();
if (!tn->fn) {
D1(printk(KERN_DEBUG "alloc fn failed\n"));
err = -ENOMEM;
jffs2_free_tmp_dnode_info(tn);
goto free_out;
}
tn->version = je32_to_cpu(node.i.version);
tn->fn->ofs = je32_to_cpu(node.i.offset);
/* There was a bug where we wrote hole nodes out with
csize/dsize swapped. Deal with it */
if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize))
tn->fn->size = je32_to_cpu(node.i.csize);
else // normal case...
tn->fn->size = je32_to_cpu(node.i.dsize);
tn->fn->raw = ref;
D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n",
ref_offset(ref), je32_to_cpu(node.i.version),
je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize)));
jffs2_add_tn_to_list(tn, &ret_tn);
break;
 
default:
if (ref_flags(ref) == REF_UNCHECKED) {
struct jffs2_eraseblock *jeb;
 
printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
je16_to_cpu(node.u.nodetype), ref_offset(ref));
 
/* Mark the node as having been checked and fix the accounting accordingly */
spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[ref->flash_offset / c->sector_size];
jeb->used_size += ref->totlen;
jeb->unchecked_size -= ref->totlen;
c->used_size += ref->totlen;
c->unchecked_size -= ref->totlen;
 
mark_ref_normal(ref);
spin_unlock(&c->erase_completion_lock);
}
node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype));
if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) {
/* Hmmm. This should have been caught at scan time. */
printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n",
ref_offset(ref));
printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n",
je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen),
je32_to_cpu(node.u.hdr_crc));
jffs2_mark_node_obsolete(c, ref);
} else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) {
case JFFS2_FEATURE_INCOMPAT:
printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
/* EEP */
BUG();
break;
case JFFS2_FEATURE_ROCOMPAT:
printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
if (!(c->flags & JFFS2_SB_FLAG_RO))
BUG();
break;
case JFFS2_FEATURE_RWCOMPAT_COPY:
printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
break;
case JFFS2_FEATURE_RWCOMPAT_DELETE:
printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
jffs2_mark_node_obsolete(c, ref);
break;
}
 
}
spin_lock(&c->erase_completion_lock);
 
}
spin_unlock(&c->erase_completion_lock);
*tnp = ret_tn;
*fdp = ret_fd;
 
return 0;
 
free_out:
jffs2_free_tmp_dnode_info_list(ret_tn);
jffs2_free_full_dirent_list(ret_fd);
return err;
}
 
void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
{
spin_lock(&c->inocache_lock);
ic->state = state;
wake_up(&c->inocache_wq);
spin_unlock(&c->inocache_lock);
}
 
/* During mount, this needs no locking. During normal operation, its
callers want to do other stuff while still holding the inocache_lock.
Rather than introducing special case get_ino_cache functions or
callbacks, we just let the caller do the locking itself. */
struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino)
{
struct jffs2_inode_cache *ret;
 
D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
 
ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
while (ret && ret->ino < ino) {
ret = ret->next;
}
if (ret && ret->ino != ino)
ret = NULL;
 
D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino));
return ret;
}
 
void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new)
{
struct jffs2_inode_cache **prev;
D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
spin_lock(&c->inocache_lock);
prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
 
while ((*prev) && (*prev)->ino < new->ino) {
prev = &(*prev)->next;
}
new->next = *prev;
*prev = new;
 
spin_unlock(&c->inocache_lock);
}
 
void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
{
struct jffs2_inode_cache **prev;
D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
spin_lock(&c->inocache_lock);
prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
while ((*prev) && (*prev)->ino < old->ino) {
prev = &(*prev)->next;
}
if ((*prev) == old) {
*prev = old->next;
}
 
spin_unlock(&c->inocache_lock);
}
 
void jffs2_free_ino_caches(struct jffs2_sb_info *c)
{
int i;
struct jffs2_inode_cache *this, *next;
for (i=0; i<INOCACHE_HASHSIZE; i++) {
this = c->inocache_list[i];
while (this) {
next = this->next;
D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
jffs2_free_inode_cache(this);
this = next;
}
c->inocache_list[i] = NULL;
}
}
 
void jffs2_free_raw_node_refs(struct jffs2_sb_info *c)
{
int i;
struct jffs2_raw_node_ref *this, *next;
 
for (i=0; i<c->nr_blocks; i++) {
this = c->blocks[i].first_node;
while(this) {
next = this->next_phys;
jffs2_free_raw_node_ref(this);
this = next;
}
c->blocks[i].first_node = c->blocks[i].last_node = NULL;
}
}
struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
{
/* The common case in lookup is that there will be a node
which precisely matches. So we go looking for that first */
struct rb_node *next;
struct jffs2_node_frag *prev = NULL;
struct jffs2_node_frag *frag = NULL;
 
D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset));
 
next = fragtree->rb_node;
 
while(next) {
frag = rb_entry(next, struct jffs2_node_frag, rb);
 
D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n",
frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right));
if (frag->ofs + frag->size <= offset) {
D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n",
frag->ofs, frag->ofs+frag->size));
/* Remember the closest smaller match on the way down */
if (!prev || frag->ofs > prev->ofs)
prev = frag;
next = frag->rb.rb_right;
} else if (frag->ofs > offset) {
D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n",
frag->ofs, frag->ofs+frag->size));
next = frag->rb.rb_left;
} else {
D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n",
frag->ofs, frag->ofs+frag->size));
return frag;
}
}
 
/* Exact match not found. Go back up looking at each parent,
and return the closest smaller one */
 
if (prev)
D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n",
prev->ofs, prev->ofs+prev->size));
else
D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n"));
return prev;
}
 
/* Pass 'c' argument to indicate that nodes should be marked obsolete as
they're killed. */
void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
{
struct jffs2_node_frag *frag;
struct jffs2_node_frag *parent;
 
if (!root->rb_node)
return;
 
frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
 
while(frag) {
if (frag->rb.rb_left) {
D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n",
frag, frag->ofs, frag->ofs+frag->size));
frag = frag_left(frag);
continue;
}
if (frag->rb.rb_right) {
D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n",
frag, frag->ofs, frag->ofs+frag->size));
frag = frag_right(frag);
continue;
}
 
D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n",
frag->ofs, frag->ofs+frag->size, frag->node,
frag->node?frag->node->frags:0));
if (frag->node && !(--frag->node->frags)) {
/* Not a hole, and it's the final remaining frag
of this node. Free the node */
if (c)
jffs2_mark_node_obsolete(c, frag->node->raw);
jffs2_free_full_dnode(frag->node);
}
parent = frag_parent(frag);
if (parent) {
if (frag_left(parent) == frag)
parent->rb.rb_left = NULL;
else
parent->rb.rb_right = NULL;
}
 
jffs2_free_node_frag(frag);
frag = parent;
}
}
 
void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
{
struct rb_node *parent = &base->rb;
struct rb_node **link = &parent;
 
D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag,
newfrag->ofs, newfrag->ofs+newfrag->size, base));
 
while (*link) {
parent = *link;
base = rb_entry(parent, struct jffs2_node_frag, rb);
D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs));
if (newfrag->ofs > base->ofs)
link = &base->rb.rb_right;
else if (newfrag->ofs < base->ofs)
link = &base->rb.rb_left;
else {
printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
BUG();
}
}
 
rb_link_node(&newfrag->rb, &base->rb, link);
}
/jffs2/v2_0/src/LICENCE
0,0 → 1,35
The files in this directory and elsewhere which refer to this LICENCE
file are part of JFFS2, the Journalling Flash File System v2.
 
Copyright (C) 2001, 2002 Red Hat, Inc.
 
JFFS2 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.
 
JFFS2 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 JFFS2; 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 these files, or you compile these
files and link them with other works to produce a work based on these
files, these files do not by themselves cause the resulting work to be
covered by the GNU General Public License. However the source code for
these files 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.
 
For information on obtaining alternative licences for JFFS2, see
http://sources.redhat.com/jffs2/jffs2-licence.html
 
 
$Id: LICENCE,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $
/jffs2/v2_0/src/histo_mips.h
0,0 → 1,2
#define BIT_DIVIDER_MIPS 1043
static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */
/jffs2/v2_0/src/jffs2port.h
0,0 → 1,241
#ifndef __LINUX_JFFS2PORT_H__
#define __LINUX_JFFS2PORT_H__
 
/* $Id: jffs2port.h,v 1.1.1.1 2004-02-14 13:29:18 phoenix Exp $ */
 
#include <pkgconf/system.h>
#include <pkgconf/hal.h>
//#include <pkgconf/kernel.h>
#include <pkgconf/io_fileio.h>
//#include <pkgconf/fs_ram.h>
 
#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/hal/drv_api.h>
#include <cyg/infra/diag.h>
 
#include <cyg/io/flash.h>
 
#include <pkgconf/fs_jffs2.h>
#include <linux/types.h>
#include <linux/list.h>
#include <asm/bug.h>
// Linux types
#define printf diag_printf
 
// Structures used by VFS
 
typedef unsigned short kdev_t;
 
struct qstr {
const unsigned char * name;
unsigned int len;
unsigned int hash;
};
 
#define DNAME_INLINE_LEN 16
 
struct dentry {
// atomic_t d_count;
unsigned int d_flags;
struct inode * d_inode; /* Where the name belongs to - NULL is negative */
struct dentry * d_parent; /* parent directory */
struct list_head d_hash; /* lookup hash list */
struct list_head d_child; /* child of parent list */
struct list_head d_subdirs; /* our children */
struct list_head d_alias; /* inode alias list */
struct qstr d_name;
struct dentry_operations *d_op;
struct super_block * d_sb; /* The root of the dentry tree */
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
};
 
struct file {
struct dentry *f_dentry;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
};
 
#define ATTR_MODE 1
#define ATTR_UID 2
#define ATTR_GID 4
#define ATTR_SIZE 8
#define ATTR_ATIME 16
#define ATTR_MTIME 32
#define ATTR_CTIME 64
#define ATTR_ATIME_SET 128
#define ATTR_MTIME_SET 256
#define ATTR_FORCE 512 /* Not a change, but a change it */
#define ATTR_ATTR_FLAG 1024
 
typedef unsigned short umode_t;
 
struct iattr {
unsigned int ia_valid;
umode_t ia_mode;
uid_t ia_uid;
gid_t ia_gid;
loff_t ia_size;
time_t ia_atime;
time_t ia_mtime;
time_t ia_ctime;
};
 
struct page {
unsigned long index;
void *virtual;
};
 
struct nameidata {
struct dentry *dentry;
struct qstr last;
unsigned int flags;
int last_type;
};
 
struct file_operations {
//struct module *owner;
//loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
//ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, char *, int);
//unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
//int (*mmap) (struct file *, struct vm_area_struct *);
//int (*open) (struct inode *, struct file *);
//int (*flush) (struct file *);
//int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
//int (*fasync) (int, struct file *, int);
//int (*lock) (struct file *, int, struct file_lock *);
//ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
//ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
};
 
struct inode_operations {
int (*create) (struct inode *,struct dentry *,int);
struct dentry * (*lookup) (struct inode *,struct dentry *);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);
int (*mkdir) (struct inode *,struct dentry *,int);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,int,int);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
int (*readlink) (struct dentry *, char *,int);
int (*follow_link) (struct dentry *, struct nameidata *);
//void (*truncate) (struct inode *);
int (*permission) (struct inode *, int);
//int (*revalidate) (struct dentry *);
int (*setattr) (struct dentry *, struct iattr *);
//int (*getattr) (struct dentry *, struct iattr *);
};
 
 
struct iovec {
void *iov_base;
ssize_t iov_len;
};
 
 
// called by JFFS2
 
#define to_kdev_t(rdev) (rdev)
#define MAJOR(rdev) (rdev)>>8
#define MINOR(rdev) (rdev)
 
#define page_address(page) ((page)->virtual)
 
static __inline__ void * kmap(struct page * page) {
return page_address(page);
}
 
#define kunmap(page) do { } while (0)
 
//struct page * read_cache_page(cyg_uint32 start, void * f, struct inode * i);
struct page *read_cache_page(unsigned long index, int (*filler)(void *,struct page*), void *data);
void page_cache_release(struct page * pg);
 
struct inode * new_inode(struct super_block *sb);
struct inode * iget(struct super_block *sb, cyg_uint32 ino);
void iput(struct inode * i);
void make_bad_inode(struct inode * inode);
int is_bad_inode(struct inode * inode);
 
#define insert_inode_hash(inode) do { } while (0)
 
#define d_alloc_root(root_inode) root_inode
 
#define flush_dcache_page(page) do { } while (0)
 
struct jffs2_sb_info;
struct jffs2_eraseblock;
 
cyg_bool jffs2_flash_read(struct jffs2_sb_info *c, cyg_uint32 read_buffer_offset, const size_t size, size_t * return_size, char * write_buffer);
cyg_bool jffs2_flash_write(struct jffs2_sb_info *c, cyg_uint32 write_buffer_offset, const size_t size, size_t * return_size, char * read_buffer);
int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
cyg_bool jffs2_flash_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 
 
// calls to JFFS2
 
// dir-ecos.c
struct inode *jffs2_lookup(struct inode *dir_i, struct qstr *name);
int jffs2_readdir (struct inode *d_inode, unsigned long f_pos, char *nbuf, int nlen);
int jffs2_create(struct inode *dir_i, struct qstr *d_name, int mode, struct inode **new_i);
int jffs2_mkdir (struct inode *dir_i, struct qstr *d_name, int mode, struct inode **new_i);
int jffs2_link (struct inode *old_d_inode, struct inode *dir_i, struct qstr *d_name);
int jffs2_unlink(struct inode *dir_i, struct inode *d_inode, struct qstr *d_name);
int jffs2_rmdir (struct inode *dir_i, struct inode *d_inode, struct qstr *d_name);
int jffs2_rename (struct inode *old_dir_i, struct inode *d_inode, struct qstr *old_d_name,
struct inode *new_dir_i, struct qstr *new_d_name);
 
#define init_name_hash() 0
static inline unsigned long partial_name_hash(unsigned long c, unsigned long prevhash)
{
prevhash = (prevhash << 4) | (prevhash >> (8*sizeof(unsigned long)-4));
return prevhash ^ c;
}
 
static inline unsigned long end_name_hash(unsigned long hash)
{
if (sizeof(hash) > sizeof(unsigned int))
hash += hash >> 4*sizeof(hash);
return (unsigned int) hash;
}
 
static inline unsigned int full_name_hash(const unsigned char * name, unsigned int len) {
 
unsigned long hash = init_name_hash();
while (len--)
hash = partial_name_hash(*name++, hash);
return end_name_hash(hash);
}
 
#endif /* __LINUX_JFFS2PORT_H__ */
 
 
 
 
 
 
 
 
 
 
/jffs2/v2_0/src/compr_rubin.h
0,0 → 1,21
/* Rubin encoder/decoder header */
/* work started at : aug 3, 1994 */
/* last modification : aug 15, 1994 */
/* $Id: compr_rubin.h,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $ */
 
#include "pushpull.h"
 
#define RUBIN_REG_SIZE 16
#define UPPER_BIT_RUBIN (((long) 1)<<(RUBIN_REG_SIZE-1))
#define LOWER_BITS_RUBIN ((((long) 1)<<(RUBIN_REG_SIZE-1))-1)
 
 
struct rubin_state {
unsigned long p;
unsigned long q;
unsigned long rec_q;
long bit_number;
struct pushpull pp;
int bit_divider;
int bits[8];
};
/jffs2/v2_0/src/scan.c
0,0 → 1,891
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: scan.c,v 1.1.1.1 2004-02-14 13:29:20 phoenix Exp $
*
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include <linux/compiler.h>
#include "nodelist.h"
 
#define EMPTY_SCAN_SIZE 1024
 
#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
c->free_size -= _x; c->dirty_size += _x; \
jeb->free_size -= _x ; jeb->dirty_size += _x; \
}while(0)
#define USED_SPACE(x) do { typeof(x) _x = (x); \
c->free_size -= _x; c->used_size += _x; \
jeb->free_size -= _x ; jeb->used_size += _x; \
}while(0)
#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
c->free_size -= _x; c->unchecked_size += _x; \
jeb->free_size -= _x ; jeb->unchecked_size += _x; \
}while(0)
 
#define noisy_printk(noise, args...) do { \
if (*(noise)) { \
printk(KERN_NOTICE args); \
(*(noise))--; \
if (!(*(noise))) { \
printk(KERN_NOTICE "Further such events for this erase block will not be printed\n"); \
} \
} \
} while(0)
 
static uint32_t pseudo_random;
 
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
unsigned char *buf, uint32_t buf_size);
 
/* These helper functions _must_ increase ofs and also do the dirty/used space accounting.
* Returning an error will abort the mount - bad checksums etc. should just mark the space
* as dirty.
*/
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_inode *ri, uint32_t ofs);
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_dirent *rd, uint32_t ofs);
 
#define BLK_STATE_ALLFF 0
#define BLK_STATE_CLEAN 1
#define BLK_STATE_PARTDIRTY 2
#define BLK_STATE_CLEANMARKER 3
#define BLK_STATE_ALLDIRTY 4
#define BLK_STATE_BADBLOCK 5
 
int jffs2_scan_medium(struct jffs2_sb_info *c)
{
int i, ret;
uint32_t empty_blocks = 0, bad_blocks = 0;
unsigned char *flashbuf = NULL;
uint32_t buf_size = 0;
#ifndef __ECOS
size_t pointlen;
 
if (c->mtd->point) {
ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf);
if (!ret && pointlen < c->mtd->size) {
/* Don't muck about if it won't let us point to the whole flash */
D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
flashbuf = NULL;
}
if (ret)
D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
}
#endif
if (!flashbuf) {
/* For NAND it's quicker to read a whole eraseblock at a time,
apparently */
if (jffs2_cleanmarker_oob(c))
buf_size = c->sector_size;
else
buf_size = PAGE_SIZE;
 
D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
flashbuf = kmalloc(buf_size, GFP_KERNEL);
if (!flashbuf)
return -ENOMEM;
}
 
for (i=0; i<c->nr_blocks; i++) {
struct jffs2_eraseblock *jeb = &c->blocks[i];
 
ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size);
 
if (ret < 0)
return ret;
 
ACCT_PARANOIA_CHECK(jeb);
 
/* Now decide which list to put it on */
switch(ret) {
case BLK_STATE_ALLFF:
/*
* Empty block. Since we can't be sure it
* was entirely erased, we just queue it for erase
* again. It will be marked as such when the erase
* is complete. Meanwhile we still count it as empty
* for later checks.
*/
empty_blocks++;
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
break;
 
case BLK_STATE_CLEANMARKER:
/* Only a CLEANMARKER node is valid */
if (!jeb->dirty_size) {
/* It's actually free */
list_add(&jeb->list, &c->free_list);
c->nr_free_blocks++;
} else {
/* Dirt */
D1(printk(KERN_DEBUG "Adding all-dirty block at 0x%08x to erase_pending_list\n", jeb->offset));
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
}
break;
 
case BLK_STATE_CLEAN:
/* Full (or almost full) of clean data. Clean list */
list_add(&jeb->list, &c->clean_list);
break;
 
case BLK_STATE_PARTDIRTY:
/* Some data, but not full. Dirty list. */
/* Except that we want to remember the block with most free space,
and stick it in the 'nextblock' position to start writing to it.
Later when we do snapshots, this must be the most recent block,
not the one with most free space.
*/
if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) &&
(jffs2_can_mark_obsolete(c) || jeb->free_size > c->wbuf_pagesize) &&
(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
/* Better candidate for the next writes to go to */
if (c->nextblock) {
c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
c->free_size -= c->nextblock->free_size;
c->wasted_size -= c->nextblock->wasted_size;
c->nextblock->free_size = c->nextblock->wasted_size = 0;
if (VERYDIRTY(c, c->nextblock->dirty_size)) {
list_add(&c->nextblock->list, &c->very_dirty_list);
} else {
list_add(&c->nextblock->list, &c->dirty_list);
}
}
c->nextblock = jeb;
} else {
jeb->dirty_size += jeb->free_size + jeb->wasted_size;
c->dirty_size += jeb->free_size + jeb->wasted_size;
c->free_size -= jeb->free_size;
c->wasted_size -= jeb->wasted_size;
jeb->free_size = jeb->wasted_size = 0;
if (VERYDIRTY(c, jeb->dirty_size)) {
list_add(&jeb->list, &c->very_dirty_list);
} else {
list_add(&jeb->list, &c->dirty_list);
}
}
break;
 
case BLK_STATE_ALLDIRTY:
/* Nothing valid - not even a clean marker. Needs erasing. */
/* For now we just put it on the erasing list. We'll start the erases later */
D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
break;
case BLK_STATE_BADBLOCK:
D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
list_add(&jeb->list, &c->bad_list);
c->bad_size += c->sector_size;
c->free_size -= c->sector_size;
bad_blocks++;
break;
default:
printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n");
BUG();
}
}
/* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
if (c->nextblock && (c->nextblock->dirty_size)) {
c->nextblock->wasted_size += c->nextblock->dirty_size;
c->wasted_size += c->nextblock->dirty_size;
c->dirty_size -= c->nextblock->dirty_size;
c->nextblock->dirty_size = 0;
}
 
if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
/* If we're going to start writing into a block which already
contains data, and the end of the data isn't page-aligned,
skip a little and align it. */
 
uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1);
 
D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
skip));
c->nextblock->wasted_size += skip;
c->wasted_size += skip;
 
c->nextblock->free_size -= skip;
c->free_size -= skip;
}
if (c->nr_erasing_blocks) {
if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
return -EIO;
}
jffs2_erase_pending_trigger(c);
}
if (buf_size)
kfree(flashbuf);
#ifndef __ECOS
else
c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
#endif
return 0;
}
 
static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
uint32_t ofs, uint32_t len)
{
int ret;
size_t retlen;
 
ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
if (ret) {
D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret));
return ret;
}
if (retlen < len) {
D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen));
return -EIO;
}
D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs));
D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]));
return 0;
}
 
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
unsigned char *buf, uint32_t buf_size) {
struct jffs2_unknown_node *node;
struct jffs2_unknown_node crcnode;
uint32_t ofs, prevofs;
uint32_t hdr_crc, buf_ofs, buf_len;
int err;
int noise = 0;
int wasempty = 0;
uint32_t empty_start = 0;
#ifdef CONFIG_JFFS2_FS_NAND
int cleanmarkerfound = 0;
#endif
 
ofs = jeb->offset;
prevofs = jeb->offset - 1;
 
D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
 
#ifdef CONFIG_JFFS2_FS_NAND
if (jffs2_cleanmarker_oob(c)) {
int ret = jffs2_check_nand_cleanmarker(c, jeb);
D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
/* Even if it's not found, we still scan to see
if the block is empty. We use this information
to decide whether to erase it or not. */
switch (ret) {
case 0: cleanmarkerfound = 1; break;
case 1: break;
case 2: return BLK_STATE_BADBLOCK;
case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
default: return ret;
}
}
#endif
buf_ofs = jeb->offset;
 
if (!buf_size) {
buf_len = c->sector_size;
} else {
buf_len = EMPTY_SCAN_SIZE;
err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
if (err)
return err;
}
/* We temporarily use 'ofs' as a pointer into the buffer/jeb */
ofs = 0;
 
/* Scan only 4KiB of 0xFF before declaring it's empty */
while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
ofs += 4;
 
if (ofs == EMPTY_SCAN_SIZE) {
#ifdef CONFIG_JFFS2_FS_NAND
if (jffs2_cleanmarker_oob(c)) {
/* scan oob, take care of cleanmarker */
int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret));
switch (ret) {
case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
case 1: return BLK_STATE_ALLDIRTY;
case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */
case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
default: return ret;
}
}
#endif
D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
}
if (ofs) {
D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
jeb->offset + ofs));
DIRTY_SPACE(ofs);
}
 
/* Now ofs is a complete physical flash offset as it always was... */
ofs += jeb->offset;
 
noise = 10;
 
while(ofs < jeb->offset + c->sector_size) {
 
D1(ACCT_PARANOIA_CHECK(jeb));
 
cond_resched();
 
if (ofs & 3) {
printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs);
ofs = (ofs+3)&~3;
continue;
}
if (ofs == prevofs) {
printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs);
DIRTY_SPACE(4);
ofs += 4;
continue;
}
prevofs = ofs;
 
if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
jeb->offset, c->sector_size, ofs, sizeof(*node)));
DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
break;
}
 
if (buf_ofs + buf_len < ofs + sizeof(*node)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
sizeof(struct jffs2_unknown_node), buf_len, ofs));
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
}
 
node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
 
if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
uint32_t inbuf_ofs = ofs - buf_ofs + 4;
uint32_t scanend;
 
empty_start = ofs;
ofs += 4;
 
/* If scanning empty space after only a cleanmarker, don't
bother scanning the whole block */
if (unlikely(empty_start == jeb->offset + c->cleanmarker_size &&
jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len))
scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs;
else
scanend = buf_len;
 
D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
while (inbuf_ofs < scanend) {
if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)
goto emptyends;
 
inbuf_ofs+=4;
ofs += 4;
}
/* Ran off end. */
D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs));
 
if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
!jeb->first_node->next_in_ino && !jeb->dirty_size)
return BLK_STATE_CLEANMARKER;
wasempty = 1;
continue;
} else if (wasempty) {
emptyends:
printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs);
DIRTY_SPACE(ofs-empty_start);
wasempty = 0;
continue;
}
 
if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
DIRTY_SPACE(4);
ofs += 4;
continue;
}
if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs));
DIRTY_SPACE(4);
ofs += 4;
continue;
}
if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
DIRTY_SPACE(4);
ofs += 4;
continue;
}
if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {
/* OK. We're out of possibilities. Whinge and move on */
noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
JFFS2_MAGIC_BITMASK, ofs,
je16_to_cpu(node->magic));
DIRTY_SPACE(4);
ofs += 4;
continue;
}
/* We seem to have a node of sorts. Check the CRC */
crcnode.magic = node->magic;
crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE);
crcnode.totlen = node->totlen;
hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4);
 
if (hdr_crc != je32_to_cpu(node->hdr_crc)) {
noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
ofs, je16_to_cpu(node->magic),
je16_to_cpu(node->nodetype),
je32_to_cpu(node->totlen),
je32_to_cpu(node->hdr_crc),
hdr_crc);
DIRTY_SPACE(4);
ofs += 4;
continue;
}
 
if (ofs + je32_to_cpu(node->totlen) >
jeb->offset + c->sector_size) {
/* Eep. Node goes over the end of the erase block. */
printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
ofs, je32_to_cpu(node->totlen));
printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
DIRTY_SPACE(4);
ofs += 4;
continue;
}
 
if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
/* Wheee. This is an obsoleted node */
D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
ofs += PAD(je32_to_cpu(node->totlen));
continue;
}
 
switch(je16_to_cpu(node->nodetype)) {
case JFFS2_NODETYPE_INODE:
if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
sizeof(struct jffs2_raw_inode), buf_len, ofs));
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
node = (void *)buf;
}
err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs);
if (err) return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
case JFFS2_NODETYPE_DIRENT:
if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
je32_to_cpu(node->totlen), buf_len, ofs));
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
node = (void *)buf;
}
err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs);
if (err) return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
 
case JFFS2_NODETYPE_CLEANMARKER:
D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
ofs += PAD(sizeof(struct jffs2_unknown_node));
} else if (jeb->first_node) {
printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
ofs += PAD(sizeof(struct jffs2_unknown_node));
} else {
struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
if (!marker_ref) {
printk(KERN_NOTICE "Failed to allocate node ref for clean marker\n");
return -ENOMEM;
}
marker_ref->next_in_ino = NULL;
marker_ref->next_phys = NULL;
marker_ref->flash_offset = ofs | REF_NORMAL;
marker_ref->totlen = c->cleanmarker_size;
jeb->first_node = jeb->last_node = marker_ref;
USED_SPACE(PAD(c->cleanmarker_size));
ofs += PAD(c->cleanmarker_size);
}
break;
 
case JFFS2_NODETYPE_PADDING:
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
ofs += PAD(je32_to_cpu(node->totlen));
break;
 
default:
switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
case JFFS2_FEATURE_ROCOMPAT:
printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
c->flags |= JFFS2_SB_FLAG_RO;
if (!(jffs2_is_readonly(c)))
return -EROFS;
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
ofs += PAD(je32_to_cpu(node->totlen));
break;
 
case JFFS2_FEATURE_INCOMPAT:
printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
return -EINVAL;
 
case JFFS2_FEATURE_RWCOMPAT_DELETE:
D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
ofs += PAD(je32_to_cpu(node->totlen));
break;
 
case JFFS2_FEATURE_RWCOMPAT_COPY:
D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
USED_SPACE(PAD(je32_to_cpu(node->totlen)));
ofs += PAD(je32_to_cpu(node->totlen));
break;
}
}
}
 
 
D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset,
jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
 
/* mark_node_obsolete can add to wasted !! */
if (jeb->wasted_size) {
jeb->dirty_size += jeb->wasted_size;
c->dirty_size += jeb->wasted_size;
c->wasted_size -= jeb->wasted_size;
jeb->wasted_size = 0;
}
 
if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
&& (!jeb->first_node || jeb->first_node->next_in_ino) )
return BLK_STATE_CLEANMARKER;
/* move blocks with max 4 byte dirty space to cleanlist */
else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
c->dirty_size -= jeb->dirty_size;
c->wasted_size += jeb->dirty_size;
jeb->wasted_size += jeb->dirty_size;
jeb->dirty_size = 0;
return BLK_STATE_CLEAN;
} else if (jeb->used_size || jeb->unchecked_size)
return BLK_STATE_PARTDIRTY;
else
return BLK_STATE_ALLDIRTY;
}
 
static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
{
struct jffs2_inode_cache *ic;
 
ic = jffs2_get_ino_cache(c, ino);
if (ic)
return ic;
 
ic = jffs2_alloc_inode_cache();
if (!ic) {
printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
return NULL;
}
memset(ic, 0, sizeof(*ic));
 
ic->ino = ino;
ic->nodes = (void *)ic;
jffs2_add_ino_cache(c, ic);
if (ino == 1)
ic->nlink=1;
return ic;
}
 
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_inode *ri, uint32_t ofs)
{
struct jffs2_raw_node_ref *raw;
struct jffs2_inode_cache *ic;
uint32_t ino = je32_to_cpu(ri->ino);
 
D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
 
/* We do very little here now. Just check the ino# to which we should attribute
this node; we can do all the CRC checking etc. later. There's a tradeoff here --
we used to scan the flash once only, reading everything we want from it into
memory, then building all our in-core data structures and freeing the extra
information. Now we allow the first part of the mount to complete a lot quicker,
but we have to go _back_ to the flash in order to finish the CRC checking, etc.
Which means that the _full_ amount of time to get to proper write mode with GC
operational may actually be _longer_ than before. Sucks to be me. */
 
raw = jffs2_alloc_raw_node_ref();
if (!raw) {
printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
return -ENOMEM;
}
 
ic = jffs2_get_ino_cache(c, ino);
if (!ic) {
/* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
first node we found for this inode. Do a CRC check to protect against the former
case */
uint32_t crc = crc32(0, ri, sizeof(*ri)-8);
 
if (crc != je32_to_cpu(ri->node_crc)) {
printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ofs, je32_to_cpu(ri->node_crc), crc);
/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
return 0;
}
ic = jffs2_scan_make_ino_cache(c, ino);
if (!ic) {
jffs2_free_raw_node_ref(raw);
return -ENOMEM;
}
}
 
/* Wheee. It worked */
 
raw->flash_offset = ofs | REF_UNCHECKED;
raw->totlen = PAD(je32_to_cpu(ri->totlen));
raw->next_phys = NULL;
raw->next_in_ino = ic->nodes;
 
ic->nodes = raw;
if (!jeb->first_node)
jeb->first_node = raw;
if (jeb->last_node)
jeb->last_node->next_phys = raw;
jeb->last_node = raw;
 
D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
je32_to_cpu(ri->offset),
je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize)));
 
pseudo_random += je32_to_cpu(ri->version);
 
UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
return 0;
}
 
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_dirent *rd, uint32_t ofs)
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *fd;
struct jffs2_inode_cache *ic;
uint32_t crc;
 
D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
 
/* We don't get here unless the node is still valid, so we don't have to
mask in the ACCURATE bit any more. */
crc = crc32(0, rd, sizeof(*rd)-8);
 
if (crc != je32_to_cpu(rd->node_crc)) {
printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ofs, je32_to_cpu(rd->node_crc), crc);
/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
return 0;
}
 
pseudo_random += je32_to_cpu(rd->version);
 
fd = jffs2_alloc_full_dirent(rd->nsize+1);
if (!fd) {
return -ENOMEM;
}
memcpy(&fd->name, rd->name, rd->nsize);
fd->name[rd->nsize] = 0;
 
crc = crc32(0, fd->name, rd->nsize);
if (crc != je32_to_cpu(rd->name_crc)) {
printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ofs, je32_to_cpu(rd->name_crc), crc);
D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino)));
jffs2_free_full_dirent(fd);
/* FIXME: Why do we believe totlen? */
/* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
return 0;
}
raw = jffs2_alloc_raw_node_ref();
if (!raw) {
jffs2_free_full_dirent(fd);
printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
return -ENOMEM;
}
ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
if (!ic) {
jffs2_free_full_dirent(fd);
jffs2_free_raw_node_ref(raw);
return -ENOMEM;
}
raw->totlen = PAD(je32_to_cpu(rd->totlen));
raw->flash_offset = ofs | REF_PRISTINE;
raw->next_phys = NULL;
raw->next_in_ino = ic->nodes;
ic->nodes = raw;
if (!jeb->first_node)
jeb->first_node = raw;
if (jeb->last_node)
jeb->last_node->next_phys = raw;
jeb->last_node = raw;
 
fd->raw = raw;
fd->next = NULL;
fd->version = je32_to_cpu(rd->version);
fd->ino = je32_to_cpu(rd->ino);
fd->nhash = full_name_hash(fd->name, rd->nsize);
fd->type = rd->type;
USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
 
return 0;
}
 
static int count_list(struct list_head *l)
{
uint32_t count = 0;
struct list_head *tmp;
 
list_for_each(tmp, l) {
count++;
}
return count;
}
 
/* Note: This breaks if list_empty(head). I don't care. You
might, if you copy this code and use it elsewhere :) */
static void rotate_list(struct list_head *head, uint32_t count)
{
struct list_head *n = head->next;
 
list_del(head);
while(count--) {
n = n->next;
}
list_add(head, n);
}
 
void jffs2_rotate_lists(struct jffs2_sb_info *c)
{
uint32_t x;
uint32_t rotateby;
 
x = count_list(&c->clean_list);
if (x) {
rotateby = pseudo_random % x;
D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby));
 
rotate_list((&c->clean_list), rotateby);
 
D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n",
list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset));
} else {
D1(printk(KERN_DEBUG "Not rotating empty clean_list\n"));
}
 
x = count_list(&c->very_dirty_list);
if (x) {
rotateby = pseudo_random % x;
D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby));
 
rotate_list((&c->very_dirty_list), rotateby);
 
D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n",
list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset));
} else {
D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n"));
}
 
x = count_list(&c->dirty_list);
if (x) {
rotateby = pseudo_random % x;
D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby));
 
rotate_list((&c->dirty_list), rotateby);
 
D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n",
list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset));
} else {
D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n"));
}
 
x = count_list(&c->erasable_list);
if (x) {
rotateby = pseudo_random % x;
D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby));
 
rotate_list((&c->erasable_list), rotateby);
 
D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n",
list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset));
} else {
D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n"));
}
 
if (c->nr_erasing_blocks) {
rotateby = pseudo_random % c->nr_erasing_blocks;
D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
 
rotate_list((&c->erase_pending_list), rotateby);
 
D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n",
list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset));
} else {
D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n"));
}
 
if (c->nr_free_blocks) {
rotateby = pseudo_random % c->nr_free_blocks;
D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby));
 
rotate_list((&c->free_list), rotateby);
 
D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n",
list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset));
} else {
D1(printk(KERN_DEBUG "Not rotating empty free_list\n"));
}
}
/jffs2/v2_0/src/nodelist.h
0,0 → 1,396
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodelist.h,v 1.1.1.1 2004-02-14 13:29:19 phoenix Exp $
*
*/
 
#ifndef __JFFS2_NODELIST_H__
#define __JFFS2_NODELIST_H__
 
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/jffs2.h>
#include <linux/jffs2_fs_sb.h>
#include <linux/jffs2_fs_i.h>
 
#ifdef __ECOS
#include "os-ecos.h"
#else
#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
#include "os-linux.h"
#endif
 
#ifndef CONFIG_JFFS2_FS_DEBUG
#define CONFIG_JFFS2_FS_DEBUG 2
#endif
 
#if CONFIG_JFFS2_FS_DEBUG > 0
#define D1(x) x
#else
#define D1(x)
#endif
 
#if CONFIG_JFFS2_FS_DEBUG > 1
#define D2(x) x
#else
#define D2(x)
#endif
 
/*
This is all we need to keep in-core for each raw node during normal
operation. As and when we do read_inode on a particular inode, we can
scan the nodes which are listed for it and build up a proper map of
which nodes are currently valid. JFFSv1 always used to keep that whole
map in core for each inode.
*/
struct jffs2_raw_node_ref
{
struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref
for this inode. If this is the last, it points to the inode_cache
for this inode instead. The inode_cache will have NULL in the first
word so you know when you've got there :) */
struct jffs2_raw_node_ref *next_phys;
uint32_t flash_offset;
uint32_t totlen;
/* flash_offset & 3 always has to be zero, because nodes are
always aligned at 4 bytes. So we have a couple of extra bits
to play with. So we set the least significant bit to 1 to
signify that the node is obsoleted by later nodes.
*/
#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */
#define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */
#define REF_PRISTINE 2 /* Completely clean. GC without looking */
#define REF_NORMAL 3 /* Possibly overlapped. Read the page and write again on GC */
#define ref_flags(ref) ((ref)->flash_offset & 3)
#define ref_offset(ref) ((ref)->flash_offset & ~3)
#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE)
#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
};
 
/*
Used for keeping track of deletion nodes &c, which can only be marked
as obsolete when the node which they mark as deleted has actually been
removed from the flash.
*/
struct jffs2_raw_node_ref_list {
struct jffs2_raw_node_ref *rew;
struct jffs2_raw_node_ref_list *next;
};
 
/* For each inode in the filesystem, we need to keep a record of
nlink, because it would be a PITA to scan the whole directory tree
at read_inode() time to calculate it, and to keep sufficient information
in the raw_node_ref (basically both parent and child inode number for
dirent nodes) would take more space than this does. We also keep
a pointer to the first physical node which is part of this inode, too.
*/
struct jffs2_inode_cache {
struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
temporary lists of dirents, and later must be set to
NULL to mark the end of the raw_node_ref->next_in_ino
chain. */
struct jffs2_inode_cache *next;
struct jffs2_raw_node_ref *nodes;
uint32_t ino;
int nlink;
int state;
};
 
/* Inode states for 'state' above. We need the 'GC' state to prevent
someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
node without going through all the iget() nonsense */
#define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */
#define INO_STATE_CHECKING 1 /* CRC checks in progress */
#define INO_STATE_PRESENT 2 /* In core */
#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */
#define INO_STATE_GC 4 /* GCing a 'pristine' node */
#define INO_STATE_READING 5 /* In read_inode() */
 
#define INOCACHE_HASHSIZE 128
 
struct jffs2_scan_info {
struct jffs2_full_dirent *dents;
struct jffs2_tmp_dnode_info *tmpnodes;
/* Latest i_size info */
uint32_t version;
uint32_t isize;
};
/*
Larger representation of a raw node, kept in-core only when the
struct inode for this particular ino is instantiated.
*/
 
struct jffs2_full_dnode
{
struct jffs2_raw_node_ref *raw;
uint32_t ofs; /* Don't really need this, but optimisation */
uint32_t size;
uint32_t frags; /* Number of fragments which currently refer
to this node. When this reaches zero,
the node is obsolete.
*/
};
 
/*
Even larger representation of a raw node, kept in-core only while
we're actually building up the original map of which nodes go where,
in read_inode()
*/
struct jffs2_tmp_dnode_info
{
struct jffs2_tmp_dnode_info *next;
struct jffs2_full_dnode *fn;
uint32_t version;
};
 
struct jffs2_full_dirent
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *next;
uint32_t version;
uint32_t ino; /* == zero for unlink */
unsigned int nhash;
unsigned char type;
unsigned char name[0];
};
/*
Fragments - used to build a map of which raw node to obtain
data from for each part of the ino
*/
struct jffs2_node_frag
{
struct rb_node rb;
struct jffs2_full_dnode *node; /* NULL for holes */
uint32_t size;
uint32_t ofs; /* Don't really need this, but optimisation */
};
 
struct jffs2_eraseblock
{
struct list_head list;
int bad_count;
uint32_t offset; /* of this block in the MTD */
 
uint32_t unchecked_size;
uint32_t used_size;
uint32_t dirty_size;
uint32_t wasted_size;
uint32_t free_size; /* Note that sector_size - free_size
is the address of the first free space */
struct jffs2_raw_node_ref *first_node;
struct jffs2_raw_node_ref *last_node;
 
struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */
 
/* For deletia. When a dirent node in this eraseblock is
deleted by a node elsewhere, that other node can only
be marked as obsolete when this block is actually erased.
So we keep a list of the nodes to mark as obsolete when
the erase is completed.
*/
// MAYBE struct jffs2_raw_node_ref_list *deletia;
};
 
#define ACCT_SANITY_CHECK(c, jeb) do { \
if (jeb->used_size + jeb->dirty_size + jeb->free_size + jeb->wasted_size + jeb->unchecked_size != c->sector_size) { \
printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \
printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \
jeb->free_size, jeb->dirty_size, jeb->used_size, jeb->wasted_size, jeb->unchecked_size, c->sector_size); \
BUG(); \
} \
if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \
printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \
printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \
c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \
BUG(); \
} \
} while(0)
 
#define ACCT_PARANOIA_CHECK(jeb) do { \
uint32_t my_used_size = 0; \
uint32_t my_unchecked_size = 0; \
struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
while (ref2) { \
if (ref_flags(ref2) == REF_UNCHECKED) \
my_unchecked_size += ref2->totlen; \
else if (!ref_obsolete(ref2)) \
my_used_size += ref2->totlen; \
ref2 = ref2->next_phys; \
} \
if (my_used_size != jeb->used_size) { \
printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \
BUG(); \
} \
if (my_unchecked_size != jeb->unchecked_size) { \
printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \
BUG(); \
} \
} while(0)
 
#define ALLOC_NORMAL 0 /* Normal allocation */
#define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
#define ALLOC_GC 2 /* Space requested for GC. Give it or die */
 
#define JFFS2_RESERVED_BLOCKS_BASE 3 /* Number of free blocks there must be before we... */
#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2) /* ... allow a normal filesystem write */
#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE) /* ... allow a normal filesystem deletion */
#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3) /* ... wake up the GC thread */
#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... pick a block from the bad_list to GC */
#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE) /* ... merge pages when garbage collecting */
 
 
/* How much dirty space before it goes on the very_dirty_list */
#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2))
 
/* check if dirty space is more than 255 Byte */
#define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN)
 
#define PAD(x) (((x)+3)&~3)
 
static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
{
while(raw->next_in_ino) {
raw = raw->next_in_ino;
}
 
return ((struct jffs2_inode_cache *)raw)->ino;
}
 
static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
{
struct rb_node *node = root->rb_node;
 
if (!node)
return NULL;
while(node->rb_left)
node = node->rb_left;
return rb_entry(node, struct jffs2_node_frag, rb);
}
#define rb_parent(rb) ((rb)->rb_parent)
#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb)
#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb)
#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb)
#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb)
#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb)
#define frag_erase(frag, list) rb_erase(&frag->rb, list);
 
/* nodelist.c */
D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
uint32_t *highest_version, uint32_t *latest_mctime,
uint32_t *mctime_ver);
void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino);
void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
void jffs2_free_ino_caches(struct jffs2_sb_info *c);
void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset);
void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete);
void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base);
struct rb_node *rb_next(struct rb_node *);
struct rb_node *rb_prev(struct rb_node *);
void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
 
/* nodemgmt.c */
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
void jffs2_complete_reservation(struct jffs2_sb_info *c);
void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
void jffs2_dump_block_lists(struct jffs2_sb_info *c);
 
/* write.c */
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, uint32_t *writelen);
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, uint32_t *writelen);
int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_raw_inode *ri, unsigned char *buf,
uint32_t offset, uint32_t writelen, uint32_t *retlen);
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen);
int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f);
int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen);
 
 
/* readinode.c */
void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint32_t ino, struct jffs2_raw_inode *latest_node);
int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
 
/* malloc.c */
int jffs2_create_slab_caches(void);
void jffs2_destroy_slab_caches(void);
 
struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize);
void jffs2_free_full_dirent(struct jffs2_full_dirent *);
struct jffs2_full_dnode *jffs2_alloc_full_dnode(void);
void jffs2_free_full_dnode(struct jffs2_full_dnode *);
struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void);
void jffs2_free_raw_dirent(struct jffs2_raw_dirent *);
struct jffs2_raw_inode *jffs2_alloc_raw_inode(void);
void jffs2_free_raw_inode(struct jffs2_raw_inode *);
struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void);
void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *);
struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void);
void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *);
struct jffs2_node_frag *jffs2_alloc_node_frag(void);
void jffs2_free_node_frag(struct jffs2_node_frag *);
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
void jffs2_free_inode_cache(struct jffs2_inode_cache *);
 
/* gc.c */
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
 
/* read.c */
int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
unsigned char *buf, uint32_t offset, uint32_t len);
char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
 
 
/* compr.c */
unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *datalen, uint32_t *cdatalen);
int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
 
/* scan.c */
int jffs2_scan_medium(struct jffs2_sb_info *c);
void jffs2_rotate_lists(struct jffs2_sb_info *c);
 
/* build.c */
int jffs2_do_mount_fs(struct jffs2_sb_info *c);
 
/* erase.c */
void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
 
#ifdef CONFIG_JFFS2_FS_NAND
/* wbuf.c */
int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad);
int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
#endif
 
/* compr_zlib.c */
int jffs2_zlib_init(void);
void jffs2_zlib_exit(void);
 
#endif /* __JFFS2_NODELIST_H__ */
/jffs2/v2_0/src/gc.c
0,0 → 1,1140
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: gc.c,v 1.1.1.1 2004-02-14 13:29:18 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include <linux/compiler.h>
#include <linux/stat.h>
#include "nodelist.h"
 
static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
struct jffs2_inode_cache *ic,
struct jffs2_raw_node_ref *raw);
static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end);
static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end);
static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic);
 
/* Called with erase_completion_lock held */
static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
{
struct jffs2_eraseblock *ret;
struct list_head *nextlist = NULL;
int n = jiffies % 128;
 
/* Pick an eraseblock to garbage collect next. This is where we'll
put the clever wear-levelling algorithms. Eventually. */
/* We possibly want to favour the dirtier blocks more when the
number of free blocks is low. */
if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) {
D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
nextlist = &c->bad_used_list;
} else if (n < 50 && !list_empty(&c->erasable_list)) {
/* Note that most of them will have gone directly to be erased.
So don't favour the erasable_list _too_ much. */
D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n"));
nextlist = &c->erasable_list;
} else if (n < 110 && !list_empty(&c->very_dirty_list)) {
/* Most of the time, pick one off the very_dirty list */
D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n"));
nextlist = &c->very_dirty_list;
} else if (n < 126 && !list_empty(&c->dirty_list)) {
D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n"));
nextlist = &c->dirty_list;
} else if (!list_empty(&c->clean_list)) {
D1(printk(KERN_DEBUG "Picking block from clean_list to GC next\n"));
nextlist = &c->clean_list;
} else if (!list_empty(&c->dirty_list)) {
D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
 
nextlist = &c->dirty_list;
} else if (!list_empty(&c->very_dirty_list)) {
D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n"));
nextlist = &c->very_dirty_list;
} else if (!list_empty(&c->erasable_list)) {
D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
 
nextlist = &c->erasable_list;
} else {
/* Eep. All were empty */
printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
return NULL;
}
 
ret = list_entry(nextlist->next, struct jffs2_eraseblock, list);
list_del(&ret->list);
c->gcblock = ret;
ret->gc_node = ret->first_node;
if (!ret->gc_node) {
printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset);
BUG();
}
/* Have we accidentally picked a clean block with wasted space ? */
if (ret->wasted_size) {
D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size));
ret->dirty_size += ret->wasted_size;
c->wasted_size -= ret->wasted_size;
c->dirty_size += ret->wasted_size;
ret->wasted_size = 0;
}
 
D1(jffs2_dump_block_lists(c));
return ret;
}
 
/* jffs2_garbage_collect_pass
* Make a single attempt to progress GC. Move one node, and possibly
* start erasing one eraseblock.
*/
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
{
struct jffs2_inode_cache *ic;
struct jffs2_eraseblock *jeb;
struct jffs2_raw_node_ref *raw;
uint32_t inum;
int ret = 0;
 
if (down_interruptible(&c->alloc_sem))
return -EINTR;
 
for (;;) {
struct jffs2_inode_cache *ic;
 
spin_lock(&c->erase_completion_lock);
if (!c->unchecked_size)
break;
 
/* We can't start doing GC yet. We haven't finished checking
the node CRCs etc. Do it now. */
/* checked_ino is protected by the alloc_sem */
if (c->checked_ino > c->highest_ino) {
printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
c->unchecked_size);
D1(jffs2_dump_block_lists(c));
spin_unlock(&c->erase_completion_lock);
BUG();
}
 
spin_unlock(&c->erase_completion_lock);
 
spin_lock(&c->inocache_lock);
 
ic = jffs2_get_ino_cache(c, c->checked_ino++);
 
if (!ic) {
spin_unlock(&c->inocache_lock);
continue;
}
 
if (!ic->nlink) {
D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
ic->ino));
spin_unlock(&c->inocache_lock);
continue;
}
switch(ic->state) {
case INO_STATE_CHECKEDABSENT:
case INO_STATE_PRESENT:
D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino));
spin_unlock(&c->inocache_lock);
continue;
 
case INO_STATE_GC:
case INO_STATE_CHECKING:
printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state);
spin_unlock(&c->inocache_lock);
BUG();
 
case INO_STATE_READING:
/* We need to wait for it to finish, lest we move on
and trigger the BUG() above while we haven't yet
finished checking all its nodes */
D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
up(&c->alloc_sem);
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
return 0;
 
default:
BUG();
 
case INO_STATE_UNCHECKED:
;
}
ic->state = INO_STATE_CHECKING;
spin_unlock(&c->inocache_lock);
 
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino));
 
ret = jffs2_do_crccheck_inode(c, ic);
if (!ret)
jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
up(&c->alloc_sem);
return ret;
}
 
/* First, work out which block we're garbage-collecting */
jeb = c->gcblock;
 
if (!jeb)
jeb = jffs2_find_gc_block(c);
 
if (!jeb) {
printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
return -EIO;
}
 
D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size));
D1(if (c->nextblock)
printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size));
 
if (!jeb->used_size) {
up(&c->alloc_sem);
goto eraseit;
}
 
raw = jeb->gc_node;
while(ref_obsolete(raw)) {
D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
jeb->gc_node = raw = raw->next_phys;
if (!raw) {
printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
BUG();
}
}
D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
if (!raw->next_in_ino) {
/* Inode-less node. Clean marker, snapshot or something like that */
/* FIXME: If it's something that needs to be copied, including something
we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
spin_unlock(&c->erase_completion_lock);
jffs2_mark_node_obsolete(c, raw);
up(&c->alloc_sem);
goto eraseit_lock;
}
inum = jffs2_raw_ref_to_inum(raw);
D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
 
spin_unlock(&c->erase_completion_lock);
 
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum));
 
/* Three possibilities:
1. Inode is already in-core. We must iget it and do proper
updating to its fragtree, etc.
2. Inode is not in-core, node is REF_PRISTINE. We lock the
inocache to prevent a read_inode(), copy the node intact.
3. Inode is not in-core, node is not pristine. We must iget()
and take the slow path.
*/
spin_lock(&c->inocache_lock);
ic = jffs2_get_ino_cache(c, inum);
 
/* This should never fail unless I'm particularly stupid.
So we don't check before dereferencing it */
 
switch(ic->state) {
case INO_STATE_CHECKEDABSENT:
/* It's been checked, but it's not currently in-core.
We can just copy any pristine nodes, but have
to prevent anyone else from doing read_inode() while
we're at it, so we set the state accordingly */
if (ref_flags(raw) == REF_PRISTINE)
ic->state = INO_STATE_GC;
else {
D1(printk("Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
inum));
}
break;
 
case INO_STATE_PRESENT:
case INO_STATE_UNCHECKED:
/* It's in-core or hasn't been checked. GC must iget() it. */
break;
 
case INO_STATE_CHECKING:
/* Should never happen. We should have finished checking
by the time we actually start doing any GC. */
BUG();
 
case INO_STATE_GC:
/* Should never happen. We are holding the alloc_sem,
no other garbage collection can happen. Note that we
do depend on this later when deciding to do a simple
node copy */
BUG();
case INO_STATE_READING:
/* Someone's currently trying to read it. We must wait for
them to finish and then go through the full iget() route
to do the GC. However, sometimes read_inode() needs to get
the alloc_sem() (for marking nodes invalid) so we must
drop the alloc_sem before sleeping. */
 
up(&c->alloc_sem);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
inum, ic->state));
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
/* And because we dropped the alloc_sem we must start again from the
beginning. Ponder chance of livelock here -- we're returning success
without actually making any progress.
 
Q: What are the chances that the inode is back in INO_STATE_READING
again by the time we next enter this function? And that this happens
enough times to cause a real delay?
 
A: Small enough that I don't care :)
*/
return 0;
 
}
 
spin_unlock(&c->inocache_lock);
 
/* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
node intact, and we don't have to muck about with the fragtree etc.
because we know it's not in-core. If it _was_ in-core, we go through
all the iget() crap anyway */
 
if (ic->state == INO_STATE_GC) {
ret = jffs2_garbage_collect_pristine(c, ic, raw);
jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
 
if (ret != -EBADFD)
goto release_sem;
 
/* Fall through if it wanted us to */
}
 
ret = jffs2_garbage_collect_live(c, jeb, raw, ic);
 
release_sem:
up(&c->alloc_sem);
 
eraseit_lock:
/* If we've finished this block, start it erasing */
spin_lock(&c->erase_completion_lock);
 
eraseit:
if (c->gcblock && !c->gcblock->used_size) {
D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
/* We're GC'ing an empty block? */
list_add_tail(&c->gcblock->list, &c->erase_pending_list);
c->gcblock = NULL;
c->nr_erasing_blocks++;
jffs2_erase_pending_trigger(c);
}
spin_unlock(&c->erase_completion_lock);
 
return ret;
}
 
 
static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic)
{
struct jffs2_inode_info *f;
struct jffs2_node_frag *frag;
struct jffs2_full_dnode *fn = NULL;
struct jffs2_full_dirent *fd;
uint32_t start = 0, end = 0, nrfrags = 0;
struct inode *inode;
int ret = 0;
 
inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
if (is_bad_inode(inode)) {
printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
/* NB. This will happen again. We need to do something appropriate here. */
up(&c->alloc_sem);
iput(inode);
return -EIO;
}
 
f = JFFS2_INODE_INFO(inode);
down(&f->sem);
 
/* Now we have the lock for this inode. Check that it's still the one at the head
of the list. */
 
if (ref_obsolete(raw)) {
D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
/* They'll call again */
goto upnout;
}
/* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
if (f->metadata && f->metadata->raw == raw) {
fn = f->metadata;
ret = jffs2_garbage_collect_metadata(c, jeb, f, fn);
goto upnout;
}
 
/* FIXME. Read node and do lookup? */
for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
if (frag->node && frag->node->raw == raw) {
fn = frag->node;
end = frag->ofs + frag->size;
#if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */
if (!nrfrags && ref_flags(fn->raw) == REF_PRISTINE) {
if (fn->frags > 1) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(raw), fn->frags);
mark_ref_normal(raw);
}
/* A hole node which isn't multi-page should be garbage-collected
and merged anyway, so we just check for the frag size here,
rather than mucking around with actually reading the node
and checking the compression type, which is the real way
to tell a hole node. */
if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
ref_offset(raw));
mark_ref_normal(raw);
}
 
if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
ref_offset(raw), frag->ofs, frag->ofs+frag->size);
mark_ref_normal(raw);
}
}
#endif
if (!nrfrags++)
start = frag->ofs;
if (nrfrags == frag->node->frags)
break; /* We've found them all */
}
}
if (fn) {
if (ref_flags(raw) == REF_PRISTINE) {
ret = jffs2_garbage_collect_pristine(c, ic, raw);
if (!ret) {
/* Urgh. Return it sensibly. */
frag->node->raw = ic->nodes;
}
if (ret != -EBADFD)
goto upnout;
}
/* We found a datanode. Do the GC */
if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) {
/* It crosses a page boundary. Therefore, it must be a hole. */
ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end);
} else {
/* It could still be a hole. But we GC the page this way anyway */
ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end);
}
goto upnout;
}
/* Wasn't a dnode. Try dirent */
for (fd = f->dents; fd; fd=fd->next) {
if (fd->raw == raw)
break;
}
 
if (fd && fd->ino) {
ret = jffs2_garbage_collect_dirent(c, jeb, f, fd);
} else if (fd) {
ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd);
} else {
printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n",
ref_offset(raw), f->inocache->ino);
if (ref_obsolete(raw)) {
printk(KERN_WARNING "But it's obsolete so we don't mind too much\n");
} else {
ret = -EIO;
}
}
upnout:
up(&f->sem);
iput(inode);
 
return ret;
}
 
static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
struct jffs2_inode_cache *ic,
struct jffs2_raw_node_ref *raw)
{
union jffs2_node_union *node;
struct jffs2_raw_node_ref *nraw;
size_t retlen;
int ret;
uint32_t phys_ofs, alloclen;
uint32_t crc;
 
D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
 
/* Ask for a small amount of space (or the totlen if smaller) because we
don't want to force wastage of the end of a block if splitting would
work. */
ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, raw->totlen),
&phys_ofs, &alloclen);
if (ret)
return ret;
 
if (alloclen < raw->totlen) {
/* Doesn't fit untouched. We'll go the old route and split it */
return -EBADFD;
}
 
node = kmalloc(raw->totlen, GFP_KERNEL);
if (!node)
return -ENOMEM;
 
ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node);
if (!ret && retlen != raw->totlen)
ret = -EIO;
if (ret)
goto out_node;
 
crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4);
if (je32_to_cpu(node->u.hdr_crc) != crc) {
printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
goto bail;
}
 
switch(je16_to_cpu(node->u.nodetype)) {
case JFFS2_NODETYPE_INODE:
crc = crc32(0, node, sizeof(node->i)-8);
if (je32_to_cpu(node->i.node_crc) != crc) {
printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(raw), je32_to_cpu(node->i.node_crc), crc);
goto bail;
}
 
if (je32_to_cpu(node->i.dsize)) {
crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize));
if (je32_to_cpu(node->i.data_crc) != crc) {
printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(raw), je32_to_cpu(node->i.data_crc), crc);
goto bail;
}
}
break;
 
case JFFS2_NODETYPE_DIRENT:
crc = crc32(0, node, sizeof(node->d)-8);
if (je32_to_cpu(node->d.node_crc) != crc) {
printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(raw), je32_to_cpu(node->d.node_crc), crc);
goto bail;
}
 
if (node->d.nsize) {
crc = crc32(0, node->d.name, node->d.nsize);
if (je32_to_cpu(node->d.name_crc) != crc) {
printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
goto bail;
}
}
break;
default:
printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
ref_offset(raw), je16_to_cpu(node->u.nodetype));
goto bail;
}
 
nraw = jffs2_alloc_raw_node_ref();
if (!nraw) {
ret = -ENOMEM;
goto out_node;
}
nraw->flash_offset = phys_ofs;
nraw->totlen = raw->totlen;
nraw->next_phys = NULL;
 
/* OK, all the CRCs are good; this node can just be copied as-is. */
 
ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node);
if (ret || (retlen != raw->totlen)) {
printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
raw->totlen, phys_ofs, ret, retlen);
if (retlen) {
/* Doesn't belong to any inode */
nraw->next_in_ino = NULL;
 
nraw->flash_offset |= REF_OBSOLETE;
jffs2_add_physical_node_ref(c, nraw);
jffs2_mark_node_obsolete(c, nraw);
} else {
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
jffs2_free_raw_node_ref(raw);
}
if (!ret)
ret = -EIO;
goto out_node;
}
nraw->flash_offset |= REF_PRISTINE;
jffs2_add_physical_node_ref(c, nraw);
 
/* Link into per-inode list. This is safe because of the ic
state being INO_STATE_GC. Note that if we're doing this
for an inode which is in-code, the 'nraw' pointer is then
going to be fetched from ic->nodes by our caller. */
nraw->next_in_ino = ic->nodes;
ic->nodes = nraw;
 
jffs2_mark_node_obsolete(c, raw);
D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
 
out_node:
kfree(node);
return ret;
bail:
ret = -EBADFD;
goto out_node;
}
 
static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
{
struct jffs2_full_dnode *new_fn;
struct jffs2_raw_inode ri;
unsigned short dev;
char *mdata = NULL, mdatalen = 0;
uint32_t alloclen, phys_ofs;
int ret;
 
if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
S_ISCHR(JFFS2_F_I_MODE(f)) ) {
/* For these, we don't actually need to read the old node */
/* FIXME: for minor or major > 255. */
dev = ((JFFS2_F_I_RDEV_MAJ(f) << 8) |
JFFS2_F_I_RDEV_MIN(f));
mdata = (char *)&dev;
mdatalen = sizeof(dev);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
} else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
mdatalen = fn->size;
mdata = kmalloc(fn->size, GFP_KERNEL);
if (!mdata) {
printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
return -ENOMEM;
}
ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
if (ret) {
printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
kfree(mdata);
return ret;
}
D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bites of symlink target\n", mdatalen));
 
}
ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen);
if (ret) {
printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
sizeof(ri)+ mdatalen, ret);
goto out;
}
memset(&ri, 0, sizeof(ri));
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen);
ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
 
ri.ino = cpu_to_je32(f->inocache->ino);
ri.version = cpu_to_je32(++f->highest_version);
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
ri.offset = cpu_to_je32(0);
ri.csize = cpu_to_je32(mdatalen);
ri.dsize = cpu_to_je32(mdatalen);
ri.compr = JFFS2_COMPR_NONE;
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
 
new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, NULL);
 
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
ret = PTR_ERR(new_fn);
goto out;
}
jffs2_mark_node_obsolete(c, fn->raw);
jffs2_free_full_dnode(fn);
f->metadata = new_fn;
out:
if (S_ISLNK(JFFS2_F_I_MODE(f)))
kfree(mdata);
return ret;
}
 
static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
{
struct jffs2_full_dirent *new_fd;
struct jffs2_raw_dirent rd;
uint32_t alloclen, phys_ofs;
int ret;
 
rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
rd.nsize = strlen(fd->name);
rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize);
rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4));
 
rd.pino = cpu_to_je32(f->inocache->ino);
rd.version = cpu_to_je32(++f->highest_version);
rd.ino = cpu_to_je32(fd->ino);
rd.mctime = cpu_to_je32(max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f)));
rd.type = fd->type;
rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen);
if (ret) {
printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
sizeof(rd)+rd.nsize, ret);
return ret;
}
new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, NULL);
 
if (IS_ERR(new_fd)) {
printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
return PTR_ERR(new_fd);
}
jffs2_add_fd_to_list(c, new_fd, &f->dents);
return 0;
}
 
static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
{
struct jffs2_full_dirent **fdp = &f->dents;
int found = 0;
 
/* On a medium where we can't actually mark nodes obsolete
pernamently, such as NAND flash, we need to work out
whether this deletion dirent is still needed to actively
delete a 'real' dirent with the same name that's still
somewhere else on the flash. */
if (!jffs2_can_mark_obsolete(c)) {
struct jffs2_raw_dirent rd;
struct jffs2_raw_node_ref *raw;
int ret;
size_t retlen;
int name_len = strlen(fd->name);
uint32_t name_crc = crc32(0, fd->name, name_len);
char *namebuf = NULL;
 
/* Prevent the erase code from nicking the obsolete node refs while
we're looking at them. I really don't like this extra lock but
can't see any alternative. Suggestions on a postcard to... */
down(&c->erase_free_sem);
 
for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
/* We only care about obsolete ones */
if (!(ref_obsolete(raw)))
continue;
 
/* Doesn't matter if there's one in the same erase block. We're going to
delete it too at the same time. */
if ((raw->flash_offset & ~(c->sector_size-1)) ==
(fd->raw->flash_offset & ~(c->sector_size-1)))
continue;
 
/* This is an obsolete node belonging to the same directory */
ret = jffs2_flash_read(c, ref_offset(raw), sizeof(struct jffs2_unknown_node), &retlen, (char *)&rd);
if (ret) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, ref_offset(raw));
/* If we can't read it, we don't need to continue to obsolete it. Continue */
continue;
}
if (retlen != sizeof(struct jffs2_unknown_node)) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n",
retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw));
continue;
}
if (je16_to_cpu(rd.nodetype) != JFFS2_NODETYPE_DIRENT ||
PAD(je32_to_cpu(rd.totlen)) != PAD(sizeof(rd) + name_len))
continue;
 
/* OK, it's a dirent node, it's the right length. We have to take a
closer look at it... */
ret = jffs2_flash_read(c, ref_offset(raw), sizeof(rd), &retlen, (char *)&rd);
if (ret) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, ref_offset(raw));
/* If we can't read it, we don't need to continune to obsolete it. Continue */
continue;
}
if (retlen != sizeof(rd)) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n",
retlen, sizeof(rd), ref_offset(raw));
continue;
}
 
/* If the name CRC doesn't match, skip */
if (je32_to_cpu(rd.name_crc) != name_crc)
continue;
/* If the name length doesn't match, or it's another deletion dirent, skip */
if (rd.nsize != name_len || !je32_to_cpu(rd.ino))
continue;
 
/* OK, check the actual name now */
if (!namebuf) {
namebuf = kmalloc(name_len + 1, GFP_KERNEL);
if (!namebuf) {
up(&c->erase_free_sem);
return -ENOMEM;
}
}
/* We read the extra byte before it so it's a word-aligned read */
ret = jffs2_flash_read(c, (ref_offset(raw))+sizeof(rd)-1, name_len+1, &retlen, namebuf);
if (ret) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, ref_offset(raw));
/* If we can't read it, we don't need to continune to obsolete it. Continue */
continue;
}
if (retlen != name_len+1) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n",
retlen, name_len+1, ref_offset(raw));
continue;
}
if (memcmp(namebuf+1, fd->name, name_len))
continue;
 
/* OK. The name really does match. There really is still an older node on
the flash which our deletion dirent obsoletes. So we have to write out
a new deletion dirent to replace it */
if (namebuf)
kfree(namebuf);
 
up(&c->erase_free_sem);
return jffs2_garbage_collect_dirent(c, jeb, f, fd);
}
 
up(&c->erase_free_sem);
 
if (namebuf)
kfree(namebuf);
}
 
/* No need for it any more. Just mark it obsolete and remove it from the list */
while (*fdp) {
if ((*fdp) == fd) {
found = 1;
*fdp = fd->next;
break;
}
fdp = &(*fdp)->next;
}
if (!found) {
printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino);
}
jffs2_mark_node_obsolete(c, fd->raw);
jffs2_free_full_dirent(fd);
return 0;
}
 
static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end)
{
struct jffs2_raw_inode ri;
struct jffs2_node_frag *frag;
struct jffs2_full_dnode *new_fn;
uint32_t alloclen, phys_ofs;
int ret;
 
D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
f->inocache->ino, start, end));
memset(&ri, 0, sizeof(ri));
 
if(fn->frags > 1) {
size_t readlen;
uint32_t crc;
/* It's partially obsoleted by a later write. So we have to
write it out again with the _same_ version as before */
ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
if (readlen != sizeof(ri) || ret) {
printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen);
goto fill;
}
if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
ref_offset(fn->raw),
je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
return -EIO;
}
if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
ref_offset(fn->raw),
je32_to_cpu(ri.totlen), sizeof(ri));
return -EIO;
}
crc = crc32(0, &ri, sizeof(ri)-8);
if (crc != je32_to_cpu(ri.node_crc)) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
ref_offset(fn->raw),
je32_to_cpu(ri.node_crc), crc);
/* FIXME: We could possibly deal with this by writing new holes for each frag */
printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
start, end, f->inocache->ino);
goto fill;
}
if (ri.compr != JFFS2_COMPR_ZERO) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw));
printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
start, end, f->inocache->ino);
goto fill;
}
} else {
fill:
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri.totlen = cpu_to_je32(sizeof(ri));
ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
 
ri.ino = cpu_to_je32(f->inocache->ino);
ri.version = cpu_to_je32(++f->highest_version);
ri.offset = cpu_to_je32(start);
ri.dsize = cpu_to_je32(end - start);
ri.csize = cpu_to_je32(0);
ri.compr = JFFS2_COMPR_ZERO;
}
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
ri.data_crc = cpu_to_je32(0);
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 
ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
if (ret) {
printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
sizeof(ri), ret);
return ret;
}
new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL);
 
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
return PTR_ERR(new_fn);
}
if (je32_to_cpu(ri.version) == f->highest_version) {
jffs2_add_full_dnode_to_inode(c, f, new_fn);
if (f->metadata) {
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
}
return 0;
}
 
/*
* We should only get here in the case where the node we are
* replacing had more than one frag, so we kept the same version
* number as before. (Except in case of error -- see 'goto fill;'
* above.)
*/
D1(if(unlikely(fn->frags <= 1)) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
fn->frags, je32_to_cpu(ri.version), f->highest_version,
je32_to_cpu(ri.ino));
});
 
for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs);
frag; frag = frag_next(frag)) {
if (frag->ofs > fn->size + fn->ofs)
break;
if (frag->node == fn) {
frag->node = new_fn;
new_fn->frags++;
fn->frags--;
}
}
if (fn->frags) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Old node still has frags!\n");
BUG();
}
if (!new_fn->frags) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: New node has no frags!\n");
BUG();
}
jffs2_mark_node_obsolete(c, fn->raw);
jffs2_free_full_dnode(fn);
return 0;
}
 
static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end)
{
struct jffs2_full_dnode *new_fn;
struct jffs2_raw_inode ri;
uint32_t alloclen, phys_ofs, offset, orig_end;
int ret = 0;
unsigned char *comprbuf = NULL, *writebuf;
struct page *pg;
unsigned char *pg_ptr;
/* FIXME: */ struct inode *inode = OFNI_EDONI_2SFFJ(f);
 
memset(&ri, 0, sizeof(ri));
 
D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
f->inocache->ino, start, end));
 
orig_end = end;
 
/* If we're looking at the last node in the block we're
garbage-collecting, we allow ourselves to merge as if the
block was already erasing. We're likely to be GC'ing a
partial page, and the next block we GC is likely to have
the other half of this page right at the beginning, which
means we'd expand it _then_, as nr_erasing_blocks would have
increased since we checked, and in doing so would obsolete
the partial node which we'd have written here. Meaning that
the GC would churn and churn, and just leave dirty blocks in
it's wake.
*/
if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) {
/* Shitloads of space */
/* FIXME: Integrate this properly with GC calculations */
start &= ~(PAGE_CACHE_SIZE-1);
end = min_t(uint32_t, start + PAGE_CACHE_SIZE, JFFS2_F_I_SIZE(f));
D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n",
start, end));
if (end < orig_end) {
printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end);
end = orig_end;
}
}
/* First, use readpage() to read the appropriate page into the page cache */
/* Q: What happens if we actually try to GC the _same_ page for which commit_write()
* triggered garbage collection in the first place?
* A: I _think_ it's OK. read_cache_page shouldn't deadlock, we'll write out the
* page OK. We'll actually write it out again in commit_write, which is a little
* suboptimal, but at least we're correct.
*/
#ifdef __ECOS
pg = read_cache_page(start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
#else
pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
#endif
if (IS_ERR(pg)) {
printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
return PTR_ERR(pg);
}
pg_ptr = (char *)kmap(pg);
comprbuf = kmalloc(end - start, GFP_KERNEL);
 
offset = start;
while(offset < orig_end) {
uint32_t datalen;
uint32_t cdatalen;
char comprtype = JFFS2_COMPR_NONE;
 
ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
 
if (ret) {
printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
break;
}
cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
datalen = end - offset;
 
writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
 
if (comprbuf) {
comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen);
}
if (comprtype) {
writebuf = comprbuf;
} else {
datalen = cdatalen;
}
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
 
ri.ino = cpu_to_je32(f->inocache->ino);
ri.version = cpu_to_je32(++f->highest_version);
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
ri.offset = cpu_to_je32(offset);
ri.csize = cpu_to_je32(cdatalen);
ri.dsize = cpu_to_je32(datalen);
ri.compr = comprtype;
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen));
new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, NULL);
 
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
ret = PTR_ERR(new_fn);
break;
}
ret = jffs2_add_full_dnode_to_inode(c, f, new_fn);
offset += datalen;
if (f->metadata) {
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
}
}
if (comprbuf) kfree(comprbuf);
 
kunmap(pg);
/* XXX: Does the page get freed automatically? */
/* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */
page_cache_release(pg);
return ret;
}
 
/jffs2/v2_0/src/readinode.c
0,0 → 1,621
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: readinode.c,v 1.1.1.1 2004-02-14 13:29:20 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/crc32.h>
#include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
#include <linux/compiler.h>
#include "nodelist.h"
 
static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
 
#if CONFIG_JFFS2_FS_DEBUG >= 1
static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
{
struct jffs2_node_frag *this = frag_first(list);
uint32_t lastofs = 0;
int buggy = 0;
 
while(this) {
if (this->node)
printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n",
this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw),
this, frag_left(this), frag_right(this), frag_parent(this));
else
printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs,
this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this));
if (this->ofs != lastofs)
buggy = 1;
lastofs = this->ofs+this->size;
this = frag_next(this);
}
if (buggy && !permitbug) {
printk(KERN_CRIT "Frag tree got a hole in it\n");
BUG();
}
}
 
void jffs2_print_frag_list(struct jffs2_inode_info *f)
{
jffs2_print_fragtree(&f->fragtree, 0);
 
if (f->metadata) {
printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
}
}
#endif /* D1 */
 
static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
{
if (this->node) {
this->node->frags--;
if (!this->node->frags) {
/* The node has no valid frags left. It's totally obsoleted */
D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size));
jffs2_mark_node_obsolete(c, this->node->raw);
jffs2_free_full_dnode(this->node);
} else {
D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n",
ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size,
this->node->frags));
mark_ref_normal(this->node->raw);
}
}
jffs2_free_node_frag(this);
}
 
/* Given an inode, probably with existing list of fragments, add the new node
* to the fragment list.
*/
int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
{
int ret;
struct jffs2_node_frag *newfrag;
 
D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
 
newfrag = jffs2_alloc_node_frag();
if (unlikely(!newfrag))
return -ENOMEM;
 
D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
if (unlikely(!fn->size)) {
jffs2_free_node_frag(newfrag);
return 0;
}
 
newfrag->ofs = fn->ofs;
newfrag->size = fn->size;
newfrag->node = fn;
newfrag->node->frags = 1;
 
ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
if (ret)
return ret;
 
/* If we now share a page with other nodes, mark either previous
or next node REF_NORMAL, as appropriate. */
if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
struct jffs2_node_frag *prev = frag_prev(newfrag);
 
mark_ref_normal(fn->raw);
/* If we don't start at zero there's _always_ a previous */
if (prev->node)
mark_ref_normal(prev->node->raw);
}
 
if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
struct jffs2_node_frag *next = frag_next(newfrag);
if (next) {
mark_ref_normal(fn->raw);
if (next->node)
mark_ref_normal(next->node->raw);
}
}
D2(jffs2_print_frag_list(f));
return 0;
}
 
/* Doesn't set inode->i_size */
static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
{
struct jffs2_node_frag *this;
uint32_t lastend;
 
/* Skip all the nodes which are completed before this one starts */
this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
 
if (this) {
D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
lastend = this->ofs + this->size;
} else {
D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
lastend = 0;
}
/* See if we ran off the end of the list */
if (lastend <= newfrag->ofs) {
/* We did */
 
/* Check if 'this' node was on the same page as the new node.
If so, both 'this' and the new node get marked REF_NORMAL so
the GC can take a look.
*/
if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
if (this->node)
mark_ref_normal(this->node->raw);
mark_ref_normal(newfrag->node->raw);
}
 
if (lastend < newfrag->node->ofs) {
/* ... and we need to put a hole in before the new node */
struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
if (!holefrag)
return -ENOMEM;
holefrag->ofs = lastend;
holefrag->size = newfrag->node->ofs - lastend;
holefrag->node = NULL;
if (this) {
/* By definition, the 'this' node has no right-hand child,
because there are no frags with offset greater than it.
So that's where we want to put the hole */
D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
} else {
D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
rb_link_node(&holefrag->rb, NULL, &list->rb_node);
}
rb_insert_color(&holefrag->rb, list);
this = holefrag;
}
if (this) {
/* By definition, the 'this' node has no right-hand child,
because there are no frags with offset greater than it.
So that's where we want to put the hole */
D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);
} else {
D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
rb_link_node(&newfrag->rb, NULL, &list->rb_node);
}
rb_insert_color(&newfrag->rb, list);
return 0;
}
 
D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
 
/* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
* - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs
*/
if (newfrag->ofs > this->ofs) {
/* This node isn't completely obsoleted. The start of it remains valid */
 
/* Mark the new node and the partially covered node REF_NORMAL -- let
the GC take a look at them */
mark_ref_normal(newfrag->node->raw);
if (this->node)
mark_ref_normal(this->node->raw);
 
if (this->ofs + this->size > newfrag->ofs + newfrag->size) {
/* The new node splits 'this' frag into two */
struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
if (!newfrag2) {
jffs2_free_node_frag(newfrag);
return -ENOMEM;
}
D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
if (this->node)
printk("phys 0x%08x\n", ref_offset(this->node->raw));
else
printk("hole\n");
)
/* New second frag pointing to this's node */
newfrag2->ofs = newfrag->ofs + newfrag->size;
newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
newfrag2->node = this->node;
if (this->node)
this->node->frags++;
 
/* Adjust size of original 'this' */
this->size = newfrag->ofs - this->ofs;
 
/* Now, we know there's no node with offset
greater than this->ofs but smaller than
newfrag2->ofs or newfrag->ofs, for obvious
reasons. So we can do a tree insert from
'this' to insert newfrag, and a tree insert
from newfrag to insert newfrag2. */
jffs2_fragtree_insert(newfrag, this);
rb_insert_color(&newfrag->rb, list);
jffs2_fragtree_insert(newfrag2, newfrag);
rb_insert_color(&newfrag2->rb, list);
return 0;
}
/* New node just reduces 'this' frag in size, doesn't split it */
this->size = newfrag->ofs - this->ofs;
 
/* Again, we know it lives down here in the tree */
jffs2_fragtree_insert(newfrag, this);
rb_insert_color(&newfrag->rb, list);
} else {
/* New frag starts at the same point as 'this' used to. Replace
it in the tree without doing a delete and insertion */
D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n",
newfrag, newfrag->ofs, newfrag->ofs+newfrag->size,
this, this->ofs, this->ofs+this->size));
rb_replace_node(&this->rb, &newfrag->rb, list);
if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
jffs2_obsolete_node_frag(c, this);
} else {
this->ofs += newfrag->size;
this->size -= newfrag->size;
 
jffs2_fragtree_insert(this, newfrag);
rb_insert_color(&this->rb, list);
return 0;
}
}
/* OK, now we have newfrag added in the correct place in the tree, but
frag_next(newfrag) may be a fragment which is overlapped by it
*/
while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
/* 'this' frag is obsoleted completely. */
D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
rb_erase(&this->rb, list);
jffs2_obsolete_node_frag(c, this);
}
/* Now we're pointing at the first frag which isn't totally obsoleted by
the new frag */
 
if (!this || newfrag->ofs + newfrag->size == this->ofs) {
return 0;
}
/* Still some overlap but we don't need to move it in the tree */
this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
this->ofs = newfrag->ofs + newfrag->size;
 
/* And mark them REF_NORMAL so the GC takes a look at them */
if (this->node)
mark_ref_normal(this->node->raw);
mark_ref_normal(newfrag->node->raw);
 
return 0;
}
 
void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
{
struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
 
D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
 
/* We know frag->ofs <= size. That's what lookup does for us */
if (frag && frag->ofs != size) {
if (frag->ofs+frag->size >= size) {
D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
frag->size = size - frag->ofs;
}
frag = frag_next(frag);
}
while (frag && frag->ofs >= size) {
struct jffs2_node_frag *next = frag_next(frag);
 
D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
frag_erase(frag, list);
jffs2_obsolete_node_frag(c, frag);
frag = next;
}
}
 
/* Scan the list of all nodes present for this ino, build map of versions, etc. */
 
static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
struct jffs2_inode_info *f,
struct jffs2_raw_inode *latest_node);
 
int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint32_t ino, struct jffs2_raw_inode *latest_node)
{
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
 
retry_inocache:
spin_lock(&c->inocache_lock);
f->inocache = jffs2_get_ino_cache(c, ino);
 
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
 
if (f->inocache) {
/* Check its state. We may need to wait before we can use it */
switch(f->inocache->state) {
case INO_STATE_UNCHECKED:
case INO_STATE_CHECKEDABSENT:
f->inocache->state = INO_STATE_READING;
break;
case INO_STATE_CHECKING:
case INO_STATE_GC:
/* If it's in either of these states, we need
to wait for whoever's got it to finish and
put it back. */
D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
ino, f->inocache->state));
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
goto retry_inocache;
 
case INO_STATE_READING:
case INO_STATE_PRESENT:
/* Eep. This should never happen. It can
happen if Linux calls read_inode() again
before clear_inode() has finished though. */
printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
/* Fail. That's probably better than allowing it to succeed */
f->inocache = NULL;
break;
 
default:
BUG();
}
}
spin_unlock(&c->inocache_lock);
if (!f->inocache && ino == 1) {
/* Special case - no root inode on medium */
f->inocache = jffs2_alloc_inode_cache();
if (!f->inocache) {
printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
return -ENOMEM;
}
D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
f->inocache->ino = f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->state = INO_STATE_READING;
jffs2_add_ino_cache(c, f->inocache);
}
if (!f->inocache) {
printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
return -ENOENT;
}
 
return jffs2_do_read_inode_internal(c, f, latest_node);
}
 
int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{
struct jffs2_raw_inode n;
struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
 
if (!f)
return -ENOMEM;
 
memset(f, 0, sizeof(*f));
init_MUTEX_LOCKED(&f->sem);
f->inocache = ic;
 
return jffs2_do_read_inode_internal(c, f, &n);
}
 
static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
struct jffs2_inode_info *f,
struct jffs2_raw_inode *latest_node)
{
struct jffs2_tmp_dnode_info *tn_list, *tn;
struct jffs2_full_dirent *fd_list;
struct jffs2_full_dnode *fn = NULL;
uint32_t crc;
uint32_t latest_mctime, mctime_ver;
uint32_t mdata_ver = 0;
size_t retlen;
int ret;
 
D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
 
/* Grab all nodes relevant to this ino */
ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
 
if (ret) {
printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
if (f->inocache->state == INO_STATE_READING)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
return ret;
}
f->dents = fd_list;
 
while (tn_list) {
tn = tn_list;
 
fn = tn->fn;
 
if (f->metadata && tn->version > mdata_ver) {
D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
mdata_ver = 0;
}
 
if (fn->size) {
jffs2_add_full_dnode_to_inode(c, f, fn);
} else {
/* Zero-sized node at end of version list. Just a metadata update */
D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version));
f->metadata = fn;
mdata_ver = tn->version;
}
tn_list = tn->next;
jffs2_free_tmp_dnode_info(tn);
}
if (!fn) {
/* No data nodes for this inode. */
if (f->inocache->ino != 1) {
printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
if (!fd_list) {
if (f->inocache->state == INO_STATE_READING)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
return -EIO;
}
printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
}
latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
latest_node->version = cpu_to_je32(0);
latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
latest_node->isize = cpu_to_je32(0);
latest_node->gid = cpu_to_je16(0);
latest_node->uid = cpu_to_je16(0);
if (f->inocache->state == INO_STATE_READING)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
return 0;
}
 
ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
if (ret || retlen != sizeof(*latest_node)) {
printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
ret, retlen, sizeof(*latest_node));
/* FIXME: If this fails, there seems to be a memory leak. Find it. */
up(&f->sem);
jffs2_do_clear_inode(c, f);
return ret?ret:-EIO;
}
 
crc = crc32(0, latest_node, sizeof(*latest_node)-8);
if (crc != je32_to_cpu(latest_node->node_crc)) {
printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw));
up(&f->sem);
jffs2_do_clear_inode(c, f);
return -EIO;
}
 
switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
case S_IFDIR:
if (mctime_ver > je32_to_cpu(latest_node->version)) {
/* The times in the latest_node are actually older than
mctime in the latest dirent. Cheat. */
latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
}
break;
 
case S_IFREG:
/* If it was a regular file, truncate it to the latest node's isize */
jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize));
break;
 
case S_IFLNK:
/* Hack to work around broken isize in old symlink code.
Remove this when dwmw2 comes to his senses and stops
symlinks from being an entirely gratuitous special
case. */
if (!je32_to_cpu(latest_node->isize))
latest_node->isize = latest_node->dsize;
/* fall through... */
 
case S_IFBLK:
case S_IFCHR:
/* Certain inode types should have only one data node, and it's
kept as the metadata node */
if (f->metadata) {
printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
f->inocache->ino, jemode_to_cpu(latest_node->mode));
up(&f->sem);
jffs2_do_clear_inode(c, f);
return -EIO;
}
if (!frag_first(&f->fragtree)) {
printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
f->inocache->ino, jemode_to_cpu(latest_node->mode));
up(&f->sem);
jffs2_do_clear_inode(c, f);
return -EIO;
}
/* ASSERT: f->fraglist != NULL */
if (frag_next(frag_first(&f->fragtree))) {
printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
f->inocache->ino, jemode_to_cpu(latest_node->mode));
/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
up(&f->sem);
jffs2_do_clear_inode(c, f);
return -EIO;
}
/* OK. We're happy */
f->metadata = frag_first(&f->fragtree)->node;
jffs2_free_node_frag(frag_first(&f->fragtree));
f->fragtree = RB_ROOT;
break;
}
if (f->inocache->state == INO_STATE_READING)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
 
return 0;
}
 
void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
{
struct jffs2_full_dirent *fd, *fds;
/* I don't think we care about the potential race due to reading this
without f->sem. It can never get undeleted. */
int deleted = f->inocache && !f->inocache->nlink;
 
/* If it's a deleted inode, grab the alloc_sem. This prevents
jffs2_garbage_collect_pass() from deciding that it wants to
garbage collect one of the nodes we're just about to mark
obsolete -- by the time we drop alloc_sem and return, all
the nodes are marked obsolete, and jffs2_g_c_pass() won't
call iget() for the inode in question.
 
We also used to do this to keep the temporary BUG() in
jffs2_mark_node_obsolete() from triggering.
*/
if(deleted)
down(&c->alloc_sem);
 
down(&f->sem);
 
if (f->metadata) {
if (deleted)
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
}
 
jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
 
fds = f->dents;
 
while(fds) {
fd = fds;
fds = fd->next;
jffs2_free_full_dirent(fd);
}
 
if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 
up(&f->sem);
 
if(deleted)
up(&c->alloc_sem);
}
/jffs2/v2_0/src/write.c
0,0 → 1,618
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: write.c,v 1.1.1.1 2004-02-14 13:29:21 phoenix Exp $
*
*/
 
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/crc32.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
 
 
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
{
struct jffs2_inode_cache *ic;
 
ic = jffs2_alloc_inode_cache();
if (!ic) {
return -ENOMEM;
}
 
memset(ic, 0, sizeof(*ic));
 
init_MUTEX_LOCKED(&f->sem);
f->inocache = ic;
f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->ino = ++c->highest_ino;
f->inocache->state = INO_STATE_PRESENT;
 
ri->ino = cpu_to_je32(f->inocache->ino);
 
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
jffs2_add_ino_cache(c, f->inocache);
 
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri->totlen = cpu_to_je32(PAD(sizeof(*ri)));
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
ri->mode = cpu_to_jemode(mode);
 
f->highest_version = 1;
ri->version = cpu_to_je32(f->highest_version);
 
return 0;
}
 
#if CONFIG_JFFS2_FS_DEBUG > 0
static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
{
unsigned char buf[16];
size_t retlen;
int ret, i;
 
ret = jffs2_flash_read(c, ofs, 16, &retlen, buf);
if (ret || (retlen != 16)) {
D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen));
return;
}
ret = 0;
for (i=0; i<16; i++) {
if (buf[i] != 0xff)
ret = 1;
}
if (ret) {
printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs);
printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
ofs,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
}
}
#endif
 
 
/* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it,
write it to the flash, link it into the existing inode/fragment list */
 
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, uint32_t *writelen)
 
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dnode *fn;
size_t retlen;
struct iovec vecs[2];
int ret;
unsigned long cnt = 2;
 
D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
BUG();
}
);
vecs[0].iov_base = ri;
vecs[0].iov_len = sizeof(*ri);
vecs[1].iov_base = (unsigned char *)data;
vecs[1].iov_len = datalen;
 
D1(writecheck(c, flash_ofs));
 
if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
}
raw = jffs2_alloc_raw_node_ref();
if (!raw)
return ERR_PTR(-ENOMEM);
fn = jffs2_alloc_full_dnode();
if (!fn) {
jffs2_free_raw_node_ref(raw);
return ERR_PTR(-ENOMEM);
}
raw->flash_offset = flash_ofs;
raw->totlen = PAD(sizeof(*ri)+datalen);
raw->next_phys = NULL;
 
fn->ofs = je32_to_cpu(ri->offset);
fn->size = je32_to_cpu(ri->dsize);
fn->frags = 0;
fn->raw = raw;
 
/* check number of valid vecs */
if (!datalen || !data)
cnt = 1;
 
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen);
if (ret || (retlen != sizeof(*ri) + datalen)) {
printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
sizeof(*ri)+datalen, flash_ofs, ret, retlen);
/* Mark the space as dirtied */
if (retlen) {
/* Doesn't belong to any inode */
raw->next_in_ino = NULL;
 
/* Don't change raw->size to match retlen. We may have
written the node header already, and only the data will
seem corrupted, in which case the scan would skip over
any node we write before the original intended end of
this node */
raw->flash_offset |= REF_OBSOLETE;
jffs2_add_physical_node_ref(c, raw);
jffs2_mark_node_obsolete(c, raw);
} else {
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
jffs2_free_raw_node_ref(raw);
}
 
/* Release the full_dnode which is now useless, and return */
jffs2_free_full_dnode(fn);
if (writelen)
*writelen = retlen;
return ERR_PTR(ret?ret:-EIO);
}
/* Mark the space used */
/* If node covers at least a whole page, or if it starts at the
beginning of a page and runs to the end of the file, or if
it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
*/
if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) ||
( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) &&
(je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) {
raw->flash_offset |= REF_PRISTINE;
} else {
raw->flash_offset |= REF_NORMAL;
}
jffs2_add_physical_node_ref(c, raw);
 
/* Link into per-inode list */
raw->next_in_ino = f->inocache->nodes;
f->inocache->nodes = raw;
 
D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize),
je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
if (writelen)
*writelen = retlen;
 
f->inocache->nodes = raw;
return fn;
}
 
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, uint32_t *writelen)
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *fd;
size_t retlen;
struct iovec vecs[2];
int ret;
 
D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n",
je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
je32_to_cpu(rd->name_crc)));
D1(writecheck(c, flash_ofs));
 
D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
BUG();
}
);
 
vecs[0].iov_base = rd;
vecs[0].iov_len = sizeof(*rd);
vecs[1].iov_base = (unsigned char *)name;
vecs[1].iov_len = namelen;
raw = jffs2_alloc_raw_node_ref();
 
if (!raw)
return ERR_PTR(-ENOMEM);
 
fd = jffs2_alloc_full_dirent(namelen+1);
if (!fd) {
jffs2_free_raw_node_ref(raw);
return ERR_PTR(-ENOMEM);
}
raw->flash_offset = flash_ofs;
raw->totlen = PAD(sizeof(*rd)+namelen);
raw->next_in_ino = f->inocache->nodes;
f->inocache->nodes = raw;
raw->next_phys = NULL;
 
fd->version = je32_to_cpu(rd->version);
fd->ino = je32_to_cpu(rd->ino);
fd->nhash = full_name_hash(name, strlen(name));
fd->type = rd->type;
memcpy(fd->name, name, namelen);
fd->name[namelen]=0;
fd->raw = raw;
 
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen);
if (ret || (retlen != sizeof(*rd) + namelen)) {
printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
sizeof(*rd)+namelen, flash_ofs, ret, retlen);
/* Mark the space as dirtied */
if (retlen) {
raw->flash_offset |= REF_OBSOLETE;
jffs2_add_physical_node_ref(c, raw);
jffs2_mark_node_obsolete(c, raw);
} else {
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
jffs2_free_raw_node_ref(raw);
}
 
/* Release the full_dnode which is now useless, and return */
jffs2_free_full_dirent(fd);
if (writelen)
*writelen = retlen;
return ERR_PTR(ret?ret:-EIO);
}
/* Mark the space used */
raw->flash_offset |= REF_PRISTINE;
jffs2_add_physical_node_ref(c, raw);
if (writelen)
*writelen = retlen;
 
f->inocache->nodes = raw;
return fd;
}
 
/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that
we don't have to go digging in struct inode or its equivalent. It should set:
mode, uid, gid, (starting)isize, atime, ctime, mtime */
int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_raw_inode *ri, unsigned char *buf,
uint32_t offset, uint32_t writelen, uint32_t *retlen)
{
int ret = 0;
uint32_t writtenlen = 0;
 
D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n",
f->inocache->ino, offset, writelen));
while(writelen) {
struct jffs2_full_dnode *fn;
unsigned char *comprbuf = NULL;
unsigned char comprtype = JFFS2_COMPR_NONE;
uint32_t phys_ofs, alloclen;
uint32_t datalen, cdatalen;
 
D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
 
ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
break;
}
down(&f->sem);
datalen = writelen;
cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen);
 
comprbuf = kmalloc(cdatalen, GFP_KERNEL);
if (comprbuf) {
comprtype = jffs2_compress(buf, comprbuf, &datalen, &cdatalen);
}
if (comprtype == JFFS2_COMPR_NONE) {
/* Either compression failed, or the allocation of comprbuf failed */
if (comprbuf)
kfree(comprbuf);
comprbuf = buf;
datalen = cdatalen;
}
/* Now comprbuf points to the data to be written, be it compressed or not.
comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
that the comprbuf doesn't need to be kfree()d.
*/
 
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen);
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
 
ri->ino = cpu_to_je32(f->inocache->ino);
ri->version = cpu_to_je32(++f->highest_version);
ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen));
ri->offset = cpu_to_je32(offset);
ri->csize = cpu_to_je32(cdatalen);
ri->dsize = cpu_to_je32(datalen);
ri->compr = comprtype;
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 
fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, NULL);
 
if (comprtype != JFFS2_COMPR_NONE)
kfree(comprbuf);
 
if (IS_ERR(fn)) {
ret = PTR_ERR(fn);
up(&f->sem);
jffs2_complete_reservation(c);
break;
}
ret = jffs2_add_full_dnode_to_inode(c, f, fn);
if (f->metadata) {
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
}
if (ret) {
/* Eep */
D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
jffs2_mark_node_obsolete(c, fn->raw);
jffs2_free_full_dnode(fn);
 
up(&f->sem);
jffs2_complete_reservation(c);
break;
}
up(&f->sem);
jffs2_complete_reservation(c);
if (!datalen) {
printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
ret = -EIO;
break;
}
D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
writtenlen += datalen;
offset += datalen;
writelen -= datalen;
buf += datalen;
}
*retlen = writtenlen;
return ret;
}
 
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen)
{
struct jffs2_raw_dirent *rd;
struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd;
uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret;
 
/* Try to reserve enough space for both node and dirent.
* Just the node will do for now, though
*/
ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
if (ret) {
up(&f->sem);
return ret;
}
 
ri->data_crc = cpu_to_je32(0);
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen);
 
D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
jemode_to_cpu(ri->mode)));
 
if (IS_ERR(fn)) {
D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
/* Eeek. Wave bye bye */
up(&f->sem);
jffs2_complete_reservation(c);
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
obsoleted by the first data write
*/
f->metadata = fn;
 
/* Work out where to put the dirent node now. */
writtenlen = PAD(writtenlen);
phys_ofs += writtenlen;
alloclen -= writtenlen;
up(&f->sem);
 
if (alloclen < sizeof(*rd)+namelen) {
/* Not enough space left in this chunk. Get some more */
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
/* Eep. */
D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
return ret;
}
}
 
rd = jffs2_alloc_raw_dirent();
if (!rd) {
/* Argh. Now we treat it like a normal delete */
jffs2_complete_reservation(c);
return -ENOMEM;
}
 
down(&dir_f->sem);
 
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
 
rd->pino = cpu_to_je32(dir_f->inocache->ino);
rd->version = cpu_to_je32(++dir_f->highest_version);
rd->ino = ri->ino;
rd->mctime = ri->ctime;
rd->nsize = namelen;
rd->type = DT_REG;
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, &writtenlen);
 
jffs2_free_raw_dirent(rd);
if (IS_ERR(fd)) {
/* dirent failed to write. Delete the inode normally
as if it were the final unlink() */
jffs2_complete_reservation(c);
up(&dir_f->sem);
return PTR_ERR(fd);
}
 
/* Link the fd into the inode's list, obsoleting an old
one if necessary. */
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
 
jffs2_complete_reservation(c);
up(&dir_f->sem);
 
return 0;
}
 
 
int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
const char *name, int namelen, struct jffs2_inode_info *dead_f)
{
struct jffs2_raw_dirent *rd;
struct jffs2_full_dirent *fd;
uint32_t alloclen, phys_ofs;
int ret;
 
rd = jffs2_alloc_raw_dirent();
if (!rd)
return -ENOMEM;
 
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
if (ret) {
jffs2_free_raw_dirent(rd);
return ret;
}
 
down(&dir_f->sem);
 
/* Build a deletion node */
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
 
rd->pino = cpu_to_je32(dir_f->inocache->ino);
rd->version = cpu_to_je32(++dir_f->highest_version);
rd->ino = cpu_to_je32(0);
rd->mctime = cpu_to_je32(get_seconds());
rd->nsize = namelen;
rd->type = DT_UNKNOWN;
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL);
jffs2_free_raw_dirent(rd);
 
if (IS_ERR(fd)) {
jffs2_complete_reservation(c);
up(&dir_f->sem);
return PTR_ERR(fd);
}
 
/* File it. This will mark the old one obsolete. */
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
 
up(&dir_f->sem);
/* dead_f is NULL if this was a rename not a real unlink */
/* Also catch the !f->inocache case, where there was a dirent
pointing to an inode which didn't exist. */
if (dead_f && dead_f->inocache) {
 
down(&dead_f->sem);
 
while (dead_f->dents) {
/* There can be only deleted ones */
fd = dead_f->dents;
dead_f->dents = fd->next;
if (fd->ino) {
printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
dead_f->inocache->ino, fd->name, fd->ino);
} else {
D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino));
}
jffs2_mark_node_obsolete(c, fd->raw);
jffs2_free_full_dirent(fd);
}
 
dead_f->inocache->nlink--;
/* NB: Caller must set inode nlink if appropriate */
up(&dead_f->sem);
}
 
jffs2_complete_reservation(c);
 
return 0;
}
 
 
int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen)
{
struct jffs2_raw_dirent *rd;
struct jffs2_full_dirent *fd;
uint32_t alloclen, phys_ofs;
int ret;
 
rd = jffs2_alloc_raw_dirent();
if (!rd)
return -ENOMEM;
 
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
jffs2_free_raw_dirent(rd);
return ret;
}
down(&dir_f->sem);
 
/* Build a deletion node */
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
 
rd->pino = cpu_to_je32(dir_f->inocache->ino);
rd->version = cpu_to_je32(++dir_f->highest_version);
rd->ino = cpu_to_je32(ino);
rd->mctime = cpu_to_je32(get_seconds());
rd->nsize = namelen;
 
rd->type = type;
 
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL);
jffs2_free_raw_dirent(rd);
 
if (IS_ERR(fd)) {
jffs2_complete_reservation(c);
up(&dir_f->sem);
return PTR_ERR(fd);
}
 
/* File it. This will mark the old one obsolete. */
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
 
jffs2_complete_reservation(c);
up(&dir_f->sem);
 
return 0;
}
/jffs2/v2_0/src/histo.h
0,0 → 1,3
/* This file provides the bit-probabilities for the input file */
#define BIT_DIVIDER 629
static int bits[9] = { 179,167,183,165,159,198,178,119,}; /* ia32 .so files */
/jffs2/v2_0/support/jffs2/jffs2.img Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
jffs2/v2_0/support/jffs2/jffs2.img Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: jffs2/v2_0/support/jffs2/etc/passwd =================================================================== --- jffs2/v2_0/support/jffs2/etc/passwd (nonexistent) +++ jffs2/v2_0/support/jffs2/etc/passwd (revision 174) @@ -0,0 +1,19 @@ +root:x:0:0:root:/root:/bin/bash +bin:x:1:1:bin:/bin: +daemon:x:2:2:daemon:/sbin: +adm:x:3:4:adm:/var/adm: +lp:x:4:7:lp:/var/spool/lpd: +sync:x:5:0:sync:/sbin:/bin/sync +shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown +halt:x:7:0:halt:/sbin:/sbin/halt +mail:x:8:12:mail:/var/spool/mail: +news:x:9:13:news:/var/spool/news: +uucp:x:10:14:uucp:/var/spool/uucp: +operator:x:11:0:operator:/root: +games:x:12:100:games:/usr/games: +gopher:x:13:30:gopher:/usr/lib/gopher-data: +ftp:x:14:50:FTP User:/home/ftp: +nobody:x:99:99:Nobody:/: +xfs:x:43:43:X Font Server:/etc/X11/fs:/bin/false +gdm:x:42:42::/home/gdm:/bin/bash +dostrowski:x:500:500:Dominic Ostrowski:/home/dostrowski:/bin/bash Index: jffs2/v2_0/support/jffs2/etc/group =================================================================== --- jffs2/v2_0/support/jffs2/etc/group (nonexistent) +++ jffs2/v2_0/support/jffs2/etc/group (revision 174) @@ -0,0 +1,31 @@ +root:x:0:root +bin:x:1:root,bin,daemon +daemon:x:2:root,bin,daemon +sys:x:3:root,bin,adm +adm:x:4:root,adm,daemon +tty:x:5: +disk:x:6:root +lp:x:7:daemon,lp +mem:x:8: +kmem:x:9: +wheel:x:10:root +mail:x:12:mail +news:x:13:news +uucp:x:14:uucp +man:x:15: +games:x:20: +gopher:x:30: +dip:x:40: +ftp:x:50: +nobody:x:99: +users:x:100: +floppy:x:19: +utmp:x:22: +xfs:x:43: +console:x:31: +gdm:x:42: +pppusers:x:44: +popusers:x:45: +slipusers:x:46: +slocate:x:21: +dostrows:x:500: Index: rom/v2_0/cdl/romfs.cdl =================================================================== --- rom/v2_0/cdl/romfs.cdl (nonexistent) +++ rom/v2_0/cdl/romfs.cdl (revision 174) @@ -0,0 +1,111 @@ +# ==================================================================== +# +# romfs.cdl +# +# ROM 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: richard.panton@3glab.com +# Date: 2000-08-01 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_FS_ROM { + display "ROM filesystem" + doc ref/fileio.html + include_dir cyg/romfs + + requires CYGPKG_IO_FILEIO + + requires CYGPKG_ISOINFRA + requires CYGINT_ISO_ERRNO + requires CYGINT_ISO_ERRNO_CODES + + implements CYGINT_IO_FILEIO_FS + + compile -library=libextras.a romfs.c + + # ---------------------------------------------------------------- + # Tests + + cdl_component CYGTST_ROMFS_BUILD_TESTS { + display "Build ROMFS tests" + flavor bool + no_define + default_value 0 + requires CYGINT_LIBC_STARTUP_CONTEXT + requires CYGINT_ISO_STDIO_FORMATTED_IO + requires CYGINT_ISO_STRERROR + description " + This option enables the building of the ROMFS tests." + + # FIXME: host compiler/flags should be provided by config system + make -priority 100 { + /bin/mk_romfs: /support/mk_romfs.c + @mkdir -p "$(dir $@)" + @$(HOST_CC) -g -O2 -o $@ $< || cc -g -O2 -o $@ $< || gcc -g -O2 -o $@ $< + } + + make -priority 100 { + /include/cyg/romfs/testromfs.h : $(PREFIX)/bin/mk_romfs /support/file2c.tcl + $(PREFIX)/bin/mk_romfs $(REPOSITORY)/$(PACKAGE)/tests/testromfs testromfs.bin + @mkdir -p "$(dir $@)" + # work around cygwin path problems by copying to build dir + @cp $(REPOSITORY)/$(PACKAGE)/support/file2c.tcl . + sh file2c.tcl testromfs.bin testromfs.h + @rm -f $@ file2c.tcl + @cp testromfs.h $@ + } + + + cdl_option CYGPKG_FS_ROM_TESTS { + display "ROM FS tests" + flavor data + no_define + calculated { "tests/fileio1.c" } + description " + This option specifies the set of tests for the ROM FS package." + } + } +} + +# End of romfs.cdl Index: rom/v2_0/tests/testromfs/var/foobar =================================================================== --- rom/v2_0/tests/testromfs/var/foobar (nonexistent) +++ rom/v2_0/tests/testromfs/var/foobar (revision 174) @@ -0,0 +1 @@ +flibble Index: rom/v2_0/tests/testromfs/etc/hosts =================================================================== Index: rom/v2_0/tests/testromfs/etc/passwd =================================================================== --- rom/v2_0/tests/testromfs/etc/passwd (nonexistent) +++ rom/v2_0/tests/testromfs/etc/passwd (revision 174) @@ -0,0 +1,32 @@ +root:x:0:0:root:/root:/bin/bash +daemon:x:1:1:daemon:/usr/sbin:/bin/sh +bin:x:2:2:bin:/bin:/bin/sh +sys:x:3:3:sys:/dev:/bin/sh +sync:x:4:100:sync:/bin:/bin/sync +games:x:5:100:games:/usr/games:/bin/sh +man:x:6:100:man:/var/cache/man:/bin/sh +lp:x:7:7:lp:/var/spool/lpd:/bin/sh +mail:x:8:8:mail:/var/spool/mail:/bin/sh +news:x:9:9:news:/var/spool/news:/bin/sh +uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh +proxy:x:13:13:proxy:/bin:/bin/sh +majordom:x:30:31:Majordomo:/usr/lib/majordomo:/bin/sh +postgres:x:31:32:postgres:/var/lib/postgres:/bin/sh +www-data:x:33:33:www-data:/var/www:/bin/sh +backup:x:34:34:backup:/var/backups:/bin/sh +msql:x:36:36:Mini SQL Database Manager:/var/lib/msql:/bin/sh +operator:x:37:37:Operator:/var:/bin/sh +list:x:38:38:SmartList:/var/list:/bin/sh +irc:x:39:39:ircd:/var:/bin/sh +gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats/gnats-db:/bin/sh +alias:x:70:65534:qmail alias:/var/qmail/alias:/bin/sh +qmaild:x:71:65534:qmail daemon:/var/qmail:/bin/sh +qmails:x:72:70:qmail send:/var/qmail:/bin/sh +qmailr:x:73:70:qmail remote:/var/qmail:/bin/sh +qmailq:x:74:70:qmail queue:/var/qmail:/bin/sh +qmaill:x:75:65534:qmail log:/var/qmail:/bin/sh +qmailp:x:76:65534:qmail pw:/var/qmail:/bin/sh +ftp:x:60000:65534::/lhome/ftp:/bin/false +nobody:x:65534:65534:nobody:/home:/bin/sh +identd:x:60001:65534::/var/run/identd:/bin/false +telnetd:x:60002:60002::/usr/lib/telnetd:/bin/false Index: rom/v2_0/tests/testromfs/etc/inetd =================================================================== Index: rom/v2_0/tests/testromfs/mnt/thing =================================================================== --- rom/v2_0/tests/testromfs/mnt/thing (nonexistent) +++ rom/v2_0/tests/testromfs/mnt/thing (revision 174) @@ -0,0 +1 @@ +hi Index: rom/v2_0/tests/fileio1.c =================================================================== --- rom/v2_0/tests/fileio1.c (nonexistent) +++ rom/v2_0/tests/fileio1.c (revision 174) @@ -0,0 +1,377 @@ +//========================================================================== +// +// 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, richard.panton@3glab.com, jlarmour +// 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include // HAL polled output + +#include // Test ROMFS data + +//========================================================================== + +MTAB_ENTRY( romfs_mte1, + "/", + "romfs", + "", + (CYG_ADDRWORD) &filedata[0] ); + + +//========================================================================== + +#define SHOW_RESULT( _fn, _res ) \ +diag_printf(": " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):""); + +#define CHKFAIL_TYPE( _fn, _res, _type ) { \ +if ( _res != -1 ) \ + diag_printf(": " #_fn "() returned %d (expected -1)\n", _res); \ +else if ( errno != _type ) \ + diag_printf(": " #_fn "() failed with errno %d (%s),\n expected %d (%s)\n", errno, strerror(errno), _type, strerror(_type) ); \ +} + +//========================================================================== + +#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 CYGINT_ISO_STRING_STRFUNCS + +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 err; + DIR *dirp; + + diag_printf(": 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; + + diag_printf(": 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(" "); + 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 ); +} + +//========================================================================== + +#ifdef CYGPKG_FS_RAM +static void copyfile( char *name2, char *name1 ) +{ + + int err; + char buf[IOSIZE]; + int fd1, fd2; + ssize_t done, wrote; + + diag_printf(": 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 ); + +} +#endif + +//========================================================================== + +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(": 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 ); + +} + +//========================================================================== +// main + +int main( int argc, char **argv ) +{ + int err; + char address[16]; + + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + diag_printf(": ROMFS root follows\n"); + listdir( "/", true ); + + diag_printf(": cd /etc\n" ); + err = chdir( "/etc" ); + if ( err < 0 ) SHOW_RESULT( chdir, err ); + + diag_printf(": ROMFS list of '' follows\n"); + listdir( "", true ); + + diag_printf(": ROMFS list of /etc follows\n"); + listdir( "/etc", true ); + + diag_printf(": ROMFS list of . follows\n"); + listdir( ".", true ); + +#ifdef CYGPKG_FS_RAM + err = mount( "", "/var", "ramfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + copyfile( "/etc/passwd", "/var/passwd_copy" ); + + comparefiles( "/etc/passwd", "/var/passwd_copy" ); +#endif + + diag_printf(": ROMFS list of / follows\n"); +#ifdef CYGPKG_FS_RAM + diag_printf(": Note that /var now gives stat() info for RAMFS\n"); +#endif + listdir( "/", true ); + + diag_printf(": Mount ROMFS again onto /mnt\n"); + sprintf( address, "%p", (void*)&filedata[0] ); + err = mount( address, "/mnt", "romfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + comparefiles( "/etc/passwd", "/mnt/etc/passwd" ); + + + err = mkdir( "/foo", 0 ); + CHKFAIL_TYPE( mkdir, err, EROFS ); + + err = rename( "/var", "/tmp" ); // RAMFS is mounted here +#ifdef CYGPKG_FS_RAM + CHKFAIL_TYPE( rename, err, EXDEV ); +#else + CHKFAIL_TYPE( rename, err, EROFS ); +#endif + + err = rename( "/var/passwd_copy", "/mnt/etc/passwd_copy" ); + CHKFAIL_TYPE( rename, err, EXDEV ); + + err = rename( "/etc", "/tmp" ); + CHKFAIL_TYPE( rename, err, EROFS ); + + diag_printf(": cd /etc\n"); + err = chdir( "/etc" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + err = chdir( "/mnt/etc" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + listdir( ".", true ); + + diag_printf(": unlink /tmp\n"); + err = unlink( "/tmp" ); + CHKFAIL_TYPE( unlink, err, EROFS ); + + diag_printf(": mount random area\n"); + sprintf(address, "%p", (void*)(&filedata[0] + 0x100)); + err = mount( address, "/tmp", "romfs" ); + CHKFAIL_TYPE( mount, err, ENOENT ); + + err = umount( "/mnt" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + err = umount( "/var" ); +#ifdef CYGPKG_FS_RAM + if( err < 0 ) SHOW_RESULT( umount, err ); +#else + CHKFAIL_TYPE( umount, err, EINVAL ); +#endif + + err = umount( "/" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + + CYG_TEST_PASS_FINISH("fileio1"); +} + +// ------------------------------------------------------------------------- +// EOF fileio1.c Index: rom/v2_0/doc/romfs.txt =================================================================== --- rom/v2_0/doc/romfs.txt (nonexistent) +++ rom/v2_0/doc/romfs.txt (revision 174) @@ -0,0 +1,12 @@ +ROMFS - A rom filesystem plug-in module for eCos +================================================ + +You can use this module if you have flash ROM available to install the +filesystem into. Instructions are given for creating and installing a +rom filesystem into a RedBoot monitor. + +See mk_romfs for instructions for making the filesystem. + +See romfs.c for comments about the filesystem structure. + + Index: rom/v2_0/doc/mk_romfs.txt =================================================================== --- rom/v2_0/doc/mk_romfs.txt (nonexistent) +++ rom/v2_0/doc/mk_romfs.txt (revision 174) @@ -0,0 +1,94 @@ +MK_ROMFS - Make a ROMFS image +============================= + +This program creates a ROMFS image that can be read by eCos. + +mk_romfs - Create an eCos ROMFS disk image from the files + contained under a specified directory + +Usage: ../support/mk_romfs [options] + fs_root is the directory containing the files to package into the ROMFS image + fs_file is the name of the ROMFS image file to create + Options include: + -v / -q increase / decrease verbosity + -n do everything EXCEPT creating the output file + -b write a big-endian image (default is little endian) + -l collapse hard links to a single node + +----------- +How to use. +----------- + +For example, suppose you wish to access the following directories and files: + / + /etc passwd, group + /dev + /mnt + /tmp (for a RAMFS) + /var (for a RAMFS) + +1. Create the required directories and files under a suitable root, eg. under + ~/rom: + $ mkdir ~/rom + $ cd ~/rom + $ mkdir etc dev mnt tmp var + $ cp /etc/passwd /etc/group etc/ + ( remembering to edit these files....;-) + +2. Make the romfs image in a suitable place, eg /tftpboot for direct upload to + the RedBoot monitor. + $ mk_romfs -v . /tftpboot/romfs.img + mk_romfs: Verbosity 2 little endian + Phase 1 - Build file list + Phase 2 - Calculate space allocation + Phase 2a - * Directories + Phase 2b - * Regular files + Phase 2c - * Executable files + Phase 3 - Construct ROMFS image file (3 kb) + Phase 3a - * Node table + Phase 3b - * Data blocks + /tftpboot/romfs.img completed + +3. Connect to your target RedBoot monitor, and load the romfs image. You will + need to determine a suitable place in RAM to load the image into. + $ telnet xxx.xxx.xxx.xxx 1000 + Trying xxx.xxx.xxx.xxx... + Connected to xxx.xxx.xxx.xxx. + Escape character is '^]'. + RedBoot> load romfs.img -r -v -b 0x1000000 + Raw file loaded 0x01000000-0x0100093e + RedBoot> + +4. Determine where to load the romfs image in the ROM. See what's there... + RedBoot> fis list + Name FLASH addr Mem addr Length Entry point + RedBoot 0x50000000 0x50000000 0x020000 0x00000000 + RedBoot[backup] 0x50020000 0x50020000 0x020000 0x00000000 + RedBoot config 0x503C0000 0x503C0000 0x020000 0x00000000 + FIS directory 0x503E0000 0x503E0000 0x020000 0x00000000 + RedBoot> fis free + 0x50040000 .. 0x503C0000 + RedBoot> + We can see that a suitable place would be 0x50040000. + Alternatively, you can let RedBoot determine the address itself... + +5. Copy the image from RAM to ROM... + RedBoot> fis create -b 0x1000000 -l 0x940 RomFs + ... Erase from 0x50040000-0x50040940: . + ... Program from 0x01000000-0x01000940 at 0x50040000: . + ... Erase from 0x503e0000-0x50400000: . + ... Program from 0x01fd0000-0x01ff0000 at 0x503e0000: . + RedBoot> fis list + Name FLASH addr Mem addr Length Entry point + RedBoot 0x50000000 0x50000000 0x020000 0x00000000 + RedBoot[backup] 0x50020000 0x50020000 0x020000 0x00000000 + RedBoot config 0x503C0000 0x503C0000 0x020000 0x00000000 + FIS directory 0x503E0000 0x503E0000 0x020000 0x00000000 + RomFs 0x50040000 0x01000000 0x000940 0x00000000 + RedBoot> + +6. MAKE A NOTE OF THE ADDRESS IN FLASH THAT THE IMAGE IS LOADED AT. + The ROMFS option 'CYGNUM_FS_ROM_BASE_ADDRESS' needs to be set to this + value in order to access the filesystem. + + Index: rom/v2_0/ChangeLog =================================================================== --- rom/v2_0/ChangeLog (nonexistent) +++ rom/v2_0/ChangeLog (revision 174) @@ -0,0 +1,106 @@ +2003-02-24 Jonathan Larmour + + * cdl/romfs.cdl: Fix doc link. + +2003-01-30 Andrew Lunn + + * cdl/romfs.cdl: Implements the CYGINT_IO_FILEIO_FS interface. + +2003-01-29 John Dallaway + + * support/file2c.tcl: Accommodate latest Cygwin Tcl shell + (tclsh83.exe) + +2002-04-15 Bart Veer + + * support/file2c.tcl: + Do not use an alignment attribute, since it is not honoured on + all targets. + + * src/romfs.c: + Remove alignment restrictions, since they are not actually needed + yet and alignment is hard to guarantee on all targets. + +2002-01-21 Jonathan Larmour + + * support/mk_romfs.c: Open image file in binary mode (for cygwin). + Spotted by Warren Jasper. + +2001-11-23 Jonathan Larmour + + * cdl/romfs.cdl (CYGTST_ROMFS_BUILD_TESTS): Try gcc and cc if $HOST_CC + doesn't exist or has a problem. + +2001-11-22 Jesper Skov + + * cdl/romfs.cdl: Use HOST_CC instead of 'cc'. + +2001-10-17 Drew Moseley +2001-10-17 Jonathan Larmour + + * support/mk_romfs.c: Open input files in binary mode (for cygwin). + * cdl/romfs.cdl: Work around cygwin path problems by copying files + into build tree. + +2001-07-20 Jonathan Larmour + + * tests/fileio1.c (main): Get this to actually pass! Remove + kernel dependency. + * cdl/fileio.cdl: Get CDL dependencies better. Don't use + fixed base address. Make test building an option. Build mk_romfs + and use it to construct a test romfs. + * support/mk_romfs.c: fix trivial typo + * tests/testromfs: Directory hierarchy added for constructing test + ROMFS. + +2001-07-13 Richard Panton (richard.panton@3glab.com) + + * support/mk_romfs.c: Convert between host FS file modes and eCos + ones. + +2000-10-25 Richard Panton (richard.panton@3glab.com) + + * cdl/romfs.cdl: + * src/romfs.c: + * support/mk_romfs.c: + * tests/fileio1.c: + A sample ROM filesystem implementation + + + +//=========================================================================== +//####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#### +//=========================================================================== + + Index: rom/v2_0/src/romfs.c =================================================================== --- rom/v2_0/src/romfs.c (nonexistent) +++ rom/v2_0/src/romfs.c (revision 174) @@ -0,0 +1,1094 @@ +//========================================================================== +// +// romfs.c +// +// ROM 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, richard.panton@3glab.com +// Date: 2000-07-25 +// Purpose: ROM file system +// Description: This is a ROM 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 ROM 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. +// +// +// Header +// ------ +// +// There is a single header that describes the overall format of the ROMFS +// disk. The address of this header is used as the base for all offsets used +// in the node and directory structures. It contains the following fields: +// +// label - A human readable label for various purposes +// fssize - The size in bytes of the entire ROMFS disk +// nodes - A count of the nodes in the disk +// +// Immediately following thisin memory is the node table, consisting of +// 'nodes' repetitions of the node object. +// +// Nodes +// ----- +// +// All files and directories are represented by node objects. Each +// romfs_node structure contains the following fields: +// +// mode - Node type, file or directory. +// 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. +// ctime - Creation time of the file (NOT the ROMFS) +// data - Offset of the first data byte for this node from the header +// +// Directories +// ----------- +// +// A directory is a node whose data is a list of directory entries. +// These contain the +// following fields: +// +// node - Index of the node in the romfs_disk table that is referenced by +// this entry. This is present in every directory entry fragment. +// next - Offset of the next name entry. +// name - The filename associated with this link to the node. +// +// Data Storage +// ------------ +// +// Each file has its data stored in a single contiguous memory block +// referenced by the offset in the node. +// +//========================================================================== + +#include +#include +#include +#include +#include + +#include // base kernel types +#include // tracing macros +#include // assertion macros + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +//========================================================================== +// Eventually we want to eXecute In Place from the ROM in a protected +// environment, so we'll need executables to be aligned to a boundary +// suitable for MMU protection. A suitable boundary would be the 4k +// boundary in all the CPU architectures I am currently aware of. + +// Forward definitions + +// Filesystem operations +static int romfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ); +static int romfs_umount ( cyg_mtab_entry *mte ); +static int romfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int mode, cyg_file *fte ); +static int romfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_file *fte ); +static int romfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_dir *dir_out ); +static int romfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + struct stat *buf); +static int romfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); +static int romfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); + +// File operations +static int romfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int romfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); +static int romfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data); +static int romfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ); +static int romfs_fo_close (struct CYG_FILE_TAG *fp); +static int romfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ); +static int romfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); +static int romfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); + +// Directory operations +static int romfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int romfs_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( romfs_fste, "romfs", 0, + CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM, + romfs_mount, + romfs_umount, + romfs_open, + (cyg_fsop_unlink *)cyg_fileio_erofs, + (cyg_fsop_mkdir *)cyg_fileio_erofs, + (cyg_fsop_rmdir *)cyg_fileio_erofs, + (cyg_fsop_rename *)cyg_fileio_erofs, + (cyg_fsop_link *)cyg_fileio_erofs, + romfs_opendir, + romfs_chdir, + romfs_stat, + romfs_getinfo, + romfs_setinfo); + +// ------------------------------------------------------------------------- +// mtab entry. +// This defines a single ROMFS loaded into ROM at the configured address +// +// MTAB_ENTRY( rom_mte, // structure name +// "/rom", // mount point +// "romfs", // FIlesystem type +// "", // hardware device +// (CYG_ADDRWORD) CYGNUM_FS_ROM_BASE_ADDRESS // Address in ROM +// ); + + +// ------------------------------------------------------------------------- +// File operations. +// This set of file operations are used for normal open files. + +static cyg_fileops romfs_fileops = +{ + romfs_fo_read, + (cyg_fileop_write *)cyg_fileio_erofs, + romfs_fo_lseek, + romfs_fo_ioctl, + cyg_fileio_seltrue, + romfs_fo_fsync, + romfs_fo_close, + romfs_fo_fstat, + romfs_fo_getinfo, + romfs_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 romfs_dirops = +{ + romfs_fo_dirread, + (cyg_fileop_write *)cyg_fileio_enosys, + romfs_fo_dirlseek, + (cyg_fileop_ioctl *)cyg_fileio_enosys, + cyg_fileio_seltrue, + (cyg_fileop_fsync *)cyg_fileio_enosys, + romfs_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 romfs_disk; +typedef struct romfs_disk romfs_disk; + +struct romfs_node; +typedef struct romfs_node romfs_node; + +struct romfs_dirent; +typedef struct romfs_dirent romfs_dirent; + +//========================================================================== +// File and directory node +// This data structure represents a file or directory. + +struct romfs_node +{ + cyg_uint32 mode; // 0-3 node type + cyg_ucount32 nlink; // 4-7 number of links to this node + cyg_uint16 uid; // 8-9 Owner id + cyg_uint16 gid; // 10-11 Group id + cyg_uint32 size; // 12-15 size of file in bytes + cyg_uint32 ctime; // 16-19 creation status time + cyg_uint32 offset; // 20-23 offset of data from start of ROMFS + cyg_uint32 pad[2]; // 24-31 padding to align to 32byte boundary +}; + +//========================================================================== +// Directory entry. +// Variable sized entry containing the name and node of a directory entry + +struct romfs_dirent +{ + cyg_ucount32 node; // Index of node in romfs_disk structure + cyg_uint32 next; // Offset from start of directory of + // a) the next entry, or + // b) the end of the directory data + char name[0]; // The name, NUL terminated +}; + +//========================================================================== +// ROMFS header +// This data structure contains overall information on the ROMFS + +struct romfs_disk +{ + cyg_uint32 magic; // 0-3 Marks a valid ROMFS entry + cyg_ucount32 nodecount; // 4-7 Count of nodes in this filesystem + cyg_ucount32 disksize; // 8-11 Count of bytes in this filesystem + cyg_uint32 dev_id; // 12-15 ID of disk (put into stat.st_dev) + char name[16]; // 16-31 Name - pads to 32 bytes + romfs_node node[0]; +}; + +#define ROMFS_MAGIC 0x526f6d2e // The magic signature word for a romfs +#define ROMFS_CIGAM 0x2e6d6f52 // The byte sex is wrong if you see this + +//========================================================================== +// 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 romfs_dirsearch +{ + romfs_disk *disk; // disk structure + romfs_node *dir; // directory to search + const char *path; // path to follow + romfs_node *node; // Node found + const char *name; // last name used + int namelen; // name fragment length + cyg_bool last; // last name in path? +}; + +typedef struct romfs_dirsearch romfs_dirsearch; + +//========================================================================== +// This seems to be the only string function referenced. Define as static +// here to avoid having to load the string library + +static bool match( const char *a, const char *b, int len ) +{ + for ( ; len > 0 && *a && *b && *a == *b ; a++, b++, len-- ) + ; + return ( len == 0 ); +} + + +//========================================================================== +// SIMPLE buffer management. +// Each node has a data buffer pointer and a size. + +// ------------------------------------------------------------------------- +// findbuffer_node() +// return a pointer to the data at the indicated file position. + +static int findbuffer_node( romfs_disk *disk, // header pointer + romfs_node *node, // node pointer + off_t pos, // data position to get + cyg_uint8 **buffer, // returned buffer pointer + size_t *size) // returned buffer size +{ + if ( pos >= node->size || node->size == 0 ) + { + // Indicate end of data. + *size = 0; + } else { + + // Calculate the buffer position + *buffer = (cyg_uint8*)disk + node->offset + pos; + *size = node->size-pos; + } + + return ENOERR; +} + +//========================================================================== +// Directory operations + + +// ------------------------------------------------------------------------- +// find_direntry() +// Find a directory entry for the name and return a pointer to the first +// entry fragment. + +static romfs_dirent *find_direntry( romfs_disk *disk, romfs_node *dir, const char *name, int namelen ) +{ + 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 ) + { + romfs_dirent *d; + cyg_uint8 *buf; + size_t size; + + err = findbuffer_node( disk, dir, pos, &buf, &size ); + if( err != ENOERR || size == 0) + return NULL; + + d = (romfs_dirent *)buf; + + // Is this the directory entry we're looking for? + if ( match( d->name, name, namelen ) ) + return d; + + // Otherwise move on to next entry in chain + pos = d->next; + } + + return NULL; +} + +//========================================================================== +// Directory search + +// ------------------------------------------------------------------------- +// init_dirsearch() +// Initialize a dirsearch object to start a search + +static void init_dirsearch( romfs_dirsearch *ds, + romfs_disk *disk, + romfs_node *dir, + const char *name) +{ + ds->disk = disk; + 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( romfs_dirsearch *ds ) +{ + romfs_node *dir = ds->dir; + const char *name = ds->path; + const char *n = name; + int namelen = 0; + romfs_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( ds->disk, dir, name, namelen ); + + if( d == NULL ) + return ENOENT; + + // pass back the node we have found + ds->node = &ds->disk->node[d->node]; + + return ENOERR; + +} + +// ------------------------------------------------------------------------- +// romfs_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 romfs_find( romfs_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 romfs_pathconf( romfs_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 + +// ------------------------------------------------------------------------- +// romfs_mount() +// Process a mount request. This mainly finds root for the +// filesystem. + +static int romfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ) +{ + romfs_disk *disk; + + if ( !mte->data ) { + // If the image address was not in the MTE data word, + if ( mte->devname && mte->devname[0] ) { + // And there's something in the 'hardware device' field, + // then read the address from there. + sscanf( mte->devname, "%p", (char**)&mte->data ); + } + } + + if ( !mte->data ) { + // If still no address, try the FSTAB entry data word + mte->data = fste->data; + } + + if ( !mte->data ) { + // If still no address, give up... + return ENOENT; + } + + disk = (romfs_disk *)mte->data; + + // Check the ROMFS magic number to ensure that there's really an fs. + if ( disk->magic == ROMFS_CIGAM ) { + // The disk image has the wrong byte sex!!! + return EIO; + } else if ( disk->magic != ROMFS_MAGIC || disk->nodecount == 0 ) { + // No image found + return ENOENT; + } + + mte->root = (cyg_dir)&disk->node[0]; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_umount() +// Unmount the filesystem. This will currently always succeed. + +static int romfs_umount ( cyg_mtab_entry *mte ) +{ + // Clear root pointer + mte->root = CYG_DIR_NULL; + + // That's all folks. + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_open() +// Open a file for reading + +static int romfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int mode, cyg_file *file ) +{ + + romfs_dirsearch ds; + romfs_node *node = NULL; + int err; + + init_dirsearch( &ds, (romfs_disk *)mte->data, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err == ENOENT ) + { + return ENOENT; + } + 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 fail the open + + err = EPERM; + } + + if( err != ENOERR ) return err; + + // Check that we actually have a file here + if( S_ISDIR(node->mode) ) return EISDIR; + + // Initialize the file object + + file->f_flag |= mode & CYG_FILE_MODE_MASK; + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &romfs_fileops; + file->f_offset = 0; + file->f_data = (CYG_ADDRWORD)node; + file->f_xops = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_opendir() +// Open a directory for reading. + +static int romfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_file *file ) +{ + romfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (romfs_disk *)mte->data, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err != ENOERR ) return err; + + // check it is really a directory. + if( !S_ISDIR(ds.node->mode) ) return ENOTDIR; + + // 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 = &romfs_dirops; + file->f_offset = 0; + file->f_data = (CYG_ADDRWORD)ds.node; + file->f_xops = 0; + + return ENOERR; + +} + +// ------------------------------------------------------------------------- +// romfs_chdir() +// Change directory support. + +static int romfs_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. + + romfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (romfs_disk *)mte->data, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err != ENOERR ) return err; + + // check it is a directory + if( !S_ISDIR(ds.node->mode) ) + return ENOTDIR; + + // Pass it out + *dir_out = (cyg_dir)ds.node; + } + // 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. Do nothing in ROMFS. + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_stat() +// Get struct stat info for named object. + +static int romfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + struct stat *buf) +{ + romfs_dirsearch ds; + int err; + romfs_disk *disk = (romfs_disk *)mte->data; + + init_dirsearch( &ds, disk, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err != ENOERR ) return err; + + // Fill in the status + buf->st_mode = ds.node->mode; + buf->st_ino = (ino_t)(ds.node - &disk->node[0]); + buf->st_dev = (dev_t)disk->dev_id; + buf->st_nlink = ds.node->nlink; + buf->st_uid = ds.node->uid; + buf->st_gid = ds.node->gid; + buf->st_size = ds.node->size; + buf->st_atime = ds.node->ctime; + buf->st_mtime = ds.node->ctime; + buf->st_ctime = ds.node->ctime; + + return err; +} + +// ------------------------------------------------------------------------- +// romfs_getinfo() +// Getinfo. Currently only support pathconf(). + +static int romfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ) +{ + romfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (romfs_disk *)mte->data, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err != ENOERR ) return err; + + switch( key ) + { + case FS_INFO_CONF: + err = romfs_pathconf( ds.node, (struct cyg_pathconf_info *)buf ); + break; + + default: + err = EINVAL; + } + return err; +} + +// ------------------------------------------------------------------------- +// romfs_setinfo() +// Setinfo. Nothing to support here at present. + +static int romfs_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 + +// ------------------------------------------------------------------------- +// romfs_fo_read() +// Read data from the file. + +static int romfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + romfs_node *node = (romfs_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( (romfs_disk *)fp->f_mte->data, node, pos, &fbuf, &bsize ); + + 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 file offset and transfer residue. + + uio->uio_resid = resid; + fp->f_offset = pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_fo_lseek() +// Seek to a new file position. + +static int romfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *apos, int whence ) +{ + romfs_node *node = (romfs_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; +} + +// ------------------------------------------------------------------------- +// romfs_fo_ioctl() +// Handle ioctls. Currently none are defined. + +static int romfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data) +{ + // No Ioctls currenly defined. + + return EINVAL; +} + +// ------------------------------------------------------------------------- +// romfs_fo_fsync(). +// Force the file out to data storage. + +static int romfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ) +{ + // Data is always permanently where it belongs, nothing to do + // here. + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_fo_close() +// Close a file. We just clear out the data pointer. + +static int romfs_fo_close (struct CYG_FILE_TAG *fp) +{ + fp->f_data = 0; // zero data pointer + + return ENOERR; +} + +// ------------------------------------------------------------------------- +//romfs_fo_fstat() +// Get file status. + +static int romfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ) +{ + romfs_node *node = (romfs_node *)fp->f_data; + romfs_disk *disk = (romfs_disk*)(fp->f_mte->data); + + // Fill in the status + buf->st_mode = node->mode; + buf->st_ino = (ino_t)(node - &disk->node[0]); + buf->st_dev = disk->dev_id; + buf->st_nlink = node->nlink; + buf->st_uid = node->uid; + buf->st_gid = node->gid; + buf->st_size = node->size; + buf->st_atime = node->ctime; + buf->st_mtime = node->ctime; + buf->st_ctime = node->ctime; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_fo_getinfo() +// Get info. Currently only supports fpathconf(). + +static int romfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) +{ + romfs_node *node = (romfs_node *)fp->f_data; + int err; + + switch( key ) + { + case FS_INFO_CONF: + err = romfs_pathconf( node, (struct cyg_pathconf_info *)buf ); + break; + + default: + err = EINVAL; + } + return err; +} + +// ------------------------------------------------------------------------- +// romfs_fo_setinfo() +// Set info. Nothing supported here. + +static int romfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) +{ + // No setinfo key supported at present + + return ENOERR; +} + + +//========================================================================== +// Directory operations + +// ------------------------------------------------------------------------- +// romfs_fo_dirread() +// Read a single directory entry from a file. + +static int romfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + romfs_node *dir = (romfs_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; + romfs_dirent *d = NULL; + cyg_uint8 *buf; + size_t size; + int i; + + if( len < sizeof(struct dirent) ) + return EINVAL; + + // Get the next entry + err = findbuffer_node( (romfs_disk *)fp->f_mte->data, dir, pos, &buf, &size ); + if( err != ENOERR || size == 0 || pos >= dir->size ) + return err; + + d = (romfs_dirent *)buf; + + for ( i = 0 ; i < nlen && d->name[i] ; i++, nbuf++ ) + *nbuf = d->name[i]; + + // 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 = d->next; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_fo_dirlseek() +// Seek directory to start. + +static int romfs_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 romfs.c Index: rom/v2_0/support/file2c.tcl =================================================================== --- rom/v2_0/support/file2c.tcl (nonexistent) +++ rom/v2_0/support/file2c.tcl (revision 174) @@ -0,0 +1,124 @@ +#!/bin/bash +# restart using a Tcl shell \ + exec sh -c 'for tclshell in tclsh tclsh83 cygtclsh80 ; do \ + ( echo | $tclshell ) 2> /dev/null && exec $tclshell "`( cygpath -w \"$0\" ) 2> /dev/null || echo $0`" "$@" ; \ + done ; \ + echo "file2c.tcl: cannot find Tcl shell" ; exit 1' "$0" "$@" + +#=============================================================================== +# +# file2c.tcl +# +# Convert a file into a header that can be #included from C. +# +#=============================================================================== +#####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): jlarmour,bartv +# Contact(s): +# Date: 2001-07-20 +# Purpose: +# Description: +# Usage: file2c.tcl +# +#####DESCRIPTIONEND#### +#=============================================================================== + + + +if { $argc != 2 } { + puts "Usage: file2c.tcl " + exit 1 +} +set infile [lindex $argv 0] +set outfile [lindex $argv 1] + +set status [ catch { + set infilefd [open $infile "r"] + fconfigure $infilefd -translation binary + set data [read $infilefd] + close $infilefd +} message] + +if { $status != 0 } { + error "Unable to read file $infile: $message" +} + +set result "" + +set status [ catch { + set outfilefd [ open $outfile "w" ] +} message ] + +if { $status != 0 } { + error "Unable to create file $outfile: $message" +} + +append result "/* This is a generated file. Do not edit. */\n\n" +append result "static const unsigned char filedata\[\] = {\n" + +set datalength [ string length $data ] + +set aligned_datalength [expr $datalength - ($datalength % 8)] + +for { set i 0 } {$i < $aligned_datalength} {incr i 8} { + binary scan $data "@[set i]H16" var0 + append result [format " 0x%2s, 0x%2s, 0x%2s, 0x%2s, 0x%2s, 0x%2s, 0x%2s, 0x%2s,\n" \ + [string range $var0 0 1] \ + [string range $var0 2 3] \ + [string range $var0 4 5] \ + [string range $var0 6 7] \ + [string range $var0 8 9] \ + [string range $var0 10 11] \ + [string range $var0 12 13] \ + [string range $var0 14 15]] +} + +if { $aligned_datalength != $datalength } { + append result " " + for { set i $aligned_datalength } {$i < $datalength} {incr i} { + binary scan $data "@[set i]H2" var0 + append result [format "0x%2s, " $var0] + } +} + +# Remove either comma+newline or comma+space from the end +set result [string range $result 0 [expr [string length $result] - 3]] + +append result "\n};" + +puts $outfilefd $result +close $outfilefd Index: rom/v2_0/support/Makefile =================================================================== --- rom/v2_0/support/Makefile (nonexistent) +++ rom/v2_0/support/Makefile (revision 174) @@ -0,0 +1,39 @@ +#========================================================================== +#####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#### +#========================================================================== + +CC = gcc +CFLAGS = -ggdb -Wall + +mk_romfs: Index: rom/v2_0/support/mk_romfs.c =================================================================== --- rom/v2_0/support/mk_romfs.c (nonexistent) +++ rom/v2_0/support/mk_romfs.c (revision 174) @@ -0,0 +1,734 @@ +//========================================================================== +// +// mk_romfs.c +// +// Create ROM file system image +// +//========================================================================== +//####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): richard.panton@3glab.com +// Contributors: richard.panton@3glab.com +// Date: 2000-07-25 +// Purpose: ROM file system +// Description: This program creates a ROM file system image, suitable +// for use with the sample ROM file system implemented by +// this package. +// * CAUTION! * This is host code and can only be built +// in a host, e.g. Linux, environment. +// +//####DESCRIPTIONEND#### +//========================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//========================================================================== +// +// CONFIGURABLE ITEMS HERE +// +//========================================================================== + +// define LONG to be a four byte unsigned integer on the host +#define LONG unsigned long + +// define SHORT to be a two byte unsigned integer on the host +#define SHORT unsigned short + +// All data files should be aligned to this sized boundary (minimum probably 32) +#define DATA_ALIGN 32 + +// The data stored in a directory should be aligned to this size boundary +#define DIRECTORY_ALIGN 32 + +// All executable files should be aligned to this sized boundary (minimum probably 32) +#define EXEC_ALIGN 32 + +// Undefine this if the host filesystem does not support lstat() +#define HAS_LSTAT + +//========================================================================== + +// Return (n) aligned to the next (b) byte boundary +#define ALIGN_TO( n, b ) (( (n) + (b)-1 ) & ~((b)-1)) + +// Set the stat call to use +#ifdef HAS_LSTAT +#define get_status( p, b ) lstat( (p), (b) ) +#else +#define get_status( p, b ) stat( (p), (b) ) +#endif + +// This is how we identify a directory from its mode +#define IS_DIRECTORY( m ) (S_ISDIR(m)) + +// This is how we identify a data file from its mode +#define IS_DATAFILE( m ) (S_ISREG(m) && ((m)&S_IXUSR) == 0 ) + +// This is how we identify an executable from its mode +#define IS_EXECUTABLE( m ) (S_ISREG(m) && ((m)&S_IXUSR) != 0 ) + +// This is how we identify a symbolic link from its mode +#define IS_SYMLINK( m ) (S_ISLNK(m)) + +#define ROMFS_MAGIC 0x526f6d2e + +//========================================================================= +// EXIT CODES +#define EXIT_SUCCESS 0 +#define EXIT_ARGS 1 +#define EXIT_MALLOC 2 +#define EXIT_FILESYS 3 +#define EXIT_WRITE 4 +#define EXIT_SEEK 5 +#define EXIT_COMPILE 6 +#define EXIT_BUG 7 + + + +// These are the structures we will build into the ROMFS image. +// The sizes of these structures should be fixed for all architectures +typedef struct romfs_dirent { + LONG node; // 4 + LONG next; // 8 + char name[0]; // 8 + strlen(name) + 1 +} romfs_dirent; // Aligns to next 32 byte boundary + +typedef struct romfs_node { + LONG mode; // 4 + LONG nlink; // 8 + SHORT uid; // 10 + SHORT gid; // 12 + LONG size; // 16 + LONG ctime; // 20 + LONG data_offset; // 24 + char pad[8]; // 32 +} romfs_node; // Next node begins here + +typedef struct romfs_disk { + LONG magic; // 4 + LONG nodecount; // 8 + LONG disksize; // 12 + LONG dev_id; // 16 + char name[16]; // 32 +} romfs_disk; // Nodes start here + +// This is the holding structure for a node +typedef struct node { + const char *path; // Filename (inc. path) of a link to this node + size_t size; // Size of file/directory/link + mode_t st_mode; // Type and permissions + uid_t uid; // Owner id + gid_t gid; // Group id + time_t ctime; // File creation time + int nodenum; // Nodenumber of this node in the ROMFS image + dev_t device; // Device (for hardlink check) + ino_t inode; // Inode (for hardlink check) + int nlink; // [DIRECTORIES] Number of sub-directories [FILES] hard links + romfs_dirent *entry; // [DIRECTORIES] Points to an array of directory entries + int entry_size; // Size to be allocated to file (includes alignment bytes) + unsigned long offset; // Offset within ROMFS image of data + struct node *sibling; // Points to the next entry in this directory + struct node *child; // [DIRECTORIES] Points to any subdirectories + struct node *next_in_rom; // Next in ROMFS write order + struct node *next_multilink;// Next node that is multilinked +} node; + +static int nodes = 0; +static char *prog; +static int verbose = 1; +static int dowrite = 1; +static int bigendian = 0; +static int hardlinks = 0; +static unsigned long coffset = 0; +static node * first = NULL; +static node ** last_p = &first; +static node * first_multilink = NULL; +static node ** last_multilink_p = &first_multilink; +static int fd = -1; + +#define VERB_NONE 0 +#define VERB_MINIMUM 1 +#define VERB_SUB 2 +#define VERB_MAX 3 +#define VERB_EXCESSIVE 4 + +// Use gcc format argument checking on this function, which cannot return +static void fatal_error( int exitcode, const char *fmt, ... ) \ + __attribute__ (( noreturn,format (printf, 2, 3) )); + +// Use gcc format argument checking on this function +static void verb_printf( int level, const char *fmt, ... ) \ + __attribute__ ((format (printf, 2, 3) )); + +static void fatal_error( int exitcode, const char *fmt, ... ) { + va_list v; + + va_start( v, fmt ); + vfprintf( stderr, fmt, v ); + + exit(exitcode); +} + +static void verb_printf( int level, const char *fmt, ... ){ + if ( level <= verbose ) { + va_list v; + va_start( v,fmt ); + vprintf(fmt, v); + } +} + +static void *mymalloc( size_t size ) { + void *p = malloc(size); + if ( !p ) { + fatal_error( EXIT_MALLOC, "Out of memory allocating %d bytes\n", size ); + } + return p; +} + +static void myrealloc( void **o, size_t newsize ) { + if ( *o == NULL ) + *o = mymalloc( newsize ); + else if ( !(*o = realloc( *o, newsize )) ) { + fatal_error( EXIT_MALLOC, "Out of memory re-allocating %d bytes\n", newsize ); + } +} + +static void outputlong( unsigned char *b, unsigned long w ) { + if ( bigendian ) { + b[0] = (w>>24) & 0xff; + b[1] = (w>>16) & 0xff; + b[2] = (w>> 8) & 0xff; + b[3] = (w ) & 0xff; + } else { + b[3] = (w>>24) & 0xff; + b[2] = (w>>16) & 0xff; + b[1] = (w>> 8) & 0xff; + b[0] = (w ) & 0xff; + } +} + +static void outputshort( unsigned char *b, unsigned short w ) { + if ( bigendian ) { + b[0] = (w>> 8) & 0xff; + b[1] = (w ) & 0xff; + } else { + b[1] = (w>> 8) & 0xff; + b[0] = (w ) & 0xff; + } +} + +static unsigned long ConvertMode( unsigned long posix_mode ) { + unsigned long result = 0; + if ( S_ISDIR( posix_mode ) ) result |= 1<<0; + if ( S_ISCHR( posix_mode ) ) result |= 1<<1; + if ( S_ISBLK( posix_mode ) ) result |= 1<<2; + if ( S_ISREG( posix_mode ) ) result |= 1<<3; + if ( S_ISFIFO(posix_mode ) ) result |= 1<<4; + // We cannot create MQ, SEM, or SHM entries here + if ( posix_mode & S_IRUSR ) result |= 1<<8; + if ( posix_mode & S_IWUSR ) result |= 1<<9; + if ( posix_mode & S_IXUSR ) result |= 1<<10; + if ( posix_mode & S_IRGRP ) result |= 1<<11; + if ( posix_mode & S_IWGRP ) result |= 1<<12; + if ( posix_mode & S_IXGRP ) result |= 1<<13; + if ( posix_mode & S_IROTH ) result |= 1<<14; + if ( posix_mode & S_IWOTH ) result |= 1<<15; + if ( posix_mode & S_IXOTH ) result |= 1<<16; + if ( posix_mode & S_ISUID ) result |= 1<<17; + if ( posix_mode & S_ISGID ) result |= 1<<18; + return result; +} + +static const char *AddDirEntry( const char *name, node *parent_node, int node_num ) { + int this_size = ((strlen(name) + 4 + 4 + 1) + 31) & ~31; + int start = parent_node->size; + romfs_dirent *g; + myrealloc( (void**)&parent_node->entry, (parent_node->size += this_size) ); + g = (romfs_dirent *)((unsigned char *)parent_node->entry + start); + memset( (void*)g, '\0', this_size ); + outputlong( (char*)&g->node, node_num); + outputlong( (char*)&g->next, parent_node->size); + strcpy(g->name,name); + verb_printf( VERB_MAX, "\t%s --> node %d\n", name, node_num ); + return (const char *)g->name; +} + +extern int errno; + +static node * FindLink( dev_t d, ino_t i ) { + // See if the node has been previously included by checking the device/inode + // combinations of all known multi-linked nodes + node *np = first_multilink; + + for ( ; np ; np = np->next_multilink ) { + if ( np->device == d && np->inode == i ) + return np; + } + return NULL; +} + +static node * GetNodeInfo( const char *path, const char *name, int *hlink ) { + char newpath[1024]; + node *node, *lnode; + struct stat stbuff; + + sprintf(newpath,"%s/%s",path,name); + if ( (get_status(newpath,&stbuff)) < 0 ) { + fatal_error(EXIT_FILESYS, "stat(%s) failed: %s\n", newpath, strerror(errno)); + } + if ( !(stbuff.st_mode & S_IRUSR) ) { + fatal_error(EXIT_FILESYS, "\"%s\" is not readable\n", newpath ); + } + if ( hardlinks && S_ISREG( stbuff.st_mode ) && stbuff.st_nlink > 1 ) { + + // See if this node has already been loaded + lnode = FindLink( stbuff.st_dev, stbuff.st_ino ); + + if ( lnode ) { + lnode->nlink++; + *hlink = 1; + return lnode; // Return the found link instead + } + + // Create a new node + node = mymalloc( sizeof(struct node) ); + + // Incorporate the new link into the 'multi-linked' node list + *last_multilink_p = node; + last_multilink_p = &node->next_multilink; + } else { + // Create a new node + node = mymalloc( sizeof(struct node) ); + } + node->path = strdup( newpath ); + // We re-calculate the size for directories + node->size = IS_DIRECTORY( stbuff.st_mode ) ? 0 : stbuff.st_size; + node->st_mode = stbuff.st_mode; + node->uid = stbuff.st_uid; + node->gid = stbuff.st_gid; + node->ctime = stbuff.st_ctime; + node->nodenum = nodes++; + node->device = stbuff.st_dev; + node->inode = stbuff.st_ino; + // We always re-calculate the number of links + node->nlink = IS_DIRECTORY( stbuff.st_mode ) ? 2 : 1; + node->entry = NULL; + node->entry_size = 0; + node->offset = 0; + node->sibling = NULL; + node->child = NULL; + node->next_in_rom = NULL; + node->next_multilink = NULL; + *hlink = 0; + return node; +} + +static void ScanDirectory(node *mynode, int p_node) { + + DIR *dh; + struct dirent *e; + node **last_p = &mynode->child; + node *th; + int was_hardlinked; + + if ( (dh = opendir( mynode->path )) == NULL ) { + perror(mynode->path); + return; + } + + verb_printf(VERB_EXCESSIVE, "Construct directory '%s'(%d):\n", + mynode->path, mynode->nodenum ); + + // Add . & .. here because they MUST be present in the image + AddDirEntry( ".", mynode, mynode->nodenum ); + AddDirEntry( "..", mynode, p_node ); + + while ( (e = readdir( dh )) ) { + // Ignore . & .. here because they MAY NOT be in the host filesystem + if ( strcmp(e->d_name,".") && strcmp(e->d_name,"..") ) { + + + th = GetNodeInfo( mynode->path, e->d_name, &was_hardlinked ); + AddDirEntry( e->d_name, mynode, th->nodenum ); + + if ( !was_hardlinked ) { + verb_printf( VERB_EXCESSIVE, "\t\tNew node %d for entry '%s'\n", th->nodenum, e->d_name); + *last_p = th; + last_p = &th->sibling; + } else { + verb_printf( VERB_EXCESSIVE, "\t\tRe-used node %d for entry '%s'\n", th->nodenum, e->d_name); + } + } + } + closedir( dh ); + verb_printf(VERB_EXCESSIVE,"Completed '%s'. Checking for child directories...\n", mynode->path); + + for ( th = mynode->child ; th ; th = th->sibling ) { + if ( IS_DIRECTORY( th->st_mode ) ) { + mynode->nlink++; + ScanDirectory( th, mynode->nodenum ); + } + } +} + +static void AllocateSpaceToDirectories( node *first ) { + node *np; + + for ( np = first ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) ) { + // The first node is a directory. Add its data + np->offset = coffset; + np->entry_size = ALIGN_TO( np->size, DIRECTORY_ALIGN ); + coffset += np->entry_size; + + verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n", + np->nodenum, np->offset, np->entry_size ); + + // Link this node into the write order chain. + // For node 0 (the root), this will overwrite the first pointer with itself + *last_p = np; + last_p = &np->next_in_rom; + } + } + + // Now add any child directories + for ( np = first ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) && np->child ) + AllocateSpaceToDirectories( np->child ); + } +} + +static void AllocateSpaceToDataFiles( node *first ) { + node *np; + + // There are two loops below. It CAN be done in just one, but this re-orders + // the file positions in relation to their inode numbers. To keep it simple + // to check, allocation takes place in the first loop, recursion in the second + + // Search for child data files + for ( np = first->child ; np ; np = np->sibling ) { + if ( IS_DATAFILE( np->st_mode ) || IS_SYMLINK( np->st_mode ) ) { + np->offset = coffset; + np->entry_size = ALIGN_TO( np->size, DATA_ALIGN ); + coffset += np->entry_size; + + // Link in to the rom write order list + *last_p = np; + last_p = &np->next_in_rom; + + verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n", + np->nodenum, np->offset, np->entry_size ); + } + } + + // Recurse into sub-directories + for ( np = first->child ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) ) { + AllocateSpaceToDataFiles( np ); + } + } +} + +static void AllocateSpaceToExecutables( node *first ) { + node *np; + + // The first node is a directory. Don't bother with that... + + // Search for child executables + for ( np = first->child ; np ; np = np->sibling ) { + if ( IS_EXECUTABLE( np->st_mode ) ) { + np->offset = coffset; + np->entry_size = ALIGN_TO( np->size, EXEC_ALIGN ); + coffset += np->entry_size; + + // Link in to the rom write order list + *last_p = np; + last_p = &np->next_in_rom; + + verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n", + np->nodenum, np->offset, np->entry_size ); + } + } + + // Recurse into sub-directories + for ( np = first->child ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) ) { + AllocateSpaceToExecutables( np ); + } + } +} + +static void WriteNode( int fd, node *np ) { + romfs_node anode; + char padhere[9]; + outputlong( (char*) &anode.mode, ConvertMode( np->st_mode ) ); + outputlong( (char*) &anode.nlink, np->nlink ); + outputshort((char*) &anode.uid, np->uid ); + outputshort((char*) &anode.gid, np->gid ); + outputlong( (char*) &anode.size, np->size ); + outputlong( (char*) &anode.ctime, np->ctime ); + outputlong( (char*) &anode.data_offset, np->offset ); + sprintf( padhere, "<%6d>", np->nodenum ); + memcpy( anode.pad, padhere, 8 ); + if ( dowrite && write( fd, (void*)&anode, sizeof(anode) ) != sizeof(anode) ) + fatal_error(EXIT_WRITE, "Error writing node %d (%s): %s\n", np->nodenum, np->path, strerror(errno) ); +} + +static int WriteNodeAndSiblings( int fd, int nodenum, node *first ) { + node *np; + + for ( np = first ; np ; np = np->sibling ) { + if ( np->nodenum != nodenum++ ) { + fatal_error(EXIT_BUG, "BUG: Out of sequence node number; got %d, expected %d\n", np->nodenum, nodenum-1); + } + WriteNode( fd, np ); + } + + for ( np = first ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) && np->child ) { + nodenum = WriteNodeAndSiblings( fd, nodenum, np->child ); + } + } + return nodenum; +} + +static void WriteNodeTable( int fd ) { + romfs_disk header; + int wnodes; + + outputlong( (char*) &header.magic, ROMFS_MAGIC ); + outputlong( (char*) &header.nodecount, nodes ); + outputlong( (char*) &header.disksize, coffset ); + outputlong( (char*) &header.dev_id, 0x01020304 ); + strcpy( header.name, "ROMFS v1.0" ); + if ( dowrite && write( fd, (void*)&header, sizeof(header) ) != sizeof(header) ) + fatal_error(EXIT_WRITE, "Error writing ROMFS header: %s\n", strerror(errno) ); + + if ( (wnodes = WriteNodeAndSiblings( fd, 0, first )) != nodes ) { + fatal_error(EXIT_BUG, "BUG: Lost/gained some nodes; wrote %d, expected %d\n", wnodes, nodes ); + } +} + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static void WriteData( int fd, node *np ) { + char newpath[1024]; + int ffd; + unsigned long todo; + + if ( IS_SYMLINK( np->st_mode ) ) { + if ( (ffd = readlink( np->path, newpath, sizeof(newpath) )) < 0 ) + fatal_error(EXIT_FILESYS, "Error reading symlink \"%s\": %s\n", np->path, strerror(errno) ); + + if ( !dowrite ) return; + + if ( lseek( fd, np->offset, SEEK_SET ) != np->offset ) + fatal_error(EXIT_SEEK, "Error seeking to offset 0x%lX: %s\n", np->offset, strerror(errno) ); + + if ( write( fd, newpath, ffd ) != ffd ) + fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) ); + + return; + } + + if ( (ffd=open(np->path, O_RDONLY | O_BINARY )) < 0 ) + fatal_error(EXIT_FILESYS, "Error opening \"%s\": %s\n", np->path, strerror(errno) ); + + if ( dowrite && lseek( fd, np->offset, SEEK_SET ) != np->offset ) + fatal_error(EXIT_SEEK, "Error seeking to offset 0x%lX: %s\n", np->offset, strerror(errno) ); + + todo = np->size; + while ( todo >= 1024 ) { + if ( read( ffd, newpath, 1024 ) != 1024 ) + fatal_error(EXIT_FILESYS, "Error reading file \"%s\" at offset 0x%lX: %s\n", np->path, np->size - todo, strerror(errno) ); + if ( dowrite && write( fd, newpath, 1024 ) != 1024 ) + fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) ); + todo -= 1024; + } + + if ( todo ) { + if ( read( ffd, newpath, todo ) != todo ) + fatal_error(EXIT_FILESYS, "Error reading file \"%s\" at offset 0x%lX: %s\n", np->path, np->size - todo, strerror(errno) ); + if ( dowrite && write( fd, newpath, todo ) != todo ) + fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) ); + } + + close(ffd); + +} + +static void WriteDataBlocks( int fd, node *first ) { + for ( ; first ; first = first->next_in_rom ) { + if ( dowrite && lseek( fd, first->offset, SEEK_SET ) != first->offset ) + fatal_error(EXIT_SEEK, "Error seeking to offset 0x%lX: %s\n", first->offset, strerror(errno) ); + if ( IS_DIRECTORY( first->st_mode ) ) { + if ( dowrite && write( fd, first->entry, first->size ) != first->size ) + fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) ); + } else { + WriteData( fd, first ); + } + } +} + +static void usage(void) { + fprintf(stderr,"\n%s - Create an eCos ROMFS disk image from the files\n",prog); + fprintf(stderr,"%*s contained under a specified directory\n\n", strlen(prog), ""); + fprintf(stderr,"Usage: %s [options] \n", prog); + fprintf(stderr," fs_root is the directory containing the files to package into the ROMFS image\n"); + fprintf(stderr," fs_file is the name of the ROMFS image file to create\n"); + fprintf(stderr," Options include:\n"); + fprintf(stderr," -v / -q increase / decrease verbosity\n"); + fprintf(stderr," -n do everything EXCEPT creating the output file\n"); + fprintf(stderr," -b write a big-endian image (default is little endian)\n"); + fprintf(stderr," -l collapse hard links to a single node\n"); + fprintf(stderr,"\n"); + exit(EXIT_ARGS); +} + +int main(int ac, char *av[]) { + int dummy; + + prog = av[0]; + + // Check structure sizes + if (sizeof(romfs_node) != 32) { + fatal_error(EXIT_COMPILE , "Size of romfs_node is %d, NOT 32\n", sizeof(romfs_node) ); + } else if (sizeof(romfs_dirent) != 8) { + fatal_error(EXIT_COMPILE , "Size of romfs_dirent is %d, NOT 8\n", sizeof(romfs_dirent) ); + } else if (sizeof(romfs_disk) != 32) { + fatal_error(EXIT_COMPILE , "Size of romfs_disk is %d, NOT 32\n", sizeof(romfs_disk) ); + } + + // Parse option arguments + while ( ac > 1 && av[1][0] == '-' ) { + char *o = &av[1][1]; + for ( ; *o ; o++ ) { + switch ( *o ) { + case 'q' : + verbose--; + break; + case 'v' : + verbose++; + break; + case 'n' : + dowrite = 0; + break; + case 'b' : + bigendian = 1; + break; + case 'l' : + hardlinks = 1; + break; + default : + fprintf(stderr,"%s: Invalid flag -%c\n", prog, *o ); + usage(); + } + } + av++; ac--; + } + + // Check remaining arguments + if ( ac != 3 ) usage(); + + + verb_printf( VERB_MINIMUM, "%s: Verbosity %d %s%s endian\n", + prog, verbose, + dowrite ? "" : "no write, ", + bigendian ? "big" : "little" ); + + // Phase 1. Recursively scan the root directory for files and directories. + verb_printf(VERB_MINIMUM, "Phase 1 - Build file list\n"); + + first = GetNodeInfo( av[1], ".", &dummy ); // Initialize the root node entry. + ScanDirectory( first, 0 ); + + // Phase 2. Work out space allocations for filesystem + verb_printf(VERB_MINIMUM, "Phase 2 - Calculate space allocation\n"); + coffset = sizeof(romfs_disk) + nodes * sizeof(romfs_node); + verb_printf(VERB_MAX,"\t\tnode table : 0x000000 (+0x%05lX) %d nodes\n", coffset, nodes ); + + // Phase 2a. Work out space allocations for the directories of the filesystem + verb_printf(VERB_SUB,"Phase 2a - * Directories\n"); + coffset = ALIGN_TO( coffset, DIRECTORY_ALIGN ); + AllocateSpaceToDirectories( first ); + + // Phase 2b. Work out space allocations for the data files of the filesystem + verb_printf(VERB_SUB,"Phase 2b - * Regular files\n"); + coffset = ALIGN_TO( coffset, DATA_ALIGN ); + AllocateSpaceToDataFiles( first ); + + // Phase 2c. Work out space allocations for the executable files of the filesystem + verb_printf(VERB_SUB,"Phase 2c - * Executable files\n"); + coffset = ALIGN_TO( coffset, EXEC_ALIGN ); + AllocateSpaceToExecutables( first ); + + // Round off the image size... + coffset = ALIGN_TO( coffset, EXEC_ALIGN ); + + // Phase 3. Write out the image file + verb_printf(VERB_MINIMUM, "Phase 3 - Construct ROMFS image file (%ld kb)\n", ALIGN_TO( coffset, 1024 )/1024); + + if ( dowrite ) { + if ( (fd = open( av[2], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666 )) < 0 ) { + fatal_error(EXIT_WRITE,"Failed to open output file '%s', errno=%d\n", av[2], errno ); + } + } else { + verb_printf(VERB_NONE," (No image is being written)\n"); + } + + verb_printf(VERB_SUB,"Phase 3a - * Node table\n"); + WriteNodeTable( fd ); + + verb_printf(VERB_SUB,"Phase 3b - * Data blocks\n"); + WriteDataBlocks( fd, first ); + + if ( fd >= 0 ) close(fd); + + verb_printf(VERB_MINIMUM, "%s completed\n", av[2] ); + + return 0; +}

powered by: WebSVN 2.1.0

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