URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/ecos-2.0/packages/fs/jffs2/v2_0
- from Rev 1254 to Rev 1765
- ↔ Reverse comparison
Rev 1254 → Rev 1765
/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 |
|
|
|
|
|
/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 |
/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 |
/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__ */ |
/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 */ |
/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 */ |
/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 |
/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#### |
//=========================================================================== |
|
|
/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); |
} |
|
/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); |
} |
|
/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")); |
} |
/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; |
} |
/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); |
} |
/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; |
} |
|
/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; |
} |
/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 */ |
/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; |
} |
/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__ */ |
/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); |
} |
/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; |
} |
} |
} |
} |
|
|
/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; |
} |
/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); |
} |
/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 $ |
/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__ */ |
/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 */ |
/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]; |
}; |
/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__ */ |
|
|
|
|
|
|
|
|
|
|
/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")); |
} |
} |
/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__ */ |
/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; |
} |
|
/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); |
} |
/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; |
} |
/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 */ |
/support/jffs2/jffs2.img
0,0 → 1,12
+B5WAazF]Nrxnк^ٰQI\׳
+om2=R6
+eZXG"n`ǎ CbJЩ⧺CKR\G
+z*(3ZVFh4jDgXm/ΰDO[o=V2M9E]#uTRߎ3l't1{*U*EUIQ$.u- ~2ӆQ̕0t(h82˰O8TX`o+Ͳ>9z*.rx<
+ PC g
\ No newline at end of file
/support/jffs2/etc/passwd
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 |
/support/jffs2/etc/group
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: |