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/jffs2
    from Rev 27 to Rev 174
    Reverse comparison

Rev 27 → Rev 174

/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
 
 
 
 
 
/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
/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
/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__ */
/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 */
/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 */
/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
/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####
//===========================================================================
 
/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);
}
 
/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;
}
/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);
}
 
/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"));
}
/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);
}
/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;
}
 
/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;
}
/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 */
/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;
}
/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__ */
/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);
}
/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;
}
/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;
}
}
}
}
 
 
/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__ */
/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);
}
/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 $
/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 */
/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__ */
 
 
 
 
 
 
 
 
 
 
/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];
};
/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"));
}
}
/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__ */
/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;
}
 
/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);
}
/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;
}
/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 */
/v2_0/support/jffs2/jffs2.img Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
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: v2_0/support/jffs2/etc/passwd =================================================================== --- v2_0/support/jffs2/etc/passwd (nonexistent) +++ 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: v2_0/support/jffs2/etc/group =================================================================== --- v2_0/support/jffs2/etc/group (nonexistent) +++ 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:

powered by: WebSVN 2.1.0

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