diff options
author | Michael Gielda <mgielda@antmicro.com> | 2014-04-03 14:53:04 +0200 |
---|---|---|
committer | Michael Gielda <mgielda@antmicro.com> | 2014-04-03 14:53:04 +0200 |
commit | ae1e4e08a1005a0c487f03ba189d7536e7fdcba6 (patch) | |
tree | f1c296f8a966a9a39876b0e98e16d9c5da1776dd /ecos/packages/fs | |
parent | f157da5337118d3c5cd464266796de4262ac9dbd (diff) |
Added the OS files
Diffstat (limited to 'ecos/packages/fs')
76 files changed, 28813 insertions, 0 deletions
diff --git a/ecos/packages/fs/fat/current/ChangeLog b/ecos/packages/fs/fat/current/ChangeLog new file mode 100644 index 0000000..5a7507d --- /dev/null +++ b/ecos/packages/fs/fat/current/ChangeLog @@ -0,0 +1,230 @@ +2009-04-28 John Dallaway <john@dallaway.org.uk> + + cdl/fatfs.cdl: Use CYGPKG_IO_FILEIO as the parent. + +2008-08-19 Andrew Lunn <andrew.lunn@ascom.ch> + + * cdl/fatfs.cdl: Add configury for the disk device names to be + used for the tests. Default to the synthetic tests, since what + seems to be the most used test platform. + * doc/README.txt: Updates for the naming scheme. + * doc/fat16.ecm: Fit the new naming scheme. + +2008-05-13 Savin Zlobec <savinz@users.sourceforge.net> + + * src/fatfs.c: Fix handling of '.' and '..' nodes in dirsearch. + +2008-04-02 Xinghua Yang <yxinghua@sunnorth.com.cn> + Andrew Lunn <andrew.lunn@ascom.ch> + Taiyun Wang <taiyun@sunnorth.com.cn> + + * cdl/fatfs.cdl: Use CYGPKG_FS_FAT_RET_DIRENT_DTYPE to control + whether fatfs sets file type in fatfs_fo_dirread. + * src/fatfs.c: Set file type in fatfs_fo_dirread + * test/fatfs1.c: Test the new d_type in dirent when present. + +2007-07-31 Hajime Ishitani <pigmon@mail.snd.co.jp> + + * src/fatfs.c: When removing a directory, first remove . and + .. nodes. + +2007-02-05 Ya-Chau Yang <a8850607@stmail.fju.edu.tw> + Savin Zlobec <savinz@users.sourceforge.net> + + * src/fatfs_supp.c: Fixed FAT32 cluster handling in + fatfs_delete_file and fatfs_rename_file. + +2007-01-27 Andrew Lunn <andrew.lunn@ascom.ch> + + * tests/fatfs1.c: Fixed the format string to diag_printf() + to remove compiler warnings. + +2006-08-04 Paul Fine <pfine@dtccom.com> + Andrew Lunn <andrew.lunn@ascom.ch> + + * src/fats.c: Added functionality to the fatfs_getinfo() function + to return disk usage information about the filesystem, making this + information accessible through the cyg_fs_getinfo() interface. + * tests/fatfs1.c: Added code to test the disk usage. + +2005-07-30 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/fatfs_supp.c: Correct types to remove compiler warnings. + +2005-06-07 Savin Zlobec <savin@elatec.si> + + * src/fatfs_supp.c: lseek returned EEOF when at the end of the + file rather than the current position. + +2005-03-27 Andrew Lunn <andrew.lunn@ascom.ch> + + * tests/fatfs1.c: Fixed various compiler warnings about types passed to + diag_printf(). + +2005-01-18 Savin Zlobec <savin@elatec.si> + + * src/fatfs_supp.c: + Fixed FAT12/16 root dir size checking in read_next_raw_dentry - + reported by Stephane Royo. + +2004-12-13 John Dallaway <jld@ecoscentric.com> + + * tests/fileio1.c: Rename to fatfs1.c. eCos test names should be + unique. + * cdl/fatfs.cdl: Build the renamed test. + +2004-11-14 Jani Monoses <jani@iv.ro> + + * test/fileio1.c: Start the test using main, not via a + thread. This makes is more uniform with the other fileio1 tests. + +2004-11-11 David Brennan <eCos@brennanhome.com> + + * src/fatfs.c: fixed typos in TRACE statements + +2004-10-23 David Brennan <eCos@brennanhome.com> + + * tests/fileio1.c: fixed check for return value for attribute + tests. + +2004-10-17 David Brennan <eCos@brennanhome.com> + + * src/fatfs.c: + * src/fatfs_supp.c: + * include/fatfs.h (NEW): + * tests/fileio1.c: + * cdl/fatfs.cdl: Added configurable support for FAT filesystem + attributes. + +2004-10-13 David Brennan <eCos@brennanhome.com> + + * src/fatfs.c: Added code to setinfo to allow performing a + file-system sync + +2004-10-06 David Brennan <eCos@brennanhome.com> + + * tests/fileio1.c: Added include of <stdio.h> to fix compiler + warning for rename(). + +2004-08-10 Savin Zlobec <savin@elatec.si> + + * src/fatfs.h: + * src/fatfs_supp.c: + Added FAT32 support. + +2004-07-13 Savin Zlobec <savin@elatec.si> + + * cdl/fatfs.cdl: + * src/fatfs.h: + * src/fatfs.c: + * src/fatfs_supp.c: + * src/fatfs_ncache.c: + Refactored the code and changed file node cache memory + allocation from malloc to custom pool based one. + +2004-07-05 Savin Zlobec <savin@elatec.si> + + * cdl/fatfs.cdl: + * src/fatfs.h: + * src/fatfs.c: + * src/fatfs_supp.c: + * src/fatfs_ncache.c: + Removed FAT table cache - it added little or no speed gain to + the fatfs. Implemented private data for fatfs file descriptors which + holds the current FAT cluster position, this greatly improves + read/write times for big files comparing to the old implementation. + * src/fatfs_tcache.c: Removed. + +2004-06-24 Savin Zlobec <savin@elatec.si> + + * src/fatfs.h: + * src/fatfs_supp.c: + Implemented fatfs_get_disk_usage function for + getting the number of total and free clusters. + +2004-01-19 Nick Garnett <nickg@calivar.com> + + + * doc/README.txt: + * doc/fatfs.ecm: + * doc/fat16.ecm: + * doc/mkdisk0: + Added some initial basic documentation, sample configurations and + shell script to manufacture test disk image. + +2003-10-18 Savin Zlobec <savin@elatec.si> + + * cdl/fatfs.cdl: + * src/fatfs.h: + * src/fatfs_ncache.c: + Fixed node allocation tresholds (there should be no + more unnecessary node memory allocation/deallocation). + Added more sanity checks and a cdl option to enable them. + +2003-10-17 Savin Zlobec <savin@elatec.si> + + * src/fatfs_supp.c: Fixed dos to unix date conversion. + +2003-10-16 Savin Zlobec <savin@elatec.si> + + * src/fatfs.c: Fixed dir entry searching. + * src/fatfs_ncache.c: Added more sanity checks. + +2003-10-13 Savin Zlobec <savin@elatec.si> + + * src/fatfs.h: + * src/fatfs.c: + * src/fatfs_supp.c: + Added FAT12 support and FAT type detection. + Fixed file deletion. + Fixed reusing of deleted directory entries. + Fixed free clusters search. + Fixed directory renaming. + Fixed out of space situation while writting. + +2003-09-01 Savin Zlobec <savin@elatec.si> + + * cdl/fatfs.cdl: + * src/fatfs.h: + * src/fatfs.c: + * src/fatfs_supp.c: + * src/fatfs_tcache.c: + * src/fatfs_ncache.c: + Implemented block IO with BLib, fixed FAT data access + problems and added FAT table cache memory constraints. + +2003-07-07 Savin Zlobec <savin@elatec.si> + + * cdl/fatfs.cdl: + * src/fatfs.h: + * src/fatfs.c: + * src/fatfs_supp.c: + * src/fatfs_ncache.c: + * src/fatfs_tcache.c: + A FAT16 filesystem implementation + + + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/ecos/packages/fs/fat/current/cdl/fatfs.cdl b/ecos/packages/fs/fat/current/cdl/fatfs.cdl new file mode 100644 index 0000000..a222451 --- /dev/null +++ b/ecos/packages/fs/fat/current/cdl/fatfs.cdl @@ -0,0 +1,159 @@ +# ==================================================================== +# +# fatfs.cdl +# +# FAT Filesystem configuration data +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2009 Free Software Foundation, 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## This exception does not invalidate any other reasons why a work based +## on this file might be covered by the GNU General Public License. +## ------------------------------------------- +## ####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): Savin Zlobec <savin@elatec.si> +# Contributors: +# Date: 2003-06-25 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_FS_FAT { + display "FAT filesystem" + include_dir cyg/fs + + parent CYGPKG_IO_FILEIO + requires CYGPKG_IO_FILEIO + + requires CYGPKG_ISOINFRA + requires CYGINT_ISO_ERRNO + requires CYGINT_ISO_ERRNO_CODES + requires CYGPKG_MEMALLOC + requires CYGPKG_BLOCK_LIB + + implements CYGINT_IO_FILEIO_FS + + compile -library=libextras.a fatfs.c \ + fatfs_supp.c \ + fatfs_ncache.c + + cdl_option CYGNUM_FS_FAT_NODE_HASH_TABLE_SIZE { + display "Node hash table size" + flavor data + default_value 32 + legal_values 1 to 9999999999 + description "This option controls the number of slots in the + hash table used to store file nodes using filenames + as keys." + } + + cdl_option CYGNUM_FS_FAT_NODE_POOL_SIZE { + display "Node pool size" + flavor data + default_value { (CYGNUM_FILEIO_NFILE + 2) } + legal_values 1 to 9999999999 + requires { CYGNUM_FS_FAT_NODE_POOL_SIZE >= (CYGNUM_FILEIO_NFILE+2) } + description "This option controls the size of the node pool used + for storing file nodes. This value should be set to + the maximum required number of simultaneously open + files plus the desired size of unused node cache." + } + + cdl_option CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE { + display "FAT block cache memory size" + flavor data + default_value 10240 + legal_values 1 to 9999999999 + description "This option controls the amount of memory used for + the block cache." + } + + cdl_option CYGDBG_FS_FAT_NODE_CACHE_EXTRA_CHECKS { + display "Node cache extra checks" + flavor bool + default_value 1 + active_if CYGPKG_INFRA_DEBUG && CYGDBG_USE_ASSERTS + description "This option controls the inclusion of extra + sanity checks in node cache code." + } + + cdl_option CYGCFG_FS_FAT_USE_ATTRIBUTES { + display "Support for FAT FS file attributes" + flavor bool + default_value 0 + description "This option controls if the FAT filesystem supports + or honors the FAT filesystem file attributes." + } + + cdl_option CYGPKG_FS_FAT_RET_DIRENT_DTYPE { + display "Support for fileio's struct dirent d_type field" + flavor bool + default_value 0 + active_if CYGPKG_FILEIO_DIRENT_DTYPE + description "This option controls whether the FAT filesystem + supports setting fileio's struct dirent d_type field. + If this option is enabled, d_type will be set." + } + + + # -------------------------------------------------------------------- + + cdl_component CYGPKG_DEVS_DISK_TESTING { + display "Testing configuration" + default_value 1 + + cdl_option CYGDAT_DEVS_DISK_TEST_DEVICE { + display "Test device driver" + flavor data + default_value {"\"/dev/synthdisk0/1\"" } + } + + cdl_option CYGDAT_DEVS_DISK_TEST_DEVICE2 { + display "Second Test device driver" + flavor data + default_value {"\"/dev/synthdisk0/2\"" } + } + + } + + cdl_option CYGPKG_FS_FAT_TESTS { + display "FAT FS tests" + flavor data + no_define + calculated { "tests/fatfs1" } + description "This option specifies the set of tests for the + FAT FS package." + } +} + +# ==================================================================== +# End of fatfs.cdl diff --git a/ecos/packages/fs/fat/current/doc/README.txt b/ecos/packages/fs/fat/current/doc/README.txt new file mode 100644 index 0000000..f7b484d --- /dev/null +++ b/ecos/packages/fs/fat/current/doc/README.txt @@ -0,0 +1,116 @@ + +FAT Filesystem README ++++++++++++++++++++++ + +This file serves as documentation for the FAT filesystem until full +documentation can be generated. + +Configuring FAT +=============== + +The FAT filesystem support consists of several packages: + +CYGPKG_FS_FAT + The FAT filesystem itself. + +CYGPKG_BLOCK_LIB + Block library. This actually implements the block cache. It + depends on the Linux compatibility library + (CYGPKG_LINUX_COMPAT) for support. + +CYGPKG_IO_DISK + Disk device IO support. This provides the top level disk + driver functions. It also interprets partition tables and + provides a separate access channel for each partition. + +CYGPKG_DEVS_DISK_ECOSYNTH + This is the only low level device driver currently known to + work. This driver treats a file in the Linux filesystem as a + disk image. + +CYGPKG_DEVS_DISK_V85X_EDB_V850 + This is a device driver for an IDE compatible Compact Flash + device on a platform that is otherwise unsupported. It has not + been tested. + +and other disk drivers which have been added in the mean time. + +In addition the above new packages, FAT support also relies on the +following existing packages: + +CYGPKG_IO_FILEIO + The File IO package. This provides the API by which the FAT + filesystem is accessed. + +CYGPKG_IO + Device IO package. This provides all the infrastructure for + the disk devices. + +CYGPKG_LIBC_STRING + Strings library. This provides the string and memory move and + compare routines used by the filesystem. + +CYGPKG_MEMALLOC + The FAT filesystem currently uses malloc() to allocate + its memory resources, so this package is required. + +The doc directory that contains this file contains a number of .ecm +files that will add the FAT filesystem to most configurations. These +should be used as a basis for creating new configurations. + +Device Drivers +============== + +The disk device interface is divided into a generic layer in the +CYGPKG_IO_DISK package and drivers for each device. + +Each disk driver provides a number of device files. The drivers use a +naming convention to ensure they are unique. The convention is +/dev/XXXdiskY/Z. XXX is the name of the device driver, eg ide, mmc, +synth etc. The DISK packages provide support for disk partitions. They +interpret the on-disk data structures and provides an independent +channel to access each partition independently. Partitions are +referenced using an additional element to the device name. If the +device is named "/dev/XXXdisk0" then "/dev/XXXdisk0/1" refers to +partition 1, "/dev/XXXdisk0/2" to partition 2 and so on. If the disk +is not partitioned, for example a floppy disk, then "/dev/XXXdisk0/0" +refers to the whole disk. + +Testing +======= + +There is currently just one test available for the FAT filesystem, +fileio1. This test will currently only run on the Linux synthetic +target and needs a special disk image. However, with some work it +should be possible to make this test run on other disk drivers, if the +image can be installed and the configury set appropriately. + +To run the test you first need to configure the synthetic disk driver +to access the disk image. This is most easily done by importing the +fat16.ecm file. A complete build would look like this: + +$ mkdir linux.fat16 +$ cd linux.fat16 +$ ecosconfig new linux +$ ecosconfig import $ECOS_REPOSITORY/fs/fat/current/doc/fat16.ecm +$ ecosconfig tree +$ make tests + +To make the disk image, first ensure that there is at least 10MB of +space available in your /tmp directory. Add the following two lines to +your ~/.mtoolsrc file (you may need to create it): + +drive z: file="/tmp/disk0.img" partition=1 +drive y: file="/tmp/disk0.img" partition=2 + +Now run the mkdisk0 shell script from the fs/fat/doc directory: + +$ source $ECOS_REPOSITORY/fs/fat/current/doc/mkdisk0 +20000+0 records in +20000+0 records out +Warning: no active (bootable) partition present +$ + +Ignore the warning, we are not interested in booting this disk. + +It should now be possible to run the fileio1 test program. diff --git a/ecos/packages/fs/fat/current/doc/fat16.ecm b/ecos/packages/fs/fat/current/doc/fat16.ecm new file mode 100644 index 0000000..c4d5038 --- /dev/null +++ b/ecos/packages/fs/fat/current/doc/fat16.ecm @@ -0,0 +1,53 @@ +cdl_savefile_version 1; +cdl_savefile_command cdl_savefile_version {}; +cdl_savefile_command cdl_savefile_command {}; +cdl_savefile_command cdl_configuration { description hardware template package }; +cdl_savefile_command cdl_package { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_component { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_option { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_interface { value_source user_value wizard_value inferred_value }; + +cdl_configuration eCos { + + package CYGPKG_IO_FILEIO current ; + package CYGPKG_IO current ; + package CYGPKG_LIBC_STRING current ; + + package CYGPKG_DEVS_DISK_ECOSYNTH current ; + package CYGPKG_IO_DISK current ; + package CYGPKG_BLOCK_LIB current ; + package CYGPKG_LINUX_COMPAT current ; + package CYGPKG_FS_FAT current ; + + package CYGPKG_MEMALLOC current ; + +}; + + +cdl_component CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 { + user_value 1 +}; + +cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_FILENAME { + user_value {"/tmp/disk0.img"} +}; + +cdl_option CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE { + user_value 10240000 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR { + user_value 1 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_CYLINDERS { + user_value 100 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_HEADS { + user_value 5 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_SECTORS { + user_value 40 +}; diff --git a/ecos/packages/fs/fat/current/doc/fatfd.ecm b/ecos/packages/fs/fat/current/doc/fatfd.ecm new file mode 100644 index 0000000..ef71853 --- /dev/null +++ b/ecos/packages/fs/fat/current/doc/fatfd.ecm @@ -0,0 +1,41 @@ +cdl_savefile_version 1; +cdl_savefile_command cdl_savefile_version {}; +cdl_savefile_command cdl_savefile_command {}; +cdl_savefile_command cdl_configuration { description hardware template package }; +cdl_savefile_command cdl_package { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_component { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_option { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_interface { value_source user_value wizard_value inferred_value }; + +cdl_configuration eCos { + + package CYGPKG_IO_FILEIO current ; + package CYGPKG_IO current ; + package CYGPKG_LIBC_STRING current ; + + package CYGPKG_DEVS_DISK_ECOSYNTH current ; + package CYGPKG_IO_DISK current ; + package CYGPKG_BLOCK_LIB current ; + package CYGPKG_LINUX_COMPAT current ; + package CYGPKG_FS_FAT current ; + + package CYGPKG_MEMALLOC current ; + +}; + + +cdl_component CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 { + user_value 1 +}; + +cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_FILENAME { + user_value {"/dev/fd0"} +}; + +cdl_option CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE { + user_value 1474560 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR { + user_value 0 +};
\ No newline at end of file diff --git a/ecos/packages/fs/fat/current/doc/mkdisk0 b/ecos/packages/fs/fat/current/doc/mkdisk0 new file mode 100755 index 0000000..37fb7f7 --- /dev/null +++ b/ecos/packages/fs/fat/current/doc/mkdisk0 @@ -0,0 +1,18 @@ +#! /bin/bash +# +# Add the following lines to your ~/.mtoolsrc file before running this script +# +# drive z: file="/tmp/disk0.img" partition=1 +# drive y: file="/tmp/disk0.img" partition=2 +# +# +# + +dd if=/dev/zero of=/tmp/disk0.img bs=512 count=20000 +mpartition -I -t 100 -h 5 -s 40 Z: +mpartition -cfa -t 49 -h 5 -s 40 -b 200 Z: +mpartition -cf -t 50 -h 5 -s 40 -b 10000 Y: +mformat z: +mformat y: + +# end diff --git a/ecos/packages/fs/fat/current/include/fatfs.h b/ecos/packages/fs/fat/current/include/fatfs.h new file mode 100644 index 0000000..a29c9a2 --- /dev/null +++ b/ecos/packages/fs/fat/current/include/fatfs.h @@ -0,0 +1,83 @@ +#ifndef CYGONCE_CYG_FS_FAT_H +#define CYGONCE_CYG_FS_FAT_H +//============================================================================= +// +// fatfs.h +// +// FAT FS attributes and stat information +// +//============================================================================= +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//============================================================================= +//#####DESCRIPTIONBEGIN#### +// +// Author(s): David Brennam <eCos@brennanhome.com> +// Contributors: +// Date: 2004-10-22 +// Purpose: +// Description: This header contains attributes and stat like mode +// information for FAT filesystems. +// +// Usage: +// #include <cyg/fs/fatfs.h> +// +// +//####DESCRIPTIONEND#### +// +//============================================================================= +// ------------------------------------------------------------------------- +// FAT filesystem dir entry attributes + +#define S_FATFS_RDONLY (0x01) // Read only +#define S_FATFS_HIDDEN (0x02) // Hidden +#define S_FATFS_SYSTEM (0x04) // System +#define S_FATFS_VOLUME (0x08) // Volume label +#define S_FATFS_DIR (0x10) // Subdirectory +#define S_FATFS_ARCHIVE (0x20) // Needs archiving + +// Mode bits which are allowed to be changed by attrib +#define S_FATFS_ATTRIB (S_FATFS_RDONLY | S_FATFS_HIDDEN | S_FATFS_SYSTEM | \ + S_FATFS_ARCHIVE) +// ------------------------------------------------------------------------- +// mode FAT dir entry attributes macros + +#define S_FATFS_ISRDONLY(__mode) ((__mode) & S_FATFS_RDONLY) +#define S_FATFS_ISHIDDEN(__mode) ((__mode) & S_FATFS_HIDDEN) +#define S_FATFS_ISSYSTEM(__mode) ((__mode) & S_FATFS_SYSTEM) +#define S_FATFS_ISVOLUME(__mode) ((__mode) & S_FATFS_VOLUME) +#define S_FATFS_ISDIR(__mode) ((__mode) & S_FATFS_DIR) +#define S_FATFS_ISARCHIVE(__mode) ((__mode) & S_FATFS_ARCHIVE) + +#endif // CYGONCE_CYG_FS_FAT_H +// End of fatfs.h + diff --git a/ecos/packages/fs/fat/current/src/fatfs.c b/ecos/packages/fs/fat/current/src/fatfs.c new file mode 100644 index 0000000..4a1628f --- /dev/null +++ b/ecos/packages/fs/fat/current/src/fatfs.c @@ -0,0 +1,1587 @@ +//========================================================================== +// +// fatfs.c +// +// FAT file system +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Savin Zlobec <savin@elatec.si> (based on ramfs.c) +// Original data: nickg +// Date: 2003-06-29 +// Purpose: FAT file system +// Description: This is a FAT filesystem for eCos. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/system.h> +#include <pkgconf/hal.h> +#include <pkgconf/io_fileio.h> +#include <pkgconf/fs_fat.h> + +#include <cyg/infra/cyg_type.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/infra/diag.h> +#include <cyg/fileio/fileio.h> +#include <cyg/io/io.h> +#include <blib/blib.h> +#include <cyg/fs/fatfs.h> + +#include "fatfs.h" + +//========================================================================== +// Tracing support defines + +#ifdef FATFS_TRACE_FS_OP +# define TFS 1 +#else +# define TFS 0 +#endif + +#ifdef FATFS_TRACE_FILE_OP +# define TFO 1 +#else +# define TFO 0 +#endif + +//========================================================================== +// Forward definitions + +// Filesystem operations +static int fatfs_mount (cyg_fstab_entry *fste, cyg_mtab_entry *mte); +static int fatfs_umount (cyg_mtab_entry *mte); +static int fatfs_open (cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int mode, cyg_file *fte); +static int fatfs_unlink (cyg_mtab_entry *mte, cyg_dir dir, const char *name); +static int fatfs_mkdir (cyg_mtab_entry *mte, cyg_dir dir, const char *name); +static int fatfs_rmdir (cyg_mtab_entry *mte, cyg_dir dir, const char *name); +static int fatfs_rename (cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2 ); +static int fatfs_link (cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2, int type ); +static int fatfs_opendir(cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_file *fte ); +static int fatfs_chdir (cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_dir *dir_out ); +static int fatfs_stat (cyg_mtab_entry *mte, cyg_dir dir, const char *name, + struct stat *buf); +static int fatfs_getinfo(cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); +static int fatfs_setinfo(cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); + +// File operations +static int fatfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int fatfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int fatfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); +static int fatfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data); +static int fatfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ); +static int fatfs_fo_close (struct CYG_FILE_TAG *fp); +static int fatfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ); +static int fatfs_fo_getinfo(struct CYG_FILE_TAG *fp, int key, + void *buf, int len ); +static int fatfs_fo_setinfo(struct CYG_FILE_TAG *fp, int key, + void *buf, int len ); + +// Directory operations +static int fatfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int fatfs_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t *pos, int whence); + +//========================================================================== +// Filesystem table entries + +// ------------------------------------------------------------------------- +// Fstab entry. + +FSTAB_ENTRY(fatfs_fste, "fatfs", 0, + CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM, + fatfs_mount, + fatfs_umount, + fatfs_open, + fatfs_unlink, + fatfs_mkdir, + fatfs_rmdir, + fatfs_rename, + fatfs_link, + fatfs_opendir, + fatfs_chdir, + fatfs_stat, + fatfs_getinfo, + fatfs_setinfo); + +// ------------------------------------------------------------------------- +// File operations. + +static cyg_fileops fatfs_fileops = +{ + fatfs_fo_read, + fatfs_fo_write, + fatfs_fo_lseek, + fatfs_fo_ioctl, + cyg_fileio_seltrue, + fatfs_fo_fsync, + fatfs_fo_close, + fatfs_fo_fstat, + fatfs_fo_getinfo, + fatfs_fo_setinfo +}; + +// ------------------------------------------------------------------------- +// Directory file operations. + +static cyg_fileops fatfs_dirops = +{ + fatfs_fo_dirread, + (cyg_fileop_write *)cyg_fileio_enosys, + fatfs_fo_dirlseek, + (cyg_fileop_ioctl *)cyg_fileio_enosys, + cyg_fileio_seltrue, + (cyg_fileop_fsync *)cyg_fileio_enosys, + fatfs_fo_close, + (cyg_fileop_fstat *)cyg_fileio_enosys, + (cyg_fileop_getinfo *)cyg_fileio_enosys, + (cyg_fileop_setinfo *)cyg_fileio_enosys +}; + +// ------------------------------------------------------------------------- +// Directory search data +// Parameters for a directory search. The fields of this structure are +// updated as we follow a pathname through the directory tree. + +typedef struct fatfs_dirsearch_s +{ + fatfs_disk_t *disk; // Disk info + fatfs_node_t *dir; // Directory to search + const char *path; // Path to follow + fatfs_node_t *node; // Node found + const char *name; // Last name fragment used + int namelen; // Name fragment length + cyg_bool last; // Last name in path? +} fatfs_dirsearch_t; + +// ------------------------------------------------------------------------- +// FATFS file descriptor data + +typedef struct fatfs_fd_s +{ + fatfs_node_t *node; + fatfs_data_pos_t pos; +} fatfs_fd_t; + +static fatfs_fd_t fatfs_fds_base[CYGNUM_FILEIO_NFD]; +static fatfs_fd_t *fatfs_fds_pool[CYGNUM_FILEIO_NFD]; +static cyg_uint32 fatfs_fds_free_cnt; + +//========================================================================== + +#if TFS +static void +print_disk_info(fatfs_disk_t *disk) +{ + diag_printf("FAT: sector size: %u\n", disk->sector_size); + diag_printf("FAT: cluster size: %u\n", disk->cluster_size); + diag_printf("FAT: FAT table position: %u\n", disk->fat_tbl_pos); + diag_printf("FAT: FAT table num ent: %u\n", disk->fat_tbl_nents); + diag_printf("FAT: FAT table size: %u\n", disk->fat_tbl_size); + diag_printf("FAT: FAT tables num: %u\n", disk->fat_tbls_num); + diag_printf("FAT: FAT root dir pos: %u\n", disk->fat_root_dir_pos); + diag_printf("FAT: FAT root dir nents: %u\n", disk->fat_root_dir_nents); + diag_printf("FAT: FAT root dir size: %u\n", disk->fat_root_dir_size); + diag_printf("FAT: FAT data position: %u\n", disk->fat_data_pos); +} +#endif + +static void +init_fatfs_fds(void) +{ + static bool initialized = false; + + int i; + + if (initialized) + return; + + initialized = true; + + for (i = 0; i < CYGNUM_FILEIO_NFD; i++) + { + fatfs_fds_pool[i] = &fatfs_fds_base[i]; + } + fatfs_fds_free_cnt = i; +} + +static fatfs_fd_t * +alloc_fatfs_fd(fatfs_disk_t *disk, fatfs_node_t *node) +{ + fatfs_fd_t *fd = NULL; + + if (fatfs_fds_free_cnt > 0) + { + fd = fatfs_fds_pool[--fatfs_fds_free_cnt]; + + fd->node = node; + fatfs_initpos(disk, &node->dentry, &fd->pos); + } + + return fd; +} + +static void +free_fatfs_fd(fatfs_fd_t *fd) +{ + fatfs_fds_pool[fatfs_fds_free_cnt++] = fd; +} + +static void +init_dirsearch(fatfs_dirsearch_t *ds, + fatfs_disk_t *disk, + fatfs_node_t *dir, + const char *name) +{ + ds->disk = disk; + + if (NULL == dir) + ds->dir = disk->root; + else + ds->dir = dir; + + ds->path = name; + ds->node = ds->dir; + ds->namelen = 0; + ds->last = false; +} + +static int +find_direntry(fatfs_dirsearch_t *ds) +{ + fatfs_dir_entry_t dentry; + fatfs_data_pos_t pos; + int err; + + CYG_TRACE1(TFS, "searching for dir entry '%s'", ds->name); + + // Check for '.' + + if (!strncmp(".", ds->name, ds->namelen)) + { + ds->node = ds->dir; + return ENOERR; + } + + // Check the cache + + ds->node = fatfs_node_find(ds->disk, + ds->name, + ds->namelen, + ds->dir->dentry.cluster); + + if (ds->node != NULL) + { + // Dir entry found in cache + + CYG_TRACE0(TFS, "dir entry found in cache"); + + fatfs_node_touch(ds->disk, ds->node); + return ENOERR; + } + + // Dir entry not in cache - search the current dir + + fatfs_initpos(ds->disk, &ds->dir->dentry, &pos); + + while (true) + { + // Read next dir entry + + err = fatfs_read_dir_entry(ds->disk, &ds->dir->dentry, &pos, &dentry); + if (err != ENOERR) + return (err == EEOF ? ENOERR : err); + + // Compare filenames + + if ('\0' == dentry.filename[ds->namelen] && + 0 == strncasecmp(dentry.filename, ds->name, ds->namelen)) + { + // Dir entry found - allocate new node and return + + CYG_TRACE0(TFS, "dir entry found"); + + if (0 == strncmp(ds->name, "..", ds->namelen)) + { + fatfs_dir_entry_t _dentry; + fatfs_data_pos_t _pos; + + if (0 == dentry.cluster) + { + ds->node = ds->disk->root; + return ENOERR; + } + + fatfs_initpos(ds->disk, &dentry, &_pos); + while (true) + { + err = fatfs_read_dir_entry(ds->disk, &dentry, &_pos, &_dentry); + if (err != ENOERR) + return err; + if (0 == strcmp(".", _dentry.filename)) + break; + } + + ds->node = fatfs_node_find(ds->disk, + _dentry.filename, + strlen(_dentry.filename), + _dentry.parent_cluster); + + if (NULL != ds->node) + fatfs_node_touch(ds->disk, ds->node); + else + ds->node = fatfs_node_alloc(ds->disk, &_dentry); + + if (NULL == ds->node) + return EMFILE; + + return ENOERR; + } + else + ds->node = fatfs_node_alloc(ds->disk, &dentry); + + if (NULL == ds->node) + return EMFILE; + + return ENOERR; + } + } +} + +static int +find_entry(fatfs_dirsearch_t *ds) +{ + const char *name = ds->path; + const char *n = name; + char namelen = 0; + int err; + + if( !S_ISDIR(ds->dir->dentry.mode) ) + { + CYG_TRACE1(TFS, "entry '%s' not dir", ds->dir->dentry.filename); + 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; + + err = find_direntry(ds); + if (err != ENOERR) + return err; + + CYG_TRACE2(TFS, "entry '%s' %s", name, (ds->node ? "found" : "not found")); + + if (ds->node != NULL) + return ENOERR; + else + return ENOENT; +} + +static int +fatfs_find(fatfs_dirsearch_t *ds) +{ + int err; + + CYG_TRACE1(TFS, "find path='%s'", ds->path); + + // Short circuit empty paths + if (*(ds->path) == '\0') + return ENOERR; + + // Iterate down directory tree until we find the object we want + for(;;) + { + err = find_entry(ds); + + if (err != ENOERR) + return err; + + if (ds->last) + { + CYG_TRACE0(TFS, "entry found"); + return ENOERR; + } + + // Update dirsearch object to search next directory + ds->dir = ds->node; + ds->path += ds->namelen; + + // Skip dirname separators + if (*(ds->path) == '/') ds->path++; + + CYG_TRACE1(TFS, "find path to go='%s'", ds->path); + } +} + +//========================================================================== +// Filesystem operations + +// ------------------------------------------------------------------------- +// fatfs_mount() +// Process a mount request. This mainly creates a root for the +// filesystem. + +static int +fatfs_mount(cyg_fstab_entry *fste, cyg_mtab_entry *mte) +{ + cyg_io_handle_t dev_h; + fatfs_disk_t *disk; + fatfs_dir_entry_t root_dentry; + Cyg_ErrNo err; + + CYG_TRACE2(TFS, "mount fste=%p mte=%p", fste, mte); + + init_fatfs_fds(); + + CYG_TRACE1(TFS, "looking up disk device '%s'", mte->devname); + + err = cyg_io_lookup(mte->devname, &dev_h); + if (err != ENOERR) + return err; + + disk = (fatfs_disk_t *)malloc(sizeof(fatfs_disk_t)); + if (NULL == disk) + return ENOMEM; + + CYG_TRACE0(TFS, "initializing block cache"); + + disk->bcache_mem = (cyg_uint8 *)malloc(CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE); + if (NULL == disk->bcache_mem) + { + free(disk); + return ENOMEM; + } + // FIXME: get block size from disk device + err = cyg_blib_io_create(dev_h, disk->bcache_mem, + CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE, 512, &disk->blib); + if (err != ENOERR) + { + free(disk->bcache_mem); + free(disk); + return err; + } + + disk->dev_h = dev_h; + + CYG_TRACE0(TFS, "initializing disk"); + + err = fatfs_init(disk); + if (err != ENOERR) + { + cyg_blib_delete(&disk->blib); + free(disk->bcache_mem); + free(disk); + return err; + } + +#if TFS + print_disk_info(disk); +#endif + + CYG_TRACE0(TFS, "initializing node cache"); + + fatfs_node_cache_init(disk); + + CYG_TRACE0(TFS, "initializing root node"); + + fatfs_get_root_dir_entry(disk, &root_dentry); + + disk->root = fatfs_node_alloc(disk, &root_dentry); + + fatfs_node_ref(disk, disk->root); + + mte->root = (cyg_dir)disk->root; + mte->data = (CYG_ADDRWORD)disk; + + CYG_TRACE0(TFS, "disk mounted"); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_umount() +// Unmount the filesystem. This will currently only succeed if the +// filesystem is empty. + +static int +fatfs_umount(cyg_mtab_entry *mte) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_node_t *root = (fatfs_node_t *) mte->root; + + CYG_TRACE3(TFS, "umount mte=%p %d live nodes %d dead nodes", + mte, fatfs_get_live_node_count(disk), + fatfs_get_dead_node_count(disk)); + + if (root->refcnt > 1) + return EBUSY; + + if (fatfs_get_live_node_count(disk) != 1) + return EBUSY; + + fatfs_node_unref(disk, root); + fatfs_node_cache_flush(disk); + // FIXME: cache delete can fail if cache can't be synced + cyg_blib_delete(&disk->blib); + free(disk->bcache_mem); + free(disk); + + mte->root = CYG_DIR_NULL; + mte->data = (CYG_ADDRWORD) NULL; + + CYG_TRACE0(TFS, "disk umounted"); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_open() +// Open a file for reading or writing. + +static int +fatfs_open(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + int mode, + cyg_file *file) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_node_t *node = NULL; + fatfs_fd_t *fd; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE5(TFS, "open mte=%p dir=%p name='%s' mode=%d file=%p", + mte, dir, name, mode, file); + + init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name); + + err = fatfs_find(&ds); + + if (err == ENOENT) + { + if (ds.last && (mode & O_CREAT)) + { + fatfs_dir_entry_t new_file_dentry; + + // 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. + + CYG_TRACE1(TFS, "creating new file '%s'", name); + + err = fatfs_create_file(disk, + &ds.dir->dentry, + ds.name, + ds.namelen, + &new_file_dentry); + if (err != ENOERR) + return err; + + node = fatfs_node_alloc(disk, &new_file_dentry); + if (NULL == node) + return EMFILE; + + // Update directory times + ds.dir->dentry.atime = + ds.dir->dentry.mtime = cyg_timestamp(); + + err = ENOERR; + } + } + else if (err == ENOERR) + { + // The node exists. If the O_CREAT and O_EXCL bits are set, we + // must fail the open + + if ((mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) + err = EEXIST; + else + node = ds.node; + } + + if (err == ENOERR && (mode & O_TRUNC)) + { + // If the O_TRUNC bit is set we must clean out the file data + CYG_TRACE0(TFS, "truncating file"); + fatfs_trunc_file(disk, &node->dentry); + } + + if (err != ENOERR) + return err; + + if (S_ISDIR(node->dentry.mode)) + return EISDIR; + +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + // if the file is read only and is opened for writing + // fail with permission error + if (S_FATFS_ISRDONLY(node->dentry.attrib) && (mode & O_WRONLY)) + return EACCES; +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + + // Allocate file object private data and + // make a reference to this file node + + fd = alloc_fatfs_fd(disk, node); + if (NULL == fd) + return EMFILE; + + fatfs_node_ref(disk, node); + + // Initialize the file object + + if (mode & O_APPEND) + fatfs_setpos(disk, &node->dentry, &fd->pos, node->dentry.size); + + file->f_flag |= mode & CYG_FILE_MODE_MASK; + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &fatfs_fileops; + file->f_offset = (mode & O_APPEND) ? node->dentry.size : 0; + file->f_data = (CYG_ADDRWORD) fd; + file->f_xops = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_unlink() +// Remove a file link from its directory. + +static int +fatfs_unlink(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE3(TFS, "unlink mte=%p dir=%p name='%s'", mte, dir, name); + + init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name); + + err = fatfs_find(&ds); + + if (err != ENOERR) + return err; + + if (ds.node->refcnt > 0) + return EBUSY; + +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + // if the file is read only fail with permission error + if (S_FATFS_ISRDONLY(ds.node->dentry.attrib)) + return EPERM; +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + + err = fatfs_delete_file(disk, &ds.node->dentry); + if (err == ENOERR) + fatfs_node_free(disk, ds.node); + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_mkdir() +// Create a new directory. + +static int +fatfs_mkdir(cyg_mtab_entry *mte, cyg_dir dir, const char *name) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE3(TFS, "mkdir mte=%p dir=%p name='%s'", mte, dir, name); + + init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name); + + err = fatfs_find(&ds); + + if (err == ENOENT) + { + if (ds.last) + { + fatfs_dir_entry_t new_dir_dentry; + + // The entry does not exist, and it is the last element in + // the pathname, so we can create it here + + err = fatfs_create_dir(disk, + &ds.dir->dentry, + ds.name, + ds.namelen, + &new_dir_dentry); + if (err != ENOERR) + return err; + + fatfs_node_alloc(disk, &new_dir_dentry); + + return ENOERR; + } + } + else if (err == ENOERR) + { + return EEXIST; + } + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_rmdir() +// Remove a directory. + +static int +fatfs_rmdir(cyg_mtab_entry *mte, cyg_dir dir, const char *name) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_dirsearch_t ds; + int err; + fatfs_node_t *node; + + CYG_TRACE3(TFS, "rmdir mte=%p dir=%p name='%s'", mte, dir, name); + + init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name); + + err = fatfs_find(&ds); + + if (err != ENOERR) + return err; + + if (!S_ISDIR(ds.node->dentry.mode)) + return EPERM; + + if (ds.node->refcnt > 0) + return EBUSY; + + err = fatfs_delete_file(disk, &ds.node->dentry); + if (err == ENOERR) + { + node = fatfs_node_find( disk, ".", 1, ds.node->dentry.cluster ); + if (node != NULL) + fatfs_node_free(disk, node); + + node = fatfs_node_find( disk, "..", 2, ds.node->dentry.cluster ); + if (node != NULL) + fatfs_node_free(disk, node); + + fatfs_node_free(disk, ds.node); + } + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_rename() +// Rename a file/dir. + +static int +fatfs_rename(cyg_mtab_entry *mte, + cyg_dir dir1, + const char *name1, + cyg_dir dir2, + const char *name2) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_dirsearch_t ds1, ds2; + int err; + + CYG_TRACE5(TFS, "rename mte=%p dir1=%p name1='%s' dir2=%p name2='%s'", + mte, dir1, name1, dir2, name2); + + init_dirsearch(&ds1, disk, (fatfs_node_t *)dir1, name1); + + err = fatfs_find(&ds1); + if (err != ENOERR) + return err; + +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + // if the file is read only fail with permission error + if (S_FATFS_ISRDONLY(ds1.node->dentry.attrib)) + return EPERM; +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + + // Protect the found nodes from being reused + // by the search for the ds2 dir/node pair + fatfs_node_ref(disk, ds1.dir); + fatfs_node_ref(disk, ds1.node); + + init_dirsearch(&ds2, disk, (fatfs_node_t *) dir2, name2); + + err = fatfs_find(&ds2); + + // Check if the target name already exists + if (err == ENOERR && ds2.last) + { + err = EEXIST; + goto out; + } + + // Check if the target dir doesn't exist + if (err == ENOENT && !ds2.last) + goto out; + + // Check if the target and the source are the same + if (ds1.node == ds2.node) + { + err = ENOERR; + goto out; + } + + err = fatfs_rename_file(disk, + &ds1.dir->dentry, + &ds1.node->dentry, + &ds2.dir->dentry, + ds2.name, + ds2.namelen); + + fatfs_node_rehash(disk, ds1.node); + +out: + // Unreference previousely protected nodes + fatfs_node_unref(disk, ds1.dir); + fatfs_node_unref(disk, ds1.node); + + if (err == ENOERR) + { + ds1.dir->dentry.atime = + ds1.dir->dentry.mtime = + ds2.dir->dentry.atime = + ds2.dir->dentry.mtime = cyg_timestamp(); + } + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_link() +// Make a new directory entry for a file. + +static int +fatfs_link(cyg_mtab_entry *mte, + cyg_dir dir1, + const char *name1, + cyg_dir dir2, + const char *name2, + int type) +{ + CYG_TRACE6(TFS, "link mte=%p dir1=%p name1='%s' dir2=%p name2='%s' type=%d", + mte, dir1, name1, dir2, name2, type); + + // Linking not supported + return EINVAL; +} + +// ------------------------------------------------------------------------- +// fatfs_opendir() +// Open a directory for reading. + +static int +fatfs_opendir(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + cyg_file *file) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_fd_t *fd; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE4(TFS, "opendir mte=%p dir=%p name='%s' file=%p", + mte, dir, name, file); + + init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name); + + err = fatfs_find(&ds); + if (err != ENOERR) + return err; + + if (!S_ISDIR(ds.node->dentry.mode)) + return ENOTDIR; + + // Allocate file object private data and + // make a reference to this file node + + fd = alloc_fatfs_fd(disk, ds.node); + if (NULL == fd) + return EMFILE; + + fatfs_node_ref(disk, ds.node); + + // Initialize the file object + + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &fatfs_dirops; + file->f_data = (CYG_ADDRWORD) fd; + file->f_xops = 0; + file->f_offset = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_chdir() +// Change directory support. + +static int +fatfs_chdir(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + cyg_dir *dir_out) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + + CYG_TRACE4(TFS, "chdir mte=%p dir=%p dir_out=%p name=%d", + mte, dir, dir_out, name); + + if (dir_out != NULL) + { + // This is a request to get a new directory pointer in *dir_out + + fatfs_dirsearch_t ds; + int err; + + init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name); + + err = fatfs_find(&ds); + if (err != ENOERR) + return err; + + if (!S_ISDIR(ds.node->dentry.mode)) + return ENOTDIR; + + if (ds.node != disk->root) + fatfs_node_ref(disk, ds.node); + + *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. + + fatfs_node_t *node = (fatfs_node_t *) dir; + + if (node != disk->root) + fatfs_node_unref(disk, node); + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_stat() +// Get struct stat info for named object. + +static int +fatfs_stat(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + struct stat *buf) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE4(TFS, "stat mte=%p dir=%p name='%s' buf=%p", + mte, dir, name, buf); + + init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name); + + err = fatfs_find(&ds); + if (err != ENOERR) + return err; + + // Fill in the status + + buf->st_mode = ds.node->dentry.mode; +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + if (!S_FATFS_ISRDONLY(ds.node->dentry.attrib)) + buf->st_mode |= (S_IWUSR | S_IWGRP | S_IWOTH); +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + buf->st_ino = (ino_t) ds.node->dentry.cluster; + buf->st_dev = 0; + buf->st_nlink = 1; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = ds.node->dentry.size; + buf->st_atime = ds.node->dentry.atime; + buf->st_mtime = ds.node->dentry.mtime; + buf->st_ctime = ds.node->dentry.ctime; + + return ENOERR; +} + +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES +// ------------------------------------------------------------------------- +// fatfs_set_attrib() +// Set FAT file system attributes for specified file + +static int +fatfs_set_attrib(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + const cyg_fs_attrib_t new_attrib) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE4(TFS, "set_attrib mte=%p dir=%p name='%s' buf=%x", + mte, dir, name, new_attrib); + + // Verify new_mode is valid + if ((new_attrib & S_FATFS_ATTRIB) != new_attrib) + return EINVAL; + + init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name); + + err = fatfs_find(&ds); + if (err != ENOERR) + return err; + + // Change the "changeable" mode bits for the file. + ds.node->dentry.attrib = + (ds.node->dentry.attrib & (~S_FATFS_ATTRIB)) | new_attrib; + + return fatfs_write_dir_entry(disk,&ds.node->dentry); +} + +// ------------------------------------------------------------------------- +// fatfs_get_attrib() +// Set FAT file system attributes for specified file + +static int +fatfs_get_attrib(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + cyg_fs_attrib_t * const file_attrib) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE4(TFS, "get_attrib mte=%p dir=%p name='%s' buf=%x", + mte, dir, name, file_attrib); + + init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name); + + err = fatfs_find(&ds); + if (err != ENOERR) + return err; + + // Get the attribute field + CYG_CHECK_DATA_PTR(file_attrib,"Invalid destination attribute pointer"); + *file_attrib = ds.node->dentry.attrib; + + return ENOERR; +} +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + +// ------------------------------------------------------------------------- +// fatfs_getinfo() +// Getinfo. Support for attrib + +static int +fatfs_getinfo(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + int key, + void *buf, + int len) +{ + int err = EINVAL; + + CYG_TRACE6(TFS, "getinfo mte=%p dir=%p name='%s' key=%d buf=%p len=%d", + mte, dir, name, key, buf, len); + switch( key ) + { +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + case FS_INFO_ATTRIB: + err = fatfs_get_attrib(mte, dir, name, (cyg_fs_attrib_t*)buf); + break; +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) + case FS_INFO_BLOCK_USAGE: { + cyg_uint32 total_clusters; + cyg_uint32 free_clusters; + struct cyg_fs_block_usage *usage = (struct cyg_fs_block_usage *) buf; + fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; + + err = fatfs_get_disk_usage(disk, &total_clusters, &free_clusters); + if (err) + return err; + usage->total_blocks = total_clusters; + usage->free_blocks = free_clusters; + usage->block_size = disk->cluster_size; + break; + } +#endif + default: + err = EINVAL; + break; + } + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_setinfo() +// Setinfo. Support for fssync and attrib + +static int +fatfs_setinfo(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + int key, + void *buf, + int len) +{ + int err = EINVAL; + + CYG_TRACE6(TFS, "setinfo mte=%p dir=%p name='%s' key=%d buf=%p len=%d", + mte, dir, name, key, buf, len); + + switch( key ) + { + case FS_INFO_SYNC: + err = cyg_blib_sync(&(((fatfs_disk_t *) mte->data)->blib)); + break; +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + case FS_INFO_ATTRIB: + err = fatfs_set_attrib(mte, dir, name, *(cyg_fs_attrib_t *)buf); + break; +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + default: + err = EINVAL; + break; + } + return err; +} + +//========================================================================== +// File operations + +// ------------------------------------------------------------------------- +// fatfs_fo_read() +// Read data from the file. + +static int +fatfs_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) fp->f_mte->data; + fatfs_fd_t *fd = (fatfs_fd_t *) fp->f_data; + fatfs_node_t *node = fd->node; + cyg_uint32 pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + int i; + + CYG_TRACE3(TFO, "read fp=%p uio=%p pos=%d", fp, uio, pos); + + // Loop over the io vectors until there are none left + + for (i = 0; i < uio->uio_iovcnt; i++) + { + cyg_iovec *iov = &uio->uio_iov[i]; + char *buf = (char *) iov->iov_base; + off_t len = iov->iov_len; + + // Loop over each vector filling it with data from the file + + while (len > 0 && pos < node->dentry.size) + { + cyg_uint32 l = len; + int err; + + // Adjust size to end of file if necessary + if (l > node->dentry.size-pos) + l = node->dentry.size-pos; + + err = fatfs_read_data(disk, &node->dentry, &fd->pos, buf, &l); + if (err != ENOERR) + return err; + + // Update working vars + + len -= l; + buf += l; + pos += l; + resid -= l; + } + } + + // We successfully read some data, update the access time, + // file offset and transfer residue + + node->dentry.atime = cyg_timestamp(); + uio->uio_resid = resid; + fp->f_offset = (off_t) pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_write() +// Write data to file. + +static int +fatfs_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) fp->f_mte->data; + fatfs_fd_t *fd = (fatfs_fd_t *) fp->f_data; + fatfs_node_t *node = fd->node; + cyg_uint32 pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + int err = ENOERR; + int i; + + CYG_TRACE3(TFO, "write fp=%p uio=%p pos=%d", fp, uio, pos); + + // If the APPEND mode bit was supplied, force all writes to + // the end of the file + if (fp->f_flag & CYG_FAPPEND) + { + fatfs_setpos(disk, &node->dentry, &fd->pos, node->dentry.size); + pos = fp->f_offset = node->dentry.size; + } + + // Check that pos is within current file size, or at the very end + if (pos < 0 || pos > node->dentry.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_uint32 l = len; + + err = fatfs_write_data(disk, &node->dentry, &fd->pos, buf, &l); + + // Update working vars + + len -= l; + buf += l; + pos += l; + resid -= l; + + // Stop writing if there is no more space in the file + if (err == ENOSPC) + break; + + if (err != ENOERR) + return err; + } + } + + // 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->dentry.mtime = + node->dentry.atime = cyg_timestamp(); + + if (pos > node->dentry.size) + node->dentry.size = pos; + + uio->uio_resid = resid; + fp->f_offset = (off_t) pos; + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_lseek() +// Seek to a new file position. + +static int +fatfs_fo_lseek(struct CYG_FILE_TAG *fp, off_t *apos, int whence) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) fp->f_mte->data; + fatfs_fd_t *fd = (fatfs_fd_t *) fp->f_data; + off_t pos = *apos; + int err; + + CYG_TRACE3(TFO, "lseek fp=%p pos=%d whence=%d", fp, fp->f_offset, whence); + + 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 += fd->node->dentry.size; + break; + default: + return EINVAL; + } + + // Check that pos is still within current file size, + // or at the very end + if (pos < 0 || pos > fd->node->dentry.size) + return EINVAL; + + // All OK, set fp offset and return new position + + err = fatfs_setpos(disk, &fd->node->dentry, &fd->pos, pos); + + if (ENOERR == err) + *apos = fp->f_offset = pos; + + CYG_TRACE2(TFO, "lseek fp=%p new pos=%d", fp, *apos); + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_ioctl() +// Handle ioctls. Currently none are defined. + +static int +fatfs_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, CYG_ADDRWORD data) +{ + CYG_TRACE3(TFO, "ioctl fp=%p com=%x data=%x", fp, com, data); + return EINVAL; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_fsync(). +// Force the file out to data storage. + +static int +fatfs_fo_fsync(struct CYG_FILE_TAG *fp, int mode) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) fp->f_mte->data; + fatfs_fd_t *fd = (fatfs_fd_t *) fp->f_data; + fatfs_node_t *node = fd->node; + int err; + + CYG_TRACE2(TFO, "fsync fp=%p mode=%d", fp, mode); + + err = fatfs_write_dir_entry(disk, &node->dentry); + + if (ENOERR == err) + err = cyg_blib_sync(&disk->blib); + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_close() +// Close a file. + +static int +fatfs_fo_close(struct CYG_FILE_TAG *fp) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) fp->f_mte->data; + fatfs_fd_t *fd = (fatfs_fd_t *) fp->f_data; + fatfs_node_t *node = fd->node; + int err = ENOERR; + + CYG_TRACE1(TFO, "close fp=%p", fp); + + // Write file attributes to disk, unreference + // the file node and free its private data + + if (node != disk->root) + err = fatfs_write_dir_entry(disk, &node->dentry); + + fatfs_node_unref(disk, node); + + free_fatfs_fd(fd); + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_fstat() +// Get file status. + +static int +fatfs_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf) +{ + fatfs_fd_t *fd = (fatfs_fd_t *) fp->f_data; + fatfs_node_t *node = fd->node; + + CYG_TRACE2(TFO, "fstat fp=%p buf=%p", fp, buf); + + // Fill in the status + + buf->st_mode = node->dentry.mode; + buf->st_ino = (ino_t) node->dentry.cluster; + buf->st_dev = 0; + buf->st_nlink = 1; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = node->dentry.size; + buf->st_atime = node->dentry.atime; + buf->st_mtime = node->dentry.mtime; + buf->st_ctime = node->dentry.ctime; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_getinfo() +// Get info. + +static int +fatfs_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf, int len) +{ + CYG_TRACE4(TFO, "getinfo fp=%p key=%d buf=%p len=%d", fp, key, buf, len); + return EINVAL; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_setinfo() +// Set info. + +static int +fatfs_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf, int len) +{ + CYG_TRACE4(TFO, "setinfo fp=%p key=%d buf=%p len=%d", fp, key, buf, len); + return EINVAL; +} + +//========================================================================== +// Directory operations + +// ------------------------------------------------------------------------- +// fatfs_fo_dirread() +// Read a single directory entry from a file. + +static int +fatfs_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) fp->f_mte->data; + fatfs_fd_t *fd = (fatfs_fd_t *) fp->f_data; + struct dirent *ent = (struct dirent *) uio->uio_iov[0].iov_base; + char *nbuf = ent->d_name; + off_t len = uio->uio_iov[0].iov_len; + fatfs_dir_entry_t dentry; + int err; + + CYG_TRACE3(TFO, "dirread fp=%p uio=%p pos=%d", fp, uio, fp->f_offset); + + if (len < sizeof(struct dirent)) + return EINVAL; + + err = fatfs_read_dir_entry(disk, &fd->node->dentry, &fd->pos, &dentry); + + if (err != ENOERR) + return (err == EEOF ? ENOERR : err); + + strcpy(nbuf, dentry.filename); +#ifdef CYGPKG_FS_FAT_RET_DIRENT_DTYPE + ent->d_type = dentry.mode; +#endif + fd->node->dentry.atime = cyg_timestamp(); + uio->uio_resid -= sizeof(struct dirent); + fp->f_offset++; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_dirlseek() +// Seek directory to start. + +static int +fatfs_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t *pos, int whence) +{ + fatfs_disk_t *disk = (fatfs_disk_t *) fp->f_mte->data; + fatfs_fd_t *fd = (fatfs_fd_t *) fp->f_data; + int err; + + CYG_TRACE2(TFO, "dirlseek fp=%p whence=%d", fp, whence); + + // Only allow SEEK_SET to zero + + if (whence != SEEK_SET || *pos != 0) + return EINVAL; + + err = fatfs_setpos(disk, &fd->node->dentry, &fd->pos, 0); + + if (ENOERR == err) + *pos = fp->f_offset = 0; + + return err; +} + +// ------------------------------------------------------------------------- +// EOF fatfs.c diff --git a/ecos/packages/fs/fat/current/src/fatfs.h b/ecos/packages/fs/fat/current/src/fatfs.h new file mode 100644 index 0000000..2ff4000 --- /dev/null +++ b/ecos/packages/fs/fat/current/src/fatfs.h @@ -0,0 +1,307 @@ +#ifndef CYGONCE_FATFS_FATFS_H +#define CYGONCE_FATFS_FATFS_H +//========================================================================== +// +// fatfs.h +// +// FAT file system header +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Savin Zlobec <savin@elatec.si> +// Date: 2003-06-29 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/fs_fat.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/io/io.h> +#include <cyg/fileio/fileio.h> +#include <blib/blib.h> + +#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> + +// -------------------------------------------------------------------------- + +#define FATFS_HASH_TABLE_SIZE CYGNUM_FS_FAT_NODE_HASH_TABLE_SIZE + +#define FATFS_NODE_POOL_SIZE CYGNUM_FS_FAT_NODE_POOL_SIZE + +#ifdef CYGDBG_FS_FAT_NODE_CACHE_EXTRA_CHECKS +# define FATFS_NODE_CACHE_EXTRA_CHECKS 1 +#endif + +// -------------------------------------------------------------------------- + +// Node cache tracing support +//#define FATFS_TRACE_NODE_CACHE 1 + +// FAT dir entry operations tracing support +//#define FATFS_TRACE_DIR_ENTRY 1 + +// FAT clusters operations tracing support +//#define FATFS_TRACE_CLUSTER 1 + +// FAT data tracing support +//#define FATFS_TRACE_DATA 1 + +// FAT file operations tracing support +//#define FATFS_TRACE_FILE_OP 1 + +// FAT filesystem operations tracing support +//#define FATFS_TRACE_FS_OP 1 + +// -------------------------------------------------------------------------- + +typedef enum fatfs_type_e +{ + FATFS_FAT12 = 0, + FATFS_FAT16, + FATFS_FAT32 +} fatfs_type_t; + +typedef struct fatfs_data_pos_s +{ + cyg_uint32 cluster; // Cluster number + cyg_uint32 cluster_snum; // Cluster file seq number + // (0 - first cluster of file, + // 1 - second cluster of file, ...) + cyg_uint32 cluster_pos; // Position inside cluster +} fatfs_data_pos_t; + +typedef struct fatfs_dir_entry_s +{ + char filename[12+1]; // File name + mode_t mode; // Node type + size_t size; // Size of file in bytes + time_t ctime; // Creation timestamp + time_t atime; // Last access timestamp + time_t mtime; // Last write timestamp + cyg_uint8 priv_data; // Private data + cyg_uint32 cluster; // First cluster number + cyg_uint32 parent_cluster; // First cluster of parent dentry + fatfs_data_pos_t disk_pos; // Position of dir entry on disk +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + cyg_fs_attrib_t attrib; // Attribute bits for DOS compatability +#endif //CYGCFG_FS_FAT_USE_ATTRIBUTES +} fatfs_dir_entry_t; + +typedef struct fatfs_node_s +{ + fatfs_dir_entry_t dentry; // Dir entry data + cyg_ucount32 refcnt; // Open file/current dir references + + struct fatfs_node_s *list_prev; // Next node in list + struct fatfs_node_s *list_next; // Prev node in list + struct fatfs_node_s *hash_next; // Next node in hash +} fatfs_node_t; + +typedef struct fatfs_hash_table_s +{ + cyg_uint32 size; // Number of slots + cyg_uint32 n; // Number of nodes + fatfs_node_t *nodes[FATFS_HASH_TABLE_SIZE]; // Nodes slots +} fatfs_hash_table_t; + +typedef struct fatfs_node_list_s +{ + cyg_uint32 size; // Number of nodes in list + fatfs_node_t *first; // First node in list + fatfs_node_t *last; // Last node in list +} fatfs_node_list_t; + +typedef struct fatfs_disk_s +{ + cyg_uint32 sector_size; // Sector size in bytes + cyg_uint32 sector_size_log2; // Sector size log2 + cyg_uint32 cluster_size; // Cluster size in bytes + cyg_uint32 cluster_size_log2; // Cluster size log2 + cyg_uint32 fat_tbl_pos; // Position of the first FAT table + cyg_uint32 fat_tbl_size; // FAT table size in bytes + cyg_uint32 fat_tbl_nents; // Number of entries in FAT table + cyg_uint32 fat_tbls_num; // Number of FAT tables + cyg_uint32 fat_root_dir_pos; // Position of the root dir + cyg_uint32 fat_root_dir_size; // Root dir size in bytes + cyg_uint32 fat_root_dir_nents; // Max number of entries in root dir + cyg_uint32 fat_root_dir_cluster; // Cluster number of root dir (FAT32) + cyg_uint32 fat_data_pos; // Position of data area + fatfs_type_t fat_type; // Type of FAT - 12, 16 or 32 + + cyg_io_handle_t dev_h; // Disk device handle + fatfs_node_t *root; // Root dir node + + cyg_uint8 *bcache_mem; // Block cache memory base + cyg_blib_t blib; // Block cache and access library instance + + fatfs_node_t node_pool_base[FATFS_NODE_POOL_SIZE]; // Node pool base + fatfs_node_t *node_pool[FATFS_NODE_POOL_SIZE]; // Node pool + cyg_uint32 node_pool_free_cnt; // Node pool free cnt + + fatfs_node_list_t live_nlist; // List of nodes with refcnt > 0 + fatfs_node_list_t dead_nlist; // List of nodes with refcnt == 0 + fatfs_hash_table_t node_hash; // Hash of nodes in live and dead lists +} fatfs_disk_t; + +// -------------------------------------------------------------------------- + +int fatfs_init(fatfs_disk_t *disk); + +void fatfs_get_root_dir_entry(fatfs_disk_t *disk, fatfs_dir_entry_t *dentry); + +bool fatfs_is_root_dir_dentry(fatfs_dir_entry_t *dentry); + +int fatfs_get_disk_usage(fatfs_disk_t *disk, + cyg_uint32 *total_clusters, + cyg_uint32 *free_clusters); + +int fatfs_initpos(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos); + +int fatfs_setpos(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos, + cyg_uint32 offset); + +cyg_uint32 fatfs_getpos(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos); + +int fatfs_read_dir_entry(fatfs_disk_t *disk, + fatfs_dir_entry_t *dir, + fatfs_data_pos_t *pos, + fatfs_dir_entry_t *dentry); + +int fatfs_write_dir_entry(fatfs_disk_t *disk, fatfs_dir_entry_t *dentry); + +int fatfs_delete_file(fatfs_disk_t *disk, fatfs_dir_entry_t *file); + +int fatfs_create_file(fatfs_disk_t *disk, + fatfs_dir_entry_t *dir, + const char *name, + int namelen, + fatfs_dir_entry_t *dentry); + +int fatfs_create_dir(fatfs_disk_t *disk, + fatfs_dir_entry_t *dir, + const char *name, + int namelen, + fatfs_dir_entry_t *dentry); + +int fatfs_trunc_file(fatfs_disk_t *disk, fatfs_dir_entry_t *file); + +int fatfs_rename_file(fatfs_disk_t *disk, + fatfs_dir_entry_t *dir1, + fatfs_dir_entry_t *target, + fatfs_dir_entry_t *dir2, + const char *name, + int namelen); + +int fatfs_read_data(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos, + void *data, + cyg_uint32 *len); + +int fatfs_write_data(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos, + void *data, + cyg_uint32 *len); + +// -------------------------------------------------------------------------- + +void fatfs_node_cache_init(fatfs_disk_t *disk); + +void fatfs_node_cache_flush(fatfs_disk_t *disk); + +fatfs_node_t *fatfs_node_alloc(fatfs_disk_t *disk, fatfs_dir_entry_t *dentry); + +void fatfs_node_ref(fatfs_disk_t *disk, fatfs_node_t *node); + +void fatfs_node_unref(fatfs_disk_t *disk, fatfs_node_t *node); + +void fatfs_node_touch(fatfs_disk_t *disk, fatfs_node_t *node); + +void fatfs_node_rehash(fatfs_disk_t *disk, fatfs_node_t *node); + +void fatfs_node_free(fatfs_disk_t *disk, fatfs_node_t *node); + +fatfs_node_t* fatfs_node_find(fatfs_disk_t *disk, + const char *name, + unsigned int namelen, + cyg_uint32 parent_cluster); + +int fatfs_get_live_node_count(fatfs_disk_t *disk); + +int fatfs_get_dead_node_count(fatfs_disk_t *disk); + +// -------------------------------------------------------------------------- +// Support routines +// These enable the definition of local versions of certain routines +// if the given packages are not present. + +#ifndef CYGPKG_LIBC_I18N + +__externC int toupper( int c ); + +#endif + +#ifndef CYGFUN_LIBC_STRING_BSD_FUNCS + +__externC int strcasecmp( const char *s1, const char *s2 ); + +__externC int strncasecmp( const char *s1, const char *s2, size_t n ); + +#endif + +// -------------------------------------------------------------------------- + +#endif // CYGONCE_FATFS_FATFS_H + +// -------------------------------------------------------------------------- +// EOF fatfs.h diff --git a/ecos/packages/fs/fat/current/src/fatfs_ncache.c b/ecos/packages/fs/fat/current/src/fatfs_ncache.c new file mode 100644 index 0000000..b82f2b4 --- /dev/null +++ b/ecos/packages/fs/fat/current/src/fatfs_ncache.c @@ -0,0 +1,898 @@ +//========================================================================== +// +// fatfs_ncache.c +// +// FAT file system node cache functions +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Savin Zlobec <savin@elatec.si> +// Date: 2003-06-26 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/fs_fat.h> +#include <pkgconf/infra.h> +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/cyg_trac.h> +#include <cyg/infra/diag.h> +#include <sys/types.h> +#include <ctype.h> + +#include "fatfs.h" + +//========================================================================== +// Defines & macros + +#ifdef CYGDBG_USE_ASSERTS +# define USE_ASSERTS 1 +#endif + +#ifdef FATFS_NODE_CACHE_EXTRA_CHECKS +# define USE_XCHECKS 1 +#endif + +#ifdef FATFS_TRACE_NODE_CACHE +# define TNC 1 +#else +# define TNC 0 +#endif + +// This defines how many nodes should always be kept in dead list - +// it should be >= 2 or the file finding code may not work correctly! +#define DLIST_KEEP_NUM 2 + +//========================================================================== +// Node list functions + +static void +node_list_init(fatfs_node_list_t *list) +{ + list->size = 0; + list->first = list->last = NULL; +} + +static __inline__ int +node_list_get_size(fatfs_node_list_t *list) +{ + return (list->size); +} + +static __inline__ fatfs_node_t* +node_list_get_head(fatfs_node_list_t *list) +{ + return (list->first); +} + +static __inline__ fatfs_node_t* +node_list_get_tail(fatfs_node_list_t *list) +{ + return (list->last); +} + +static __inline__ fatfs_node_t* +node_list_get_next(fatfs_node_t *node) +{ + return (node->list_next); +} + +static __inline__ fatfs_node_t* +node_list_get_prev(fatfs_node_t *node) +{ + return (node->list_prev); +} + +static __inline__ bool +node_list_is_last(fatfs_node_t *node) +{ + return (NULL == node->list_next); +} + +static __inline__ bool +node_list_is_first(fatfs_node_t *node) +{ + return (NULL == node->list_prev); +} + +static void +node_list_head_add(fatfs_node_list_t *list, fatfs_node_t *node) +{ + node->list_prev = NULL; + node->list_next = list->first; + + if (NULL == list->first) + { + CYG_ASSERTC(list->size == 0); + list->last = node; + } + else + { + list->first->list_prev = node; + } + + list->first = node; + list->size++; +} + +#if 0 +static __inline__ fatfs_node_t* +node_list_head_get(fatfs_node_list_t *list) +{ + return list->first; +} + +static void +node_list_tail_add(fatfs_node_list_t *list, fatfs_node_t *node) +{ + node->list_prev = list->last; + node->list_next = NULL; + + if (NULL == list->last) + { + CYG_ASSERTC(list->size == 0); + list->first = node; + } + else + { + list->last->list_next = node; + } + + list->last = node; + list->size++; +} +#endif + +static __inline__ fatfs_node_t* +node_list_tail_get(fatfs_node_list_t *list) +{ + return list->last; +} + +static void +node_list_remove(fatfs_node_list_t *list, fatfs_node_t *node) +{ + if (list->first == list->last) + { + if (list->first == node) + { + CYG_ASSERTC(list->size == 1); + list->first = list->last = NULL; + } + else + { + CYG_ASSERT(false, "chain node not in list!"); + } + } + else if (list->first == node) + { + CYG_ASSERTC(node->list_prev == NULL); + list->first = node->list_next; + list->first->list_prev = NULL; + } + else if (list->last == node) + { + CYG_ASSERTC(node->list_next == NULL); + list->last = node->list_prev; + list->last->list_next = NULL; + } + else + { + CYG_ASSERTC(node->list_prev != NULL && node->list_next != NULL); + node->list_prev->list_next = node->list_next; + node->list_next->list_prev = node->list_prev; + } + list->size--; +} + +#ifdef USE_XCHECKS +static void +node_list_check(fatfs_node_list_t *list, int min_ref, int max_ref) +{ + int i; + fatfs_node_t *node, *pnode; + + CYG_ASSERT((list->last == NULL && list->first == NULL) || + (list->last != NULL && list->first != NULL), + "list->first and list->last broken!"); + + if (list->first == NULL) + { + CYG_ASSERTC(list->size == 0); + return; + } + + CYG_ASSERTC(list->first->list_prev == NULL); + CYG_ASSERTC(list->last->list_next == NULL); + + CYG_ASSERTC(list->first->refcnt >= min_ref && + list->first->refcnt <= max_ref); + CYG_ASSERTC(list->last->refcnt >= min_ref && + list->last->refcnt <= max_ref); + + i = 1; + node = list->first; + pnode = NULL; + while (node->list_next != NULL) + { + i++; + CYG_ASSERTC(node->list_prev == pnode); + CYG_ASSERTC(node->refcnt >= min_ref && node->refcnt <= max_ref); + pnode = node; + node = node->list_next; + } + CYG_ASSERTC(node->list_prev == pnode); + CYG_ASSERTC(list->size == i); + CYG_ASSERTC(node == list->last); +} + +static void +node_lists_check(fatfs_disk_t* disk) +{ + node_list_check(&disk->live_nlist, 1, 99999); + node_list_check(&disk->dead_nlist, 0, 0); +} +#endif // USE_XCHECKS + +//========================================================================== +// Node hash functions + +static unsigned int +hash_fn(const char *str, unsigned int strlen) +{ + unsigned int i = 0, val = 0; + + while (i++ < strlen) + val = (val ^ (int)toupper(*str++)) << 1; + return(val); +} + +static void +node_hash_init(fatfs_hash_table_t *tbl) +{ + int i; + + // Set size and clear all slots + tbl->size = FATFS_HASH_TABLE_SIZE; + for (i = 0; i < tbl->size; i++) + tbl->nodes[i] = NULL; + tbl->n = 0; +} + +static bool +node_hash_add(fatfs_hash_table_t *tbl, fatfs_node_t *node) +{ + unsigned int hval; + + // Calculate hash of given node filename + hval = hash_fn(node->dentry.filename, + strlen(node->dentry.filename)) % tbl->size; + + CYG_TRACE2(TNC, "name='%s' hval=%d", node->dentry.filename, hval); + + if (tbl->nodes[hval] == NULL) + { + // First node in this slot + node->hash_next = NULL; + tbl->nodes[hval] = node; + tbl->n++; + return true; + } + else + { + // More nodes in this slot + fatfs_node_t *lnode, *pnode; + + pnode = NULL; + lnode = tbl->nodes[hval]; + + // Insert node into list so that it is sorted by filename + while (lnode != NULL) + { + if (lnode == node) + return false; + + if (strcasecmp(lnode->dentry.filename, node->dentry.filename) > 0) + { + if (pnode != NULL) + pnode->hash_next = node; // Insert in the middle + else + tbl->nodes[hval] = node; // Insert at the beginning + node->hash_next = lnode; + tbl->n++; + return true; + } + + pnode = lnode; + lnode = lnode->hash_next; + } + // Insert at the end + pnode->hash_next = node; + node->hash_next = NULL; + tbl->n++; + return true; + } +} + +static fatfs_node_t* +node_hash_find(fatfs_hash_table_t *tbl, + const char *name, + unsigned int namelen, + unsigned int parent_cluster) +{ + unsigned int hval; + fatfs_node_t *node; + + // Calculate hash of name and get the first node in slot + hval = hash_fn(name, namelen) % tbl->size; + node = tbl->nodes[hval]; + + CYG_TRACE2(TNC, "name='%s' hval=%d\n", name, hval); + + // Find the node in list wich matches the + // given name and parent_cluster + while (node != NULL) + { + // First compare the parent cluster number and + // check filename length since it is faster than + // comparing filenames + if (parent_cluster == node->dentry.parent_cluster && + '\0' == node->dentry.filename[namelen]) + { + int i = strncasecmp(node->dentry.filename, name, namelen); + + if (i == 0) + return node; + else if (i > 0) + return NULL; // Stop searching - we have a + // sorted list so there can't be + // any matching filename further on + // if i > 0 - look at node_hash_add + } + node = node->hash_next; + } + + // No such node found + return NULL; +} + +static bool +node_hash_remove_keyless(fatfs_hash_table_t *tbl, fatfs_node_t *node) +{ + int i; + fatfs_node_t *lnode, *pnode; + + // Look in all slots, since we have no key + for (i = 0; i < tbl->size; i++) + { + // Look in list and remove it if there + lnode = tbl->nodes[i]; + pnode = NULL; + while (lnode != NULL) + { + if (lnode == node) + { + if (pnode == NULL) + tbl->nodes[i] = lnode->hash_next; + else + pnode->hash_next = lnode->hash_next; + node->hash_next = NULL; + tbl->n--; + + // All done + return true; + } + pnode = lnode; + lnode = lnode->hash_next; + } + } + return false; +} + +static bool +node_hash_remove(fatfs_hash_table_t *tbl, fatfs_node_t *node) +{ + unsigned int hval; + fatfs_node_t *lnode, *pnode; + + // Calculate hash of name and get the first node in slot + hval = hash_fn(node->dentry.filename, + strlen(node->dentry.filename)) % tbl->size; + lnode = tbl->nodes[hval]; + + // Now find the node in list and remove it + pnode = NULL; + while (lnode != NULL) + { + if (lnode == node) + { + if (pnode == NULL) + tbl->nodes[hval] = lnode->hash_next; + else + pnode->hash_next = lnode->hash_next; + node->hash_next = NULL; + tbl->n--; + + // All done + return true; + } + pnode = lnode; + lnode = lnode->hash_next; + } + + // Node not in list + return false; +} + +#ifdef USE_XCHECKS +static void +node_hash_check(fatfs_hash_table_t *tbl) +{ + int i, n; + + n = 0; + for (i = 0; i < tbl->size; i++) + { + fatfs_node_t *lnode, *pnode; + + pnode = NULL; + lnode = tbl->nodes[i]; + + while (lnode != NULL) + { + if (pnode != NULL) + { + int c = strcasecmp(pnode->dentry.filename, lnode->dentry.filename); + CYG_ASSERT(c <= 0, "hash table not sorted"); + CYG_ASSERT(pnode->dentry.parent_cluster != lnode->dentry.parent_cluster || + 0 != c, "duplicated node in hash table"); + } + n++; + pnode = lnode; + lnode = lnode->hash_next; + } + } + CYG_ASSERTC(tbl->n == n); +} + +static void +node_hash_not_found_check(fatfs_disk_t *disk, + const char *name, + unsigned int namelen, + unsigned int parent_cluster) +{ + fatfs_node_t* node; + + node = node_list_get_head(&disk->live_nlist); + while (NULL != node) + { + if (node->dentry.parent_cluster == parent_cluster && + namelen == strlen(node->dentry.filename) && + 0 == strncasecmp(name, node->dentry.filename, namelen)) + CYG_ASSERT(false, "node not found in hash, " + "but exists in live list"); + node = node_list_get_next(node); + } + + node = node_list_get_head(&disk->dead_nlist); + while (NULL != node) + { + if (node->dentry.parent_cluster == parent_cluster && + namelen == strlen(node->dentry.filename) && + 0 == strncasecmp(name, node->dentry.filename, namelen)) + CYG_ASSERT(false, "node not found in hash, " + "but exists in dead list"); + node = node_list_get_next(node); + } +} + +static void +node_hash_found_check(fatfs_disk_t *disk, + const char *name, + unsigned int namelen, + unsigned int parent_cluster, + fatfs_node_t* node) +{ + fatfs_node_t *n; + + n = node_list_get_head(&disk->live_nlist); + while (NULL != n) + { + if (n == node) + { + if (node->dentry.parent_cluster != parent_cluster || + namelen != strlen(node->dentry.filename) || + 0 != strncasecmp(name, node->dentry.filename, namelen)) + CYG_ASSERT(false, "node_hash_find returned wrong node"); + return; + } + n = node_list_get_next(n); + } + + n = node_list_get_head(&disk->dead_nlist); + while (NULL != n) + { + if (n == node) + { + if (node->dentry.parent_cluster != parent_cluster || + namelen != strlen(node->dentry.filename) || + 0 != strncasecmp(name, node->dentry.filename, namelen)) + CYG_ASSERT(false, "node_hash_find returned wrong node"); + return; + } + n = node_list_get_next(n); + } + + CYG_ASSERT(false, "node not found in dead or live lists, " + "but exists in hash."); +} +#endif // USE_XCHECKS + +//-------------------------------------------------------------------------- + +#ifdef USE_XCHECKS + +# define SANITY_CHECK() \ + CYG_MACRO_START \ + node_lists_check(disk); \ + node_hash_check(&disk->node_hash); \ + CYG_ASSERTC((disk->live_nlist.size + disk->dead_nlist.size) == \ + disk->node_hash.n); \ + CYG_MACRO_END + +# define NODE_FIND_CHECK() \ + CYG_MACRO_START \ + if (NULL == node) \ + node_hash_not_found_check(disk, name, namelen, parent_cluster); \ + else \ + node_hash_found_check(disk, name, namelen, parent_cluster, node); \ + CYG_MACRO_END + +#else // USE_XCHECKS +# define SANITY_CHECK() CYG_EMPTY_STATEMENT +# define NODE_FIND_CHECK() CYG_EMPTY_STATEMENT +#endif // not USE_XCHECKS + +//========================================================================== +// Node pool allocation functions + +static void +node_pool_init(fatfs_disk_t *disk) +{ + int i; + + for (i = 0; i < FATFS_NODE_POOL_SIZE; i++) + disk->node_pool[i] = &disk->node_pool_base[i]; + + disk->node_pool_free_cnt = i; +} + +static fatfs_node_t * +node_pool_alloc(fatfs_disk_t *disk) +{ + fatfs_node_t *node = NULL; + + if (disk->node_pool_free_cnt > 0) + node = disk->node_pool[--disk->node_pool_free_cnt]; + + return node; +} + +static void +node_pool_free(fatfs_disk_t *disk, fatfs_node_t *node) +{ + disk->node_pool[disk->node_pool_free_cnt++] = node; +} + +//========================================================================== +//========================================================================== +// Exported functions + +//-------------------------------------------------------------------------- +// fatfs_node_cache_init() +// Initializes node cache of given disk. + +void +fatfs_node_cache_init(fatfs_disk_t *disk) +{ + CYG_CHECK_DATA_PTRC(disk); + + node_list_init(&disk->live_nlist); + node_list_init(&disk->dead_nlist); + node_hash_init(&disk->node_hash); + node_pool_init(disk); + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_cache_flush() +// Frees all node cache of given disk. + +void +fatfs_node_cache_flush(fatfs_disk_t *disk) +{ + fatfs_node_t *node, *next_node; + + node = node_list_get_head(&disk->live_nlist); + + while (NULL != node) + { + next_node = node_list_get_next(node); + node_list_remove(&disk->live_nlist, node); + if (!node_hash_remove(&disk->node_hash, node)) + CYG_ASSERT(false, "Node not in hash"); + node_pool_free(disk, node); + node = next_node; + } + + node = node_list_get_head(&disk->dead_nlist); + + while (NULL != node) + { + next_node = node_list_get_next(node); + node_list_remove(&disk->dead_nlist, node); + if (!node_hash_remove(&disk->node_hash, node)) + CYG_ASSERT(false, "Node not in hash"); + node_pool_free(disk, node); + node = next_node; + } + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_alloc() +// Allocates a new node. + +fatfs_node_t* +fatfs_node_alloc(fatfs_disk_t *disk, fatfs_dir_entry_t *dentry) +{ + fatfs_node_t *node; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dentry); + + node = node_pool_alloc(disk); + + if (NULL == node) + { + CYG_TRACE2(TNC, "getting node from dead list (size=%d keep=%d)", + node_list_get_size(&disk->dead_nlist), DLIST_KEEP_NUM); + + if (node_list_get_size(&disk->dead_nlist) <= DLIST_KEEP_NUM) + return NULL; + + node = node_list_tail_get(&disk->dead_nlist); + if (NULL == node) + return NULL; + + CYG_TRACE1(TNC, "recycling node='%s'", node->dentry.filename); + + node_list_remove(&disk->dead_nlist, node); + if (!node_hash_remove(&disk->node_hash, node)) + CYG_ASSERT(false, "node not in hash"); + } + + // Init new node + node->dentry = *dentry; + node->refcnt = 0; + + node_list_head_add(&disk->dead_nlist, node); + if (!node_hash_add(&disk->node_hash, node)) + CYG_ASSERT(false, "node already in hash"); + + SANITY_CHECK(); + + return node; +} + +//-------------------------------------------------------------------------- +// fatfs_node_touch() +// Moves given node to top of its list. +// (When reusing dead node it is always taken from the bottom of list) + +void +fatfs_node_touch(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->dentry.filename, node->refcnt); + + if (node->refcnt == 0) + { + node_list_remove(&disk->dead_nlist, node); + node_list_head_add(&disk->dead_nlist, node); + } + else + { + node_list_remove(&disk->live_nlist, node); + node_list_head_add(&disk->live_nlist, node); + } + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_ref() +// Increases the given node reference count. +// If the reference goes from 0 to 1, than the node +// is moved from dead list to live list. + +void +fatfs_node_ref(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->dentry.filename, node->refcnt); + + node->refcnt++; + if (1 == node->refcnt) + { + // First reference - move node from dead to live list + + CYG_TRACE1(TNC, "node='%s' to live list", node->dentry.filename); + + node_list_remove(&disk->dead_nlist, node); + node_list_head_add(&disk->live_nlist, node); + } + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_unref() +// Decreases the given node reference count. +// If the reference goes from 1 to 0, than the node +// is moved from live list to top of dead list. +// (When reusing dead node it is always taken from the bottom of list) + +void +fatfs_node_unref(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->dentry.filename, node->refcnt); + CYG_ASSERT(node->refcnt > 0, "node->refcnt <= 0"); + + node->refcnt--; + if (0 == node->refcnt) + { + // No more references - move node from live to dead list + + CYG_TRACE1(TNC, "node='%s' to dead list", node->dentry.filename); + + node_list_remove(&disk->live_nlist, node); + node_list_head_add(&disk->dead_nlist, node); + } + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_free() +// Frees node memory and removes it from its list and hash. +// This function must not be called on a referenced node. + +void +fatfs_node_free(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->dentry.filename, node->refcnt); + CYG_ASSERTC(node->refcnt == 0); + CYG_ASSERTC(node != disk->root); + + // Remove from dead list, from hash and free ptr + + node_list_remove(&disk->dead_nlist, node); + if (!node_hash_remove(&disk->node_hash, node)) + CYG_ASSERT(false, "node not in hash"); + + node_pool_free(disk, node); + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_rehash() +// Recalculates given node hash key. + +void +fatfs_node_rehash(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + + if (!node_hash_remove_keyless(&disk->node_hash, node)) + CYG_ASSERT(false, "node not in hash"); + + if (!node_hash_add(&disk->node_hash, node)) + CYG_ASSERT(false, "node already in hash"); + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_find() +// Finds node in hash by its name and parent cluster. +// If no node found NULL is returned. + +fatfs_node_t* +fatfs_node_find(fatfs_disk_t *disk, + const char *name, + unsigned int namelen, + unsigned int parent_cluster) +{ + fatfs_node_t *node; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(name); + + node = node_hash_find(&disk->node_hash, name, namelen, parent_cluster); + + NODE_FIND_CHECK(); + + CYG_TRACE3(TNC, "node name=%s pcluster=%d %s found in cache\n", + name, parent_cluster, ((node != NULL) ? "" : "not")); + + return node; +} + +//-------------------------------------------------------------------------- +// fatfs_get_live_node_count() +// Gets the number of live nodes. + +int +fatfs_get_live_node_count(fatfs_disk_t *disk) +{ + return node_list_get_size(&disk->live_nlist); +} + +//-------------------------------------------------------------------------- +// fatfs_get_dead_node_count() +// Gets the number of dead nodes. + +int +fatfs_get_dead_node_count(fatfs_disk_t *disk) +{ + return node_list_get_size(&disk->dead_nlist); +} + +// ------------------------------------------------------------------------- +// EOF fatfs_ncache.c diff --git a/ecos/packages/fs/fat/current/src/fatfs_supp.c b/ecos/packages/fs/fat/current/src/fatfs_supp.c new file mode 100644 index 0000000..7c1c2a4 --- /dev/null +++ b/ecos/packages/fs/fat/current/src/fatfs_supp.c @@ -0,0 +1,2589 @@ +//========================================================================== +// +// fatfs_supp.c +// +// FAT file system support functions +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Savin Zlobec <savin@elatec.si> +// Date: 2003-06-30 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/fs_fat.h> +#include <pkgconf/infra.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/cyg_trac.h> +#include <cyg/infra/diag.h> +#include <cyg/io/io.h> +#include <cyg/fs/fatfs.h> +#include <blib/blib.h> + +#include <sys/types.h> +#include <ctype.h> + +#include "fatfs.h" + +//========================================================================== +// FAT defines & macros + +// ------------------------------------------------------------------------- +// FAT dir entry attributes macros + +#define DENTRY_IS_RDONLY(_dentry_) (S_FATFS_ISRDONLY((_dentry_)->attr)) +#define DENTRY_IS_HIDDEN(_dentry_) (S_FATFS_ISHIDDEN((_dentry_)->attr)) +#define DENTRY_IS_SYSTEM(_dentry_) (S_FATFS_ISSYSTEM((_dentry_)->attr)) +#define DENTRY_IS_VOLUME(_dentry_) (S_FATFS_ISVOLUME((_dentry_)->attr)) +#define DENTRY_IS_DIR(_dentry_) (S_FATFS_ISDIR((_dentry_)->attr)) +#define DENTRY_IS_ARCHIVE(_dentry_) (S_FATFS_ISARCHIVE((_dentry_)->attr)) + +#define DENTRY_IS_DELETED(_dentry_) \ + (0xE5 == (cyg_uint8)((_dentry_)->name[0])) + +#define DENTRY_IS_ZERO(_dentry_) \ + (0x00 == (cyg_uint8)((_dentry_)->name[0])) + +// ------------------------------------------------------------------------- +// FAT disk data access macros + +// FIXME: support big endian machines! + +#define GET_BYTE(_data_, _var_, _off_) \ + (_var_ = *( ((cyg_uint8 *)_data_) + (_off_) ) ) + +#define GET_WORD(_data_, _var_, _off_) \ + (_var_ = *( ((cyg_uint8 *)_data_) + (_off_) ) | \ + *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) << 8) + +#define GET_DWORD(_data_, _var_, _off_) \ + (_var_ = *( ((cyg_uint8 *)_data_) + (_off_)) | \ + *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) << 8 | \ + *( ((cyg_uint8 *)_data_) + (_off_) + 2 ) << 16 | \ + *( ((cyg_uint8 *)_data_) + (_off_) + 3 ) << 24) + +#define GET_BYTES(_data_, _var_, _size_, _off_) \ + memcpy((void *)(_var_), (void*)(((cyg_uint8 *)_data_)+(_off_)),_size_) + +#define SET_BYTE(_data_, _val_, _off_) \ + (*( ((cyg_uint8 *)_data_) + (_off_) ) = _val_) + +#define SET_WORD(_data_, _val_, _off_) \ + do { \ + *( ((cyg_uint8 *)_data_) + (_off_) ) = _val_ & 0xFF; \ + *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) = (_val_ >> 8) & 0xFF; \ + } while (0) + +#define SET_DWORD(_data_, _val_, _off_) \ + do { \ + *( ((cyg_uint8 *)_data_) + (_off_) ) = _val_ & 0xFF; \ + *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) = (_val_ >> 8) & 0xFF; \ + *( ((cyg_uint8 *)_data_) + (_off_) + 2 ) = (_val_ >> 16) & 0xFF; \ + *( ((cyg_uint8 *)_data_) + (_off_) + 3 ) = (_val_ >> 24) & 0xFF; \ + } while (0) + +#define SET_BYTES(_data_, _var_, _size_, _off_) \ + memcpy((void *)(((cyg_uint8 *)_data_)+(_off_)), (void *)(_var_), _size_) + +// ------------------------------------------------------------------------- +// FAT table entries types + +#define TENTRY_REGULAR 0 // Used when entry points to next file cluster +#define TENTRY_FREE 1 // Free cluster +#define TENTRY_LAST 2 // Last cluster of file +#define TENTRY_RESERVED 3 // Reserved cluster +#define TENTRY_BAD 4 // Bad cluster + +// ------------------------------------------------------------------------- +// FAT table structures size + +#define DENTRY_SIZE 0x20 // Dir entry size + +// ------------------------------------------------------------------------- +// Time & date defines + +#define JD_1_JAN_1970 2440588 // 1 Jan 1970 in Julian day number + +// ------------------------------------------------------------------------- +// Code tracing defines + +#ifdef FATFS_TRACE_DIR_ENTRY +# define TDE 1 +#else +# define TDE 0 +#endif + +#ifdef FATFS_TRACE_CLUSTER +# define TCL 1 +#else +# define TCL 0 +#endif + +#ifdef FATFS_TRACE_DATA +# define TDO 1 +#else +# define TDO 0 +#endif + +//========================================================================== +// FAT structures + +// ------------------------------------------------------------------------- +// FAT table boot record structure + +typedef struct fat_boot_record_s +{ + cyg_uint16 jump; // 00h : Jump code +// cyg_uint8 jump0; // + NOP + char oem_name[8+1]; // 03h : OEM name + cyg_uint16 bytes_per_sec; // 0Bh : cyg_bytes per sector + cyg_uint8 sec_per_clu; // 0Dh : Sectors per cluster + cyg_uint16 res_sec_num; // 0Eh : Number of reserved sectors + cyg_uint8 fat_tbls_num; // 10h : Number of copies of fat + cyg_uint16 max_root_dents; // 11h : Maximum number of root dir entries + cyg_uint16 sec_num_32; // 13h : Number of sectors in partition < 32MB + cyg_uint8 media_desc; // 15h : Media descriptor + cyg_uint16 sec_per_fat; // 16h : Sectors per FAT + cyg_uint16 sec_per_track; // 18h : Sectors per track + cyg_uint16 heads_num; // 1Ah : Number of heads + cyg_uint32 hsec_num; // 1Ch : Number of hidden sectors + cyg_uint32 sec_num; // 20h : Number of sectors in partition + cyg_uint8 exe_marker[2]; // 1FEh: Executable marker (55h AAh) + +// FAT32 specific fields + + cyg_uint32 sec_per_fat_32; // 24h : Sectors per FAT + cyg_uint16 ext_flags; // 28h : Flags + cyg_uint16 fs_ver; // 2Ah : FS version + cyg_uint32 root_cluster; // 2Ch : Root dir cluster + cyg_uint16 fs_info_sec; // 30h : Sector number of FSINFO structure + cyg_uint16 bk_boot_sec; // 32h : Sector number of backup boot record +// cyg_uint8 reserved[12]; // 34h : Reserved + +// Fields with different locations on FAT12/16 and FAT32 + + cyg_uint8 drv_num; // 24h (40h) : Drive number of partition +// cyg_uint8 reserved1; // 25h (41h) : Reserved 1 + cyg_uint8 ext_sig; // 26h (42h) : Extended signature + cyg_uint32 ser_num; // 27h (43h) : Serial number of partition + char vol_name[11+1]; // 2Bh (47h) : Volume name of partition + char fat_name[8+1]; // 36h (52h) : FAT name + +} fat_boot_record_t; + +// ------------------------------------------------------------------------- +// FAT dir entry structure + +typedef struct fat_raw_dir_entry_s +{ + char name[8+1]; // 00h : Name + char ext[3+1]; // 08h : Extension + cyg_uint8 attr; // 0Bh : Attribute + cyg_uint8 nt_reserved; // 0Ch : Win NT Reserved field + cyg_uint8 crt_sec_100; // 0Dh : Creation time ms stamp 0 - 199 + cyg_uint16 crt_time; // 0Eh : Creation time + cyg_uint16 crt_date; // 10h : Creation date + cyg_uint16 acc_date; // 12h : Last access date + cyg_uint16 cluster_HI; // 14h : Starting cluster HI WORD (FAT32) + cyg_uint16 wrt_time; // 16h : Time + cyg_uint16 wrt_date; // 18h : Date + cyg_uint16 cluster; // 1Ah : Starting cluster + cyg_uint32 size; // 1Ch : Size of the file +} fat_raw_dir_entry_t; + +// ------------------------------------------------------------------------- +// FAT cluster opts + +typedef enum cluster_opts_e +{ + CO_NONE = 0x00, // NULL option + CO_EXTEND = 0x01, // Extend cluster chain if one cluster too short + CO_ERASE_NEW = 0x02, // Erase newly allocated cluster + CO_MARK_LAST = 0x04 // Mark newly allocated cluster as last +} cluster_opts_t; + +//========================================================================== +// Utility functions + +// ------------------------------------------------------------------------- +// get_val_log2() +// Gets the log2 of given value or returns 0 if value is not a power of 2. + +static cyg_uint32 +get_val_log2(cyg_uint32 val) +{ + cyg_uint32 i, log2; + + i = val; + log2 = 0; + + while (0 == (i & 1)) + { + i >>= 1; + log2++; + } + + if (i != 1) return 0; + else return log2; +} + +// ------------------------------------------------------------------------- +// cluster_to_block_pos() +// Converts cluster position to blib block position. + +static void +cluster_to_block_pos(fatfs_disk_t *disk, + cyg_uint32 cluster, + cyg_uint32 cluster_pos, + cyg_uint32 *block, + cyg_uint32 *block_pos) +{ + cyg_uint32 block_size = cyg_blib_get_block_size(&disk->blib); + cyg_uint32 block_size_log2 = cyg_blib_get_block_size_log2(&disk->blib); + + *block = (cluster - 2) << (disk->cluster_size_log2 - block_size_log2); + + *block_pos = disk->fat_data_pos + cluster_pos; + + if (*block_pos > block_size) + { + *block += *block_pos >> block_size_log2; + *block_pos = *block_pos & (block_size - 1); + } +} + +// ------------------------------------------------------------------------- +// disk_write() +// Writes data to disk. + +static __inline__ int +disk_write(fatfs_disk_t *disk, + void *buf, + cyg_uint32 *len, + cyg_uint32 pos) +{ + return cyg_blib_write(&disk->blib, buf, len, 0, pos); +} + +// ------------------------------------------------------------------------- +// disk_read() +// Reads data from disk. + +static __inline__ int +disk_read(fatfs_disk_t *disk, + void *buf, + cyg_uint32 *len, + cyg_uint32 pos) +{ + return cyg_blib_read(&disk->blib, buf, len, 0, pos); +} + +// ------------------------------------------------------------------------- +// disk_cluster_write() +// Writes data to disk at specified cluster position. + +static __inline__ int +disk_cluster_write(fatfs_disk_t *disk, + void *buf, + cyg_uint32 *len, + cyg_uint32 cluster, + cyg_uint32 cluster_pos) +{ + cyg_uint32 block, block_pos; + + cluster_to_block_pos(disk, cluster, cluster_pos, &block, &block_pos); + + return cyg_blib_write(&disk->blib, buf, len, block, block_pos); +} + +// ------------------------------------------------------------------------- +// disk_cluster_read() +// Reads data from disk at specified cluster position. + +static __inline__ int +disk_cluster_read(fatfs_disk_t *disk, + void *buf, + cyg_uint32 *len, + cyg_uint32 cluster, + cyg_uint32 cluster_pos) +{ + cyg_uint32 block, block_pos; + + cluster_to_block_pos(disk, cluster, cluster_pos, &block, &block_pos); + + return cyg_blib_read(&disk->blib, buf, len, block, block_pos); +} + +// ------------------------------------------------------------------------- +// jdays_to_gdate() +// Converts juilan days into gregorian date. + +static void +jdays_to_gdate(cyg_uint32 jd, int *day, int *month, int *year) +{ + cyg_uint32 l, n, i, j; + + l = jd + 68569; + n = (4 * l) / 146097; + l = l - (146097 * n + 3) / 4; + i = (4000 * (l + 1)) / 1461001; + l = l - (1461 * i) / 4 + 31; + j = (80 * l) / 2447; + *day = l - (2447 * j) / 80; + + l = j / 11; + *month = j + 2 - (12 * l); + *year = 100 * (n - 49) + i + l; +} + +// ------------------------------------------------------------------------- +// gdate_to_jdays() +// Converts gregorian date to juilan days. + +static void +gdate_to_jdays(int day, int month, int year, cyg_uint32 *jd) +{ + *jd = day - 32075 + 1461*(year + 4800 + (month - 14)/12)/4 + + 367*(month - 2 - (month - 14)/12*12)/12 - + 3*((year + 4900 + (month - 14)/12)/100)/4; +} + +// ------------------------------------------------------------------------- +// date_unix2dos() +// Converts unix timestamp to dos time and date. + +static void +date_unix2dos(time_t unix_timestamp, + cyg_uint16 *dos_time, + cyg_uint16 *dos_date) +{ + cyg_uint32 jd; + cyg_uint16 dtime, ddate; + int hour, min, sec; + int day, month, year; + + hour = (unix_timestamp / 3600) % 24; + min = (unix_timestamp / 60) % 60; + sec = unix_timestamp % 60; + + jd = JD_1_JAN_1970 + unix_timestamp / (3600 * 24); + jdays_to_gdate(jd, &day, &month, &year); + + CYG_TRACE7(TDE, "timestamp=%d date=%d:%d:%d %d-%d-%d", + unix_timestamp, hour, min, sec, year, month, day); + + if (year < 1980) + year = 1980; + + dtime = (hour << 11) | (min << 5) | (sec >> 1); + ddate = ((year - 1980) << 9) | (month << 5) | day; + + CYG_TRACE2(TDE, "dos time=%d date=%d", dtime, ddate); + + if (NULL != dos_time) *dos_time = dtime; + if (NULL != dos_date) *dos_date = ddate; +} + +// ------------------------------------------------------------------------- +// date_dos2unix() +// Converts dos time and date to unix timestamp. + +static void +date_dos2unix(cyg_uint16 dos_time, + cyg_uint16 dos_date, + time_t *unix_timestamp) +{ + cyg_uint32 ts; + int hour, min, sec; + int day, month, year; + + sec = (dos_time & ((1<<5)-1)) * 2; + dos_time >>= 5; + min = (dos_time & ((1<<6)-1)); + dos_time >>= 6; + hour = dos_time; + + day = (dos_date & ((1<<5)-1)); + dos_date >>= 5; + month = (dos_date & ((1<<4)-1)); + dos_date >>= 4; + year = dos_date + 1980; + + gdate_to_jdays(day, month, year, &ts); + + ts -= JD_1_JAN_1970; + ts = (ts * 24 * 3600) + (sec + min * 60 + hour * 3600); + + *unix_timestamp = ts; + + CYG_TRACE2(TDE, "dos time=%d date=%d", dos_time, dos_date); + CYG_TRACE7(TDE, "timestamp=%d date=%d:%d:%d %d-%d-%d", + ts, hour, min, sec, year, month, day); +} + +//========================================================================== +// FAT boot record functions + +#if TDE + +// ------------------------------------------------------------------------- +// print_boot_record() +// Prints FAT boot record. + +static void +print_boot_record(fat_boot_record_t* fbr) +{ + diag_printf("FAT: FBR jump code: 0x%02X\n", fbr->jump); + diag_printf("FAT: FBR oem name: '%.8s'\n", fbr->oem_name); + diag_printf("FAT: FBR bytes per sec: %u\n", fbr->bytes_per_sec); + diag_printf("FAT: FBR sec per cluster: %u\n", fbr->sec_per_clu); + diag_printf("FAT: FBR reserved sec: %u\n", fbr->res_sec_num); + diag_printf("FAT: FBR fat tbls num: %u\n", fbr->fat_tbls_num); + diag_printf("FAT: FBR max root dents: %u\n", fbr->max_root_dents); + diag_printf("FAT: FBR sec num (32): %u\n", fbr->sec_num_32); + diag_printf("FAT: FBR media desc: 0x%02X\n", fbr->media_desc); + diag_printf("FAT: FBR sec per fat: %u\n", fbr->sec_per_fat); + diag_printf("FAT: FBR sec per track: %u\n", fbr->sec_per_track); + diag_printf("FAT: FBR heads num: %u\n", fbr->heads_num); + diag_printf("FAT: FBR hidden sec num: %u\n", fbr->hsec_num); + diag_printf("FAT: FBR sec num: %u\n", fbr->sec_num); + + if (0 == fbr->sec_per_fat) + { + diag_printf("FAT: FBR sec per fat32: %u\n", fbr->sec_per_fat_32); + diag_printf("FAT: FBR ext flags: 0x%04X\n", fbr->ext_flags); + diag_printf("FAT: FBR fs ver: %u\n", fbr->fs_ver); + diag_printf("FAT: FBR root cluster: %u\n", fbr->root_cluster); + diag_printf("FAT: FBR fs info sec: %u\n", fbr->fs_info_sec); + } + + diag_printf("FAT: FBR drv num: %u\n", fbr->drv_num); + diag_printf("FAT: FBR ext sig: 0x%02X\n", fbr->ext_sig); + diag_printf("FAT: FBR ser num: 0x%08X\n", fbr->ser_num); + diag_printf("FAT: FBR vol name: '%.11s'\n", fbr->vol_name); + diag_printf("FAT: FBR fat name: '%.8s'\n", fbr->fat_name); + diag_printf("FAT: FBR exe mark: 0x%02X 0x%02X\n", + fbr->exe_marker[0], fbr->exe_marker[1]); +} + +#endif // TDE + +// ------------------------------------------------------------------------- +// read_boot_record() +// Reads FAT boot record from disk. + +static int +read_boot_record(fatfs_disk_t *disk, fat_boot_record_t *fbr) +{ + cyg_uint8 data[0x5A]; + cyg_uint32 len; + int err; + + len = 0x5A; + err = disk_read(disk, (void*)data, &len, 0); + if (err != ENOERR) + return err; + + GET_WORD(data, fbr->jump, 0x00); + GET_BYTES(data, fbr->oem_name, 8, 0x03); + GET_WORD(data, fbr->bytes_per_sec, 0x0B); + GET_BYTE(data, fbr->sec_per_clu, 0x0D); + GET_WORD(data, fbr->res_sec_num, 0x0E); + GET_BYTE(data, fbr->fat_tbls_num, 0x10); + GET_WORD(data, fbr->max_root_dents, 0x11); + GET_WORD(data, fbr->sec_num_32, 0x13); + GET_BYTE(data, fbr->media_desc, 0x15); + GET_WORD(data, fbr->sec_per_fat, 0x16); + GET_WORD(data, fbr->sec_per_track, 0x18); + GET_WORD(data, fbr->heads_num, 0x1A); + GET_DWORD(data, fbr->hsec_num, 0x1C); + GET_DWORD(data, fbr->sec_num, 0x20); + + // This is a quick check for FAT12/16 or FAT32 boot record. + // The sec_per_fat field must be 0 on FAT32, since this + // field plays a crucial role in detection of the FAT type + // (12,16,32) it is quite safe to make this assumption. + if (0 == fbr->sec_per_fat) + { + GET_DWORD(data, fbr->sec_per_fat_32, 0x24); + GET_WORD(data, fbr->ext_flags, 0x28); + GET_WORD(data, fbr->fs_ver, 0x2A); + GET_DWORD(data, fbr->root_cluster, 0x2C); + GET_WORD(data, fbr->fs_info_sec, 0x30); + GET_WORD(data, fbr->bk_boot_sec, 0x32); + GET_BYTE(data, fbr->drv_num, 0x40); + GET_BYTE(data, fbr->ext_sig, 0x42); + GET_DWORD(data, fbr->ser_num, 0x43); + GET_BYTES(data, fbr->vol_name, 11, 0x47); + GET_BYTES(data, fbr->fat_name, 8, 0x52); + } + else + { + GET_BYTE(data, fbr->drv_num, 0x24); + GET_BYTE(data, fbr->ext_sig, 0x26); + GET_DWORD(data, fbr->ser_num, 0x27); + GET_BYTES(data, fbr->vol_name, 11, 0x2B); + GET_BYTES(data, fbr->fat_name, 8, 0x36); + } + + // Read the end marker + len = 0x02; + err = disk_read(disk, (void*)data, &len, 0x1FE); + if (err != ENOERR) + return err; + + GET_BYTES(data, fbr->exe_marker, 2, 0); + + // Zero terminate strings + fbr->oem_name[8] = '\0'; + fbr->vol_name[11] = '\0'; + fbr->fat_name[8] = '\0'; + +#if TDE + print_boot_record(fbr); +#endif + + return ENOERR; +} + +//========================================================================== +// FAT table entry functions + +// ------------------------------------------------------------------------- +// read_tentry() +// Reads FAT table entry from disk. + +static int +read_tentry(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry) +{ + cyg_uint8 data[4]; + cyg_uint32 pos, num3; + cyg_uint32 e; + cyg_uint32 len; + int err; + + switch (disk->fat_type) + { + case FATFS_FAT12: + num3 = num * 3; + pos = disk->fat_tbl_pos + (num3 >> 1); + len = 2; + + err = disk_read(disk, (void*)data, &len, pos); + if (err != ENOERR) + return err; + + GET_WORD(data, e, 0x00); + + if (0 == (num3 & 1)) *entry = e & 0x0FFF; + else *entry = (e >> 4) & 0x0FFF; + + break; + + case FATFS_FAT16: + pos = disk->fat_tbl_pos + (num << 1); + len = 2; + + err = disk_read(disk, (void*)data, &len, pos); + if (err != ENOERR) + return err; + + GET_WORD(data, e, 0x00); + *entry = e; + + break; + + case FATFS_FAT32: + pos = disk->fat_tbl_pos + (num << 2); + len = 4; + + err = disk_read(disk, (void*)data, &len, pos); + if (err != ENOERR) + return err; + + GET_DWORD(data, e, 0x00); + *entry = e & 0x0FFFFFFF; + + break; + + default: + CYG_ASSERT(false, "Unknown FAT type"); + } + return ENOERR; +} + +// ------------------------------------------------------------------------- +// write_tentry() +// Writes FAT table entry to disk (to all copies of FAT). + +static int +write_tentry(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry) +{ + cyg_uint8 data[4]; + cyg_uint32 pos=0, num3; + cyg_uint32 e; + cyg_uint32 len; + int i; + int err; + + switch (disk->fat_type) + { + case FATFS_FAT12: + num3 = num * 3; + pos = disk->fat_tbl_pos + (num3 >> 1); + len = 2; + + err = disk_read(disk, (void*)data, &len, pos); + if (err != ENOERR) + return err; + + GET_WORD(data, e, 0x00); + + if (0 == (num3 & 1)) e = (e & 0xF000) | (*entry & 0x0FFF); + else e = (e & 0x000F) | ((*entry & 0x0FFF) << 4); + + SET_WORD(data, e, 0x00); + + break; + + case FATFS_FAT16: + pos = disk->fat_tbl_pos + (num << 1); + len = 2; + + e = *entry; + SET_WORD(data, e, 0x00); + + break; + + case FATFS_FAT32: + pos = disk->fat_tbl_pos + (num << 2); + len = 4; + + err = disk_read(disk, (void*)data, &len, pos); + if (err != ENOERR) + return err; + + GET_DWORD(data, e, 0x00); + + e = (e & 0xF0000000) | *entry; + + SET_DWORD(data, e, 0x00); + + break; + + default: + CYG_ASSERT(false, "Unknown FAT type"); + } + + for (i = 0; i < disk->fat_tbls_num; i++) + { + err = disk_write(disk, (void*)data, &len, pos); + if (err != ENOERR) + return err; + + pos += disk->fat_tbl_size; + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// get_tentry_type() +// Gets the type of FAT table entry. + +static int +get_tentry_type(fatfs_disk_t *disk, cyg_uint32 entry) +{ + int type; + + switch (disk->fat_type) + { + case FATFS_FAT12: + if (entry < 0x0FF0) + { + if (0x0000 == entry) type = TENTRY_FREE; + else type = TENTRY_REGULAR; + } + else if (entry >= 0x0FF8) type = TENTRY_LAST; + else if (0x0FF7 == entry) type = TENTRY_BAD; + else type = TENTRY_RESERVED; + + break; + + case FATFS_FAT16: + if (entry < 0xFFF0) + { + if (0x0000 == entry) type = TENTRY_FREE; + else type = TENTRY_REGULAR; + } + else if (entry >= 0xFFF8) type = TENTRY_LAST; + else if (0xFFF7 == entry) type = TENTRY_BAD; + else type = TENTRY_RESERVED; + + break; + + case FATFS_FAT32: + + if (entry < 0x0FFFFFF0) + { + if (0x00000000 == entry) type = TENTRY_FREE; + else type = TENTRY_REGULAR; + } + else if (entry >= 0x0FFFFFF8) type = TENTRY_LAST; + else if (0x0FFFFFF7 == entry) type = TENTRY_BAD; + else type = TENTRY_RESERVED; + + break; + + default: + CYG_ASSERT(false, "Unknown FAT type"); + type = TENTRY_BAD; // least likely to cause damage + } + return type; +} + +// ------------------------------------------------------------------------- +// set_tentry_type() +// Sets the type of FAT table entry. + +static void +set_tentry_type(fatfs_disk_t *disk, cyg_uint32 *entry, cyg_uint32 type) +{ + switch (disk->fat_type) + { + case FATFS_FAT12: + switch (type) + { + case TENTRY_FREE: *entry = 0x0000; return; + case TENTRY_LAST: *entry = 0x0FF8; return; + case TENTRY_RESERVED: *entry = 0x0FF0; return; + case TENTRY_BAD: *entry = 0x0FF7; return; + default: + CYG_ASSERT(false, "Unknown tentry type"); + } + break; + + case FATFS_FAT16: + switch (type) + { + case TENTRY_FREE: *entry = 0x0000; return; + case TENTRY_LAST: *entry = 0xFFF8; return; + case TENTRY_RESERVED: *entry = 0xFFF0; return; + case TENTRY_BAD: *entry = 0xFFF7; return; + default: + CYG_ASSERT(false, "Unknown tentry type"); + } + break; + + case FATFS_FAT32: + switch (type) + { + case TENTRY_FREE: *entry = 0x00000000; return; + case TENTRY_LAST: *entry = 0x0FFFFFF8; return; + case TENTRY_RESERVED: *entry = 0x0FFFFFF0; return; + case TENTRY_BAD: *entry = 0x0FFFFFF7; return; + default: + CYG_ASSERT(false, "Unknown tentry type"); + } + break; + + default: + CYG_ASSERT(false, "Unknown FAT type"); + } +} + +// ------------------------------------------------------------------------- +// get_tentry_next_cluster() +// Gets the the next file cluster number from FAT table entry. + +static __inline__ cyg_uint32 +get_tentry_next_cluster(fatfs_disk_t *disk, cyg_uint32 entry) +{ + return entry; +} + +// ------------------------------------------------------------------------- +// set_tentry_next_cluster() +// Sets the the next cluster number to FAT table entry. + +static __inline__ void +set_tentry_next_cluster(fatfs_disk_t *disk, + cyg_uint32 *entry, + cyg_uint32 next_cluster) +{ + *entry = next_cluster; +} + +//========================================================================== +// FAT cluster functions + +// ------------------------------------------------------------------------- +// erase_cluster() +// Erases cluster (fills with 0x00). + +static int +erase_cluster(fatfs_disk_t *disk, cyg_uint32 cluster) +{ + cyg_uint8 data[32]; + cyg_uint32 pos; + cyg_uint32 len; + int err, i; + + pos = 0; + len = 32; + memset((void*)data, 0x00, len); + + CYG_TRACE1(TCL, "erasing cluster=%d", cluster); + + for (i = 0; i < (disk->cluster_size >> 5); i++) + { + err = disk_cluster_write(disk, (void*)data, &len, cluster, pos); + if (err != ENOERR) + return err; + + pos += len; + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// mark_cluster() +// Marks cluster (sets the cluster's FAT table entry to given type). + +static int +mark_cluster(fatfs_disk_t *disk, cyg_uint32 cluster, cyg_uint32 type) +{ + cyg_uint32 tentry; + + set_tentry_type(disk, &tentry, type); + return write_tentry(disk, cluster, &tentry); +} + +// ------------------------------------------------------------------------- +// link_cluster() +// Links two clusters. + +static int +link_cluster(fatfs_disk_t *disk, cyg_uint32 cluster1, cyg_uint32 cluster2) +{ + cyg_uint32 tentry; + + set_tentry_next_cluster(disk, &tentry, cluster2); + return write_tentry(disk, cluster1, &tentry); +} + +// ------------------------------------------------------------------------- +// find_next_free_cluster() +// Finds first free cluster starting from given cluster. +// If none is available free_cluster is set to 0. +// If CO_MARK_LAST is set in opts the found cluster is marked as LAST. +// If CO_ERASE_NEW is set in opts the found cluster is erased. + +static int +find_next_free_cluster(fatfs_disk_t *disk, + cyg_uint32 start_cluster, + cyg_uint32 *free_cluster, + cluster_opts_t opts) +{ + cyg_uint32 c, tentry; + int err; + + if (start_cluster < 2) c = 2; + else c = start_cluster + 1; + + *free_cluster = 0; + + CYG_TRACE1(TCL, "starting at cluster=%d", c); + + // Search from the starting cluster to the end of FAT and + // from start of FAT to the starting cluster + while (c != start_cluster) + { + // Check for end of FAT + if (c >= disk->fat_tbl_nents) + { + c = 2; + if (c >= start_cluster) + break; + } + + err = read_tentry(disk, c, &tentry); + if (err != ENOERR) + return err; + + if (TENTRY_FREE == get_tentry_type(disk, tentry)) + { + CYG_TRACE1(TCL, "found free cluster=%d", c); + + *free_cluster = c; + + if (opts & CO_MARK_LAST) + err = mark_cluster(disk, c, TENTRY_LAST); + if ((err == ENOERR) && (opts & CO_ERASE_NEW)) + err = erase_cluster(disk, c); + + return err; + } + c++; + } + + // No free clusters found + + CYG_TRACE0(TCL, "!!! no free clusters found"); + + return ENOSPC; +} + +// ------------------------------------------------------------------------- +// find_and_append_cluster() +// Finds a free cluster on disk and appends it to the given cluster. +// New cluster is marked as LAST. + +static int +find_and_append_cluster(fatfs_disk_t *disk, + cyg_uint32 cluster, + cyg_uint32 *new_cluster, + cluster_opts_t opts) +{ + cyg_uint32 free_cluster; + int err; + + err = find_next_free_cluster(disk, cluster, + &free_cluster, opts | CO_MARK_LAST); + if (err != ENOERR) + return err; + + err = link_cluster(disk, cluster, free_cluster); + if (err != ENOERR) + return err; + + *new_cluster = free_cluster; + + CYG_TRACE2(TCL, "appending new cluster=%d to cluster=%d", + free_cluster, cluster); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// find_nth_cluster0() +// Finds nth cluster in chain (ie nth cluster of file) searching +// from given position. The result is returned by the same position +// variable. + +static int +find_nth_cluster0(fatfs_disk_t *disk, + fatfs_data_pos_t *pos, + cyg_uint32 n) +{ + cyg_uint32 cluster, cluster_snum; + int err = ENOERR; + + if (pos->cluster_snum == n) + return ENOERR; + + cluster = pos->cluster; + cluster_snum = pos->cluster_snum; + + CYG_TRACE4(TCL, "cluster=%d snum=%d n=%d n_to_search=%d", + cluster, cluster_snum, n, n-cluster_snum); + + // Adjust the number of clusters that should be + // walked according to the given position + n -= cluster_snum; + + // Walk the cluster chain for n clusters or until last cluster + while (n > 0) + { + cyg_uint32 tentry; + + err = read_tentry(disk, cluster, &tentry); + if (err != ENOERR) + return err; + + switch (get_tentry_type(disk, tentry)) + { + case TENTRY_REGULAR: + break; + case TENTRY_LAST: + CYG_TRACE1(TCL, "chain end at n=%d", n); + err = EEOF; // File has less clusters than given n + // this err should be caught by the + // calling function + goto out; + default: + // Inconsistant FAT table state !!! + CYG_TRACE2(TCL, "!!! inconsistant FAT tentry=%x n=%d", + tentry, n); + err = EIO; + goto out; + } + cluster = get_tentry_next_cluster(disk, tentry); + cluster_snum++; + n--; + } + +out: + pos->cluster = cluster; + pos->cluster_snum = cluster_snum; + + CYG_TRACE2(TCL, "nth cluster=%d snum=%d", cluster, cluster_snum); + + return err; +} + +// ------------------------------------------------------------------------- +// find_nth_cluster() +// Finds nth cluster in chain (ie nth cluster of file) searching +// from given position. The result is returned by the same position +// variable. If the chain ends one cluster before the given nth cluster +// and the CO_EXTEND is specified, than the chain is extended by one cluster. + +static int +find_nth_cluster(fatfs_disk_t *disk, + fatfs_data_pos_t *pos, + cyg_uint32 n, + cluster_opts_t opts) +{ + int err; + + // Find nth cluster + err = find_nth_cluster0(disk, pos, n); + + // EEOF meens that the cluster chain ended early + if ((err != EEOF) || !(opts & CO_EXTEND)) + return err; + + // Check if one cluster short + if (pos->cluster_snum == (n - 1)) + { + // Extend the chain for one cluster + cyg_uint32 new_cluster; + + // Append new cluster to the end of chain + err = find_and_append_cluster(disk, pos->cluster, &new_cluster, opts); + if (err != ENOERR) + return err; + + // Update position + pos->cluster = new_cluster; + pos->cluster_snum += 1; + pos->cluster_pos = 0; + + CYG_TRACE1(TCL, "appended new cluster=%d", new_cluster); + } + + return err; +} + +// ------------------------------------------------------------------------- +// get_next_cluster() +// Gets next cluster in chain (ie next cluster of file). +// If CO_EXTEND is specified and the current cluster is last in the +// chain then the chain is extended by one cluster. + +static int +get_next_cluster(fatfs_disk_t *disk, + fatfs_data_pos_t *pos, + cluster_opts_t opts) +{ + int err; + + err = find_nth_cluster(disk, pos, pos->cluster_snum + 1, opts); + if (err != ENOERR) + return err; + + // Reset inside cluster position + pos->cluster_pos = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// get_position_from_off() +// Gets position from given offset. The search is started from the +// given position and the result is returned by the same variable. +// If CO_EXTEND is specified the file is extended if one cluster too short. + +static int +get_position_from_off(fatfs_disk_t *disk, + cyg_uint32 first_cluster, + cyg_uint32 offset, + fatfs_data_pos_t *pos, + cluster_opts_t opts) +{ + fatfs_data_pos_t new_pos; + cyg_uint32 n; + int err; + + // Position inside the cluster + new_pos.cluster_pos = offset & (disk->cluster_size - 1); + + // Cluster seq number to be searched for + n = offset >> disk->cluster_size_log2; + + if (n < pos->cluster_snum) + { + // Start searching from first cluster + new_pos.cluster = first_cluster; + new_pos.cluster_snum = 0; + } + else + { + // Start searching from the current position + new_pos.cluster = pos->cluster; + new_pos.cluster_snum = pos->cluster_snum; + } + + err = find_nth_cluster(disk, &new_pos, n, opts); + + // Err could be EEOF wich means that the given + // offset if out of given file (cluster chain) + + if (EEOF == err) + new_pos.cluster_pos = disk->cluster_size; + + *pos = new_pos; + + return err; +} + +// ------------------------------------------------------------------------- +// free_cluster_chain() +// Marks all clusters FREE from given cluster to the last cluster in chain. + +static int +free_cluster_chain(fatfs_disk_t *disk, cyg_uint32 start_cluster) +{ + cyg_uint32 c, next_c, tentry; + bool last; + int err; + + CYG_TRACE1(TCL, "start cluster=%d", start_cluster); + + c = next_c = start_cluster; + last = false; + while (!last) + { + err = read_tentry(disk, c, &tentry); + if (err != ENOERR) + return err; + + switch (get_tentry_type(disk, tentry)) + { + case TENTRY_LAST: + // Last cluster in chain + last = true; + break; + case TENTRY_REGULAR: + // Get next cluster in chain + next_c = get_tentry_next_cluster(disk, tentry); + break; + default: + CYG_TRACE2(TCL, "!!! inconsistant FAT tentry=%x c=%d", + tentry, c); + return EIO; + } + + // Set the current tentry to FREE + set_tentry_type(disk, &tentry, TENTRY_FREE); + err = write_tentry(disk, c, &tentry); + if (err != ENOERR) + return err; + + // Next cluster in chain + c = next_c; + } + + CYG_TRACE1(TCL, "last cluster=%d", c); + + return ENOERR; +} + +//========================================================================== +// FAT dir entry functions + +// ------------------------------------------------------------------------- +// print_raw_dentry() +// Prints FAT directory entry. + +#if TDE +static void +print_raw_dentry(fat_raw_dir_entry_t* dentry) +{ + if (DENTRY_IS_DELETED(dentry)) + diag_printf("FAT: FDE name: '?%.7s'\n", &dentry->name[1]); + else + diag_printf("FAT: FDE name: '%.8s'\n", dentry->name); + diag_printf("FAT: FDE ext: '%.3s'\n", dentry->ext); + diag_printf("FAT: FDE attr: %c%c%c%c%c%c\n", + (DENTRY_IS_RDONLY(dentry) ? 'R' : '-'), + (DENTRY_IS_HIDDEN(dentry) ? 'H' : '-'), + (DENTRY_IS_SYSTEM(dentry) ? 'S' : '-'), + (DENTRY_IS_VOLUME(dentry) ? 'V' : '-'), + (DENTRY_IS_DIR(dentry) ? 'D' : '-'), + (DENTRY_IS_ARCHIVE(dentry) ? 'A' : '-')); + diag_printf("FAT: FDE crt time: %u\n", dentry->crt_time); + diag_printf("FAT: FDE crt date: %u\n", dentry->crt_date); + diag_printf("FAT: FDE acc date: %u\n", dentry->acc_date); + diag_printf("FAT: FDE wrt time: %u\n", dentry->wrt_time); + diag_printf("FAT: FDE wrt date: %u\n", dentry->wrt_date); + diag_printf("FAT: FDE cluster: %u\n", (dentry->cluster_HI << 16) | dentry->cluster); + diag_printf("FAT: FDE size: %u\n", dentry->size); +} +#endif // TDE + +// ------------------------------------------------------------------------- +// read_raw_dentry() +// Reads dir entry from disk. + +static int +read_raw_dentry(fatfs_disk_t *disk, + fatfs_data_pos_t *pos, + fat_raw_dir_entry_t *dentry) +{ + cyg_uint8 data[DENTRY_SIZE]; + cyg_uint32 len; + int err; + + CYG_TRACE3(TDE, "cluster=%d snum=%d pos=%d", + pos->cluster, pos->cluster_snum, pos->cluster_pos); + + len = DENTRY_SIZE; + + // Check if we are reading the FAT12/16 root directory + if (0 == pos->cluster) + err = disk_read(disk, (void*)data, &len, + disk->fat_root_dir_pos + pos->cluster_pos); + else + err = disk_cluster_read(disk, (void*)data, &len, + pos->cluster, pos->cluster_pos); + if (err != ENOERR) + return err; + + GET_BYTES(data, dentry->name, 8, 0x00); + GET_BYTES(data, dentry->ext, 3, 0x08); + GET_BYTE(data, dentry->attr, 0x0B); + GET_BYTE(data, dentry->nt_reserved, 0x0C); + GET_BYTE(data, dentry->crt_sec_100, 0x0D); + GET_WORD(data, dentry->crt_time, 0x0E); + GET_WORD(data, dentry->crt_date, 0x10); + GET_WORD(data, dentry->acc_date, 0x12); + GET_WORD(data, dentry->cluster_HI, 0x14); + GET_WORD(data, dentry->wrt_time, 0x16); + GET_WORD(data, dentry->wrt_date, 0x18); + GET_WORD(data, dentry->cluster, 0x1A); + GET_DWORD(data, dentry->size, 0x1C); + + // Zero terminate strings + dentry->name[8] = '\0'; + dentry->ext[3] = '\0'; + +#if TDE + print_raw_dentry(dentry); +#endif + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// write_raw_dentry() +// Writes raw dir entry to disk. + +static int +write_raw_dentry(fatfs_disk_t *disk, + fatfs_data_pos_t *pos, + fat_raw_dir_entry_t *dentry) +{ + cyg_uint8 data[DENTRY_SIZE]; + cyg_uint32 len; + int err; + + CYG_TRACE3(TDE, "cluster=%d snum=%d pos=%d", + pos->cluster, pos->cluster_snum, pos->cluster_pos); + + SET_BYTES(data, dentry->name, 8, 0x00); + SET_BYTES(data, dentry->ext, 3, 0x08); + SET_BYTE(data, dentry->attr, 0x0B); + SET_BYTE(data, dentry->nt_reserved, 0x0C); + SET_BYTE(data, dentry->crt_sec_100, 0x0D); + SET_WORD(data, dentry->crt_time, 0x0E); + SET_WORD(data, dentry->crt_date, 0x10); + SET_WORD(data, dentry->acc_date, 0x12); + SET_WORD(data, dentry->cluster_HI, 0x14); + SET_WORD(data, dentry->wrt_time, 0x16); + SET_WORD(data, dentry->wrt_date, 0x18); + SET_WORD(data, dentry->cluster, 0x1A); + SET_DWORD(data, dentry->size, 0x1C); + + len = DENTRY_SIZE; + + // Check if we are writting to the FAT12/16 root directory + if (0 == pos->cluster) + err = disk_write(disk, (void*)data, &len, + disk->fat_root_dir_pos + pos->cluster_pos); + else + err = disk_cluster_write(disk, (void*)data, &len, + pos->cluster, pos->cluster_pos); + if (err != ENOERR) + return err; + +#if TDE + print_raw_dentry(dentry); +#endif + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// raw_dentry_set_deleted() +// Sets the dentry filename first char to 0xE5 (ie deleted). + +static __inline__ void +raw_dentry_set_deleted(fatfs_disk_t *disk, fat_raw_dir_entry_t *dentry) +{ + dentry->name[0] = 0xE5; +} + +// ------------------------------------------------------------------------- +// get_raw_dentry_filename() +// Gets the filename from given dir entry. + +static void +get_raw_dentry_filename(fat_raw_dir_entry_t *dentry, char *name) +{ + int i = 0; + char *cptr = dentry->name; + char *cname = name; + + while (*cptr != ' ' && i < 8) + { + *cname++ = *cptr++; i++; + } + cptr = dentry->ext; + + if (*cptr != ' ') + { + *cname++ = '.'; i = 0; + while (*cptr != ' ' && i < 3) + { + *cname++ = *cptr++; i++; + } + } + *cname = '\0'; + + CYG_TRACE3(TDE, "dos name='%s' dos ext='%s' filename='%s'", + dentry->name, dentry->ext, name); +} + +// ------------------------------------------------------------------------- +// set_raw_dentry_filename() +// Sets the filename of given dir entry. + +static void +set_raw_dentry_filename(fat_raw_dir_entry_t *dentry, + const char *name, + int namelen) +{ + int i, nidx; + const char *cname; + char *cptr; + + // Special case check + if ('.' == name[0]) + { + if ('\0' == name[1]) + { + strcpy(dentry->name, ". "); + strcpy(dentry->ext, " "); + return; + } + else if ('.' == name[1] && '\0' == name[2]) + { + strcpy(dentry->name, ".. "); + strcpy(dentry->ext, " "); + return; + } + } + + if (0 == namelen) + namelen = 9999; + + nidx = 0; + cname = name; + cptr = dentry->name; + for (i = 0; i < 8; i++) + { + if (*cname != '.' && *cname != '\0' && nidx++ < namelen) + *cptr++ = toupper(*cname++); + else + *cptr++ = ' '; + } + *cptr = '\0'; + + while (*cname != '.' && *cname != '\0' && nidx++ < namelen) + cname++; + + if ('.' == *cname && nidx++ < namelen) + cname++; + + cptr = dentry->ext; + for (i = 0; i < 3; i++) + { + if (*cname != '.' && *cname != '\0' && nidx++ < namelen) + *cptr++ = toupper(*cname++); + else + *cptr++ = ' '; + } + *cptr = '\0'; + + CYG_TRACE4(TDE, "filename='%s' namelen=%d dos name='%s' dos ext='%s'", + name, namelen, dentry->name, dentry->ext); +} + +// ------------------------------------------------------------------------- +// read_next_raw_dentry() +// Gets next dir entry searching from given position to the end. +// If EEOF is returned there are no more entries in given dir. + +static int +read_next_raw_dentry(fatfs_disk_t *disk, + fatfs_data_pos_t *pos, + fat_raw_dir_entry_t *dentry) +{ + int err = ENOERR; + + // If we are reading the root dir on FAT32 we have + // to correct the position to the root dir cluster + if (FATFS_FAT32 == disk->fat_type && 0 == pos->cluster) + pos->cluster = disk->fat_root_dir_cluster; + + while (true) + { + // FAT12/16 root dir check + if (0 == pos->cluster) + { + if (pos->cluster_pos >= disk->fat_root_dir_size) + err = EEOF; + } + else + { + // Change cluster if needed + if (pos->cluster_pos >= disk->cluster_size) + err = get_next_cluster(disk, pos, CO_NONE); + } + + if (err != ENOERR) + break; + + err = read_raw_dentry(disk, pos, dentry); + if (err != ENOERR) + return err; + + if (DENTRY_IS_ZERO(dentry)) + { + // If we get a ZERO dir entry, we assume that + // there are no more entries in current dir + CYG_TRACE0(TDE, "end of dir"); + err = EEOF; + break; + } + else if (!DENTRY_IS_DELETED(dentry)) + { + // Dir entry found + CYG_TRACE3(TDE, "found new dentry at cluster=%d snum=%d pos=%d", + pos->cluster, pos->cluster_snum, pos->cluster_pos); + break; + } + + pos->cluster_pos += DENTRY_SIZE; + } + + // EEOF could be returned if there are no more entries in this + // dir - this should be cought by the calling function + + return err; +} + +// ------------------------------------------------------------------------- +// get_free_raw_dentry() +// Gets free dir entry slot searching from given position extending the +// directory if needed. If an deleated entry is found it is reused. + +static int +get_free_raw_dentry(fatfs_disk_t *disk, + fatfs_data_pos_t *pos) +{ + fat_raw_dir_entry_t raw_dentry; + fatfs_data_pos_t cpos; + int err = ENOERR; + + cpos = *pos; + + // If we are reading the root dir on FAT32 we have + // to correct the position to the root dir cluster + if (FATFS_FAT32 == disk->fat_type && 0 == cpos.cluster) + cpos.cluster = disk->fat_root_dir_cluster; + + CYG_TRACE3(TDE, "cluster=%d snum=%d pos=%d", + pos->cluster, pos->cluster_snum, pos->cluster_pos); + + while (true) + { + // FAT12/16 root dir check + if (0 == cpos.cluster) + { + if (cpos.cluster_pos >= disk->fat_root_dir_size) + err = ENOSPC; + } + else + { + // Change cluster if needed + if (cpos.cluster_pos >= disk->cluster_size) + err = get_next_cluster(disk, &cpos, CO_EXTEND | CO_ERASE_NEW); + } + + if (err != ENOERR) + return err; + + err = read_raw_dentry(disk, &cpos, &raw_dentry); + if (err != ENOERR) + return err; + + if (DENTRY_IS_DELETED(&raw_dentry)) + { + CYG_TRACE3(TDE, "deleted dentry at cluster=%d snum=%d pos=%d", + cpos.cluster, cpos.cluster_snum, cpos.cluster_pos); + + *pos = cpos; + return ENOERR; + } + else if (DENTRY_IS_ZERO(&raw_dentry)) + { + CYG_TRACE3(TDE, "zero dentry at cluster=%d snum=%d pos=%d", + cpos.cluster, cpos.cluster_snum, cpos.cluster_pos); + + *pos = cpos; + return ENOERR; + } + + cpos.cluster_pos += DENTRY_SIZE; + } +} + +// ------------------------------------------------------------------------- +// raw_to_dentry() +// Converts raw FAT dir entry to dir entry. + +static void +raw_to_dentry(fat_raw_dir_entry_t *raw_dentry, + fatfs_data_pos_t *raw_dentry_pos, + fatfs_dir_entry_t *dentry) +{ + get_raw_dentry_filename(raw_dentry, dentry->filename); + + if (DENTRY_IS_DIR(raw_dentry)) + dentry->mode = __stat_mode_DIR; + else + dentry->mode = __stat_mode_REG; + +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + dentry->attrib = raw_dentry->attr; +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + + date_dos2unix(raw_dentry->crt_time, raw_dentry->crt_date, &dentry->ctime); + date_dos2unix(0, raw_dentry->acc_date, &dentry->atime); + date_dos2unix(raw_dentry->wrt_time, raw_dentry->wrt_date, &dentry->mtime); + + dentry->size = raw_dentry->size; + dentry->priv_data = raw_dentry->nt_reserved; + dentry->cluster = raw_dentry->cluster | (raw_dentry->cluster_HI << 16); + dentry->disk_pos = *raw_dentry_pos; +} + +// ------------------------------------------------------------------------- +// dentry_to_raw() +// Converts dir entry to raw FAT dir entry. + +static void +dentry_to_raw(fatfs_dir_entry_t *dentry, fat_raw_dir_entry_t *raw_dentry) +{ + set_raw_dentry_filename(raw_dentry, dentry->filename, 0); + + if (__stat_mode_DIR == dentry->mode) + raw_dentry->attr = S_FATFS_DIR; + else + raw_dentry->attr = S_FATFS_ARCHIVE; +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + raw_dentry->attr = dentry->attrib; +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + + + date_unix2dos(dentry->ctime, &raw_dentry->crt_time, &raw_dentry->crt_date); + date_unix2dos(dentry->atime, NULL, &raw_dentry->acc_date); + date_unix2dos(dentry->mtime, &raw_dentry->wrt_time, &raw_dentry->wrt_date); + + raw_dentry->crt_sec_100 = 0; //FIXME + raw_dentry->size = dentry->size; + raw_dentry->nt_reserved = dentry->priv_data; + raw_dentry->cluster = dentry->cluster & 0xFFFF; + raw_dentry->cluster_HI = dentry->cluster >> 16; +} + +//========================================================================== +// FAT data functions + +// ------------------------------------------------------------------------- +// read_data() +// Reads data from given position. + +static int +read_data(fatfs_disk_t *disk, + void *data, + cyg_uint32 *len, + fatfs_data_pos_t *pos) +{ + cyg_uint8 *buf = (cyg_uint8 *) data; + cyg_uint32 size = *len; + int err = ENOERR; + + CYG_TRACE4(TDO, "len=%d cluster=%d snum=%d pos=%d", + *len, pos->cluster, pos->cluster_snum, + pos->cluster_pos); + + while (size > 0) + { + cyg_uint32 csize; + + // Check if we are still inside current cluster + if (pos->cluster_pos >= disk->cluster_size) + { + // Get next cluster of file + err = get_next_cluster(disk, pos, CO_NONE); + if (err != ENOERR) + goto out; + } + + // Adjust the data chunk size to be read to the cluster boundary + if (size > (disk->cluster_size - pos->cluster_pos)) + csize = disk->cluster_size - pos->cluster_pos; + else + csize = size; + + CYG_TRACE4(TDO, "-- len=%d cluster=%d snum=%d pos=%d", + csize, pos->cluster, pos->cluster_snum, + pos->cluster_pos); + + err = disk_cluster_read(disk, (void*)buf, &csize, + pos->cluster, pos->cluster_pos); + if (err != ENOERR) + goto out; + + // Adjust running variables + + buf += csize; + pos->cluster_pos += csize; + size -= csize; + } + +out: + *len -= size; + + CYG_TRACE1(TDO, "total len=%d", *len); + + return err; +} + +// ------------------------------------------------------------------------- +// write_data() +// Writes data to given position. + +static int +write_data(fatfs_disk_t *disk, + void *data, + cyg_uint32 *len, + fatfs_data_pos_t *pos) +{ + cyg_uint8 *buf = (cyg_uint8 *) data; + cyg_uint32 size = *len; + int err = ENOERR; + + CYG_TRACE4(TDO, "len=%d cluster=%d snum=%d pos=%d", + *len, pos->cluster, pos->cluster_snum, + pos->cluster_pos); + + while (size > 0) + { + cyg_uint32 csize; + + // Check if we are still inside current cluster + if (pos->cluster_pos >= disk->cluster_size) + { + // Get next cluster of file, if at the last + // cluster try to extend the cluster chain + err = get_next_cluster(disk, pos, CO_EXTEND); + if (err != ENOERR) + goto out; + } + + // Adjust the data chunk size to be read to the cluster boundary + if (size > (disk->cluster_size - pos->cluster_pos)) + csize = disk->cluster_size - pos->cluster_pos; + else + csize = size; + + CYG_TRACE4(TDO, "-- len=%d cluster=%d snum=%d pos=%d", + csize, pos->cluster, pos->cluster_snum, + pos->cluster_pos); + + err = disk_cluster_write(disk, (void*)buf, &csize, + pos->cluster, pos->cluster_pos); + if (err != ENOERR) + goto out; + + // Adjust running variables + + buf += csize; + pos->cluster_pos += csize; + size -= csize; + } + +out: + *len -= size; + + CYG_TRACE1(TDO, "total len=%d", *len); + + return err; +} + +//========================================================================== +// Misc functions + +// ------------------------------------------------------------------------- +// init_dir_entry() +// Initializes attributes of a new dir entry. + +static void +init_dir_entry(fatfs_dir_entry_t *dentry, + const char *name, + int namelen, + mode_t mode, + cyg_uint32 parent_cluster, + cyg_uint32 first_cluster, + fatfs_data_pos_t *pos) +{ + if (0 == namelen) + namelen = 12; + + strncpy(dentry->filename, name, namelen); + dentry->filename[namelen] = '\0'; + + dentry->mode = mode; + +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + if (S_ISDIR(dentry->mode)) + dentry->attrib = S_FATFS_DIR; + else + dentry->attrib = S_FATFS_ARCHIVE; +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + + dentry->ctime = + dentry->atime = + dentry->mtime = cyg_timestamp(); + + dentry->priv_data = 0; + dentry->size = 0; + dentry->cluster = first_cluster; + dentry->parent_cluster = parent_cluster; + dentry->disk_pos = *pos; +} + +// ------------------------------------------------------------------------- +// is_root_dir_entry() +// Check if the given dir entry is the root dir entry. + +static __inline__ bool +is_root_dir_entry(fatfs_dir_entry_t *dentry) +{ + return ('\0' == dentry->filename[0] && 0 == dentry->cluster); +} + +//========================================================================== +//========================================================================== +// Exported functions + +// ------------------------------------------------------------------------- +// fatfs_init() +// Gets disk info. + +int +fatfs_init(fatfs_disk_t *disk) +{ + cyg_uint32 sec_num, sec_per_fat, root_dir_sec_num; + cyg_uint32 data_sec_num, data_clu_num; + fat_boot_record_t boot_rec; + int err; + + CYG_CHECK_DATA_PTRC(disk); + + err = read_boot_record(disk, &boot_rec); + if (err != ENOERR) + return err; + + // Check some known boot record values + if (!(0x29 == boot_rec.ext_sig || + 0x28 == boot_rec.ext_sig) || + 0x55 != boot_rec.exe_marker[0] || + 0xAA != boot_rec.exe_marker[1]) + return EINVAL; + + // Sector and cluster sizes + disk->sector_size = boot_rec.bytes_per_sec; + disk->sector_size_log2 = get_val_log2(disk->sector_size); + disk->cluster_size = boot_rec.bytes_per_sec * boot_rec.sec_per_clu; + disk->cluster_size_log2 = get_val_log2(disk->cluster_size); + + // Sector and cluster size should always be a power of 2 + if (0 == disk->sector_size_log2 || 0 == disk->cluster_size_log2) + return EINVAL; + + // Determine number of sectors + if (boot_rec.sec_num_32 != 0) + sec_num = boot_rec.sec_num_32; + else + sec_num = boot_rec.sec_num; + + // Determine number of sectors per fat + if (boot_rec.sec_per_fat != 0) + sec_per_fat = boot_rec.sec_per_fat; + else + sec_per_fat = boot_rec.sec_per_fat_32; + + // Number of sectors used by root directory + root_dir_sec_num = ((boot_rec.max_root_dents * DENTRY_SIZE) + + (boot_rec.bytes_per_sec - 1)) / boot_rec.bytes_per_sec; + + // Number of data sectors + data_sec_num = sec_num - (boot_rec.res_sec_num + + (boot_rec.fat_tbls_num * sec_per_fat) + root_dir_sec_num); + + // Number of data clusters + data_clu_num = data_sec_num / boot_rec.sec_per_clu; + + // FAT table size and position + disk->fat_tbl_pos = boot_rec.bytes_per_sec * boot_rec.res_sec_num; + disk->fat_tbl_size = boot_rec.bytes_per_sec * sec_per_fat; + disk->fat_tbl_nents = data_clu_num + 2; + disk->fat_tbls_num = boot_rec.fat_tbls_num; + + // Determine the type of FAT based on number of data clusters + if (data_clu_num < 4085) + disk->fat_type = FATFS_FAT12; + else if (data_clu_num < 65525) + disk->fat_type = FATFS_FAT16; + else + disk->fat_type = FATFS_FAT32; + + // Determine root dir and data positions + if (FATFS_FAT32 != disk->fat_type) + { + disk->fat_root_dir_pos = disk->fat_tbl_pos + + disk->fat_tbls_num * disk->fat_tbl_size; + disk->fat_root_dir_size = boot_rec.max_root_dents * DENTRY_SIZE; + disk->fat_root_dir_nents = boot_rec.max_root_dents; + disk->fat_root_dir_cluster = 0; + disk->fat_data_pos = disk->fat_root_dir_pos + + disk->fat_root_dir_size; + } + else + { + disk->fat_root_dir_pos = 0; + disk->fat_root_dir_size = 0; + disk->fat_root_dir_nents = 0; + disk->fat_root_dir_cluster = boot_rec.root_cluster; + disk->fat_data_pos = disk->fat_tbl_pos + + disk->fat_tbls_num * disk->fat_tbl_size; + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_get_root_dir_entry() +// Gets root dir entry. + +void +fatfs_get_root_dir_entry(fatfs_disk_t *disk, fatfs_dir_entry_t *dentry) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dentry); + + dentry->mode = __stat_mode_DIR; +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + dentry->attrib = S_FATFS_DIR; +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + dentry->size = disk->fat_root_dir_size; + dentry->ctime = 0; + dentry->atime = 0; + dentry->mtime = 0; + dentry->filename[0] = '\0'; + dentry->cluster = 0; + dentry->parent_cluster = 0; + + dentry->disk_pos.cluster = 0; + dentry->disk_pos.cluster_snum = 0; + dentry->disk_pos.cluster_pos = 0; +} + +// ------------------------------------------------------------------------- +// fatfs_get_disk_usage() +// Gets disk space. + +int +fatfs_get_disk_usage(fatfs_disk_t *disk, + cyg_uint32 *total_clusters, + cyg_uint32 *free_clusters) +{ + cyg_uint32 c, nfree, tentry; + int err; + + nfree = 0; + for (c = 2; c < disk->fat_tbl_nents; c++) + { + err = read_tentry(disk, c, &tentry); + if (err != ENOERR) + return err; + + if (TENTRY_FREE == get_tentry_type(disk, tentry)) + nfree++; + } + + *total_clusters = disk->fat_tbl_nents - 2; + *free_clusters = nfree; + + return ENOERR; +} + + +// ------------------------------------------------------------------------- +// fatfs_read_dir_entry() +// Reads dir entry at given position. +// If there is no dir entry at given position the next closest is returned +// and the position is updated. If EEOF error is returned, then there are +// no more dir entries in given dir. + +int +fatfs_read_dir_entry(fatfs_disk_t *disk, + fatfs_dir_entry_t *dir, + fatfs_data_pos_t *pos, + fatfs_dir_entry_t *dentry) +{ + fat_raw_dir_entry_t raw_dentry; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dir); + CYG_CHECK_DATA_PTRC(pos); + CYG_CHECK_DATA_PTRC(dentry); + + err = read_next_raw_dentry(disk, pos, &raw_dentry); + if (err != ENOERR) + return err; + + raw_to_dentry(&raw_dentry, pos, dentry); + dentry->parent_cluster = dir->cluster; + + // Increment position for next call + pos->cluster_pos += DENTRY_SIZE; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_initpos() +// Initializes position to the start of the given file. + +int +fatfs_initpos(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(file); + CYG_CHECK_DATA_PTRC(pos); + + pos->cluster = file->cluster; + pos->cluster_snum = 0; + pos->cluster_pos = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_setpos() +// Sets the file position from offset. + +int +fatfs_setpos(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos, + cyg_uint32 offset) +{ + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(file); + CYG_CHECK_DATA_PTRC(pos); + + err = get_position_from_off(disk, file->cluster, offset, pos, CO_NONE); + + if (EEOF == err && offset == file->size) + return ENOERR; + else + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_getpos() +// Gets the file offset from position. + +cyg_uint32 +fatfs_getpos(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(file); + CYG_CHECK_DATA_PTRC(pos); + + return (pos->cluster_snum << disk->cluster_size_log2) + pos->cluster_pos; +} + +// ------------------------------------------------------------------------- +// fatfs_write_dir_entry() +// Writes dir entry to disk. + +int +fatfs_write_dir_entry(fatfs_disk_t *disk, fatfs_dir_entry_t *dentry) +{ + fat_raw_dir_entry_t raw_dentry; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dentry); + + dentry_to_raw(dentry, &raw_dentry); + err = write_raw_dentry(disk, &dentry->disk_pos, &raw_dentry); + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_delete_file() +// Marks dir entry as deleted and frees its cluster chain. + +int +fatfs_delete_file(fatfs_disk_t *disk, fatfs_dir_entry_t *file) +{ + fat_raw_dir_entry_t raw_dentry; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(file); + + if (is_root_dir_entry(file)) + return EINVAL; + + CYG_TRACE1(TDE, "filename='%s'", file->filename); + + dentry_to_raw(file, &raw_dentry); + + // Check if we are about to delete a directory + if (DENTRY_IS_DIR(&raw_dentry)) + { + fat_raw_dir_entry_t raw_cdentry; + fatfs_data_pos_t pos; + int i = 0; + + fatfs_initpos(disk, file, &pos); + + CYG_TRACE0(TDE, "got directory"); + + // Count number of entries in this dir + + while (true) + { + err = read_next_raw_dentry(disk, &pos, &raw_cdentry); + + if (EEOF == err) + break; + else if (err != ENOERR) + return err; + + pos.cluster_pos += DENTRY_SIZE; + i++; + } + CYG_TRACE1(TDE, "child count=%d", i); + + // Check if the dir is empty (except '.' and '..') + if (i > 2) + return EPERM; + } + + // Free file clusters + free_cluster_chain(disk, raw_dentry.cluster | (raw_dentry.cluster_HI << 16)); + raw_dentry_set_deleted(disk, &raw_dentry); + err = write_raw_dentry(disk, &file->disk_pos, &raw_dentry); + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_create_file() +// Creates a new file. + +int +fatfs_create_file(fatfs_disk_t *disk, + fatfs_dir_entry_t *dir, + const char *name, + int namelen, + fatfs_dir_entry_t *dentry) +{ + fatfs_data_pos_t pos; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dir); + CYG_CHECK_DATA_PTRC(name); + CYG_CHECK_DATA_PTRC(dentry); + + CYG_TRACE2(TDE, "filename='%s' parent='%s'", name, dir->filename); + + fatfs_initpos(disk, dir, &pos); + + // Get free dir entry in parent dir + err = get_free_raw_dentry(disk, &pos); + if (err != ENOERR) + return err; + + // Create new file dir entry + + init_dir_entry(dentry, + name, + namelen, + __stat_mode_REG, + dir->cluster, + 0, + &pos); + + err = fatfs_write_dir_entry(disk, dentry); + if (err != ENOERR) + return err; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_create_dir() +// Creates a new directory. + +int +fatfs_create_dir(fatfs_disk_t *disk, + fatfs_dir_entry_t *dir, + const char *name, + int namelen, + fatfs_dir_entry_t *dentry) +{ + fatfs_dir_entry_t cdentry; + fatfs_data_pos_t pos; + cyg_uint32 free_cluster; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dir); + CYG_CHECK_DATA_PTRC(name); + CYG_CHECK_DATA_PTRC(dentry); + + CYG_TRACE2(TDE, "filename='%s' parent='%s'", name, dir->filename); + + // Get free cluster + err = find_next_free_cluster(disk, + 0, + &free_cluster, + CO_MARK_LAST | CO_ERASE_NEW); + if (err != ENOERR) + return err; + + fatfs_initpos(disk, dir, &pos); + + // Get free dir entry in parent dir + err = get_free_raw_dentry(disk, &pos); + if (err != ENOERR) + return err; + + // Create new dir entry + + init_dir_entry(dentry, + name, + namelen, + __stat_mode_DIR, + dir->cluster, + free_cluster, + &pos); + + err = fatfs_write_dir_entry(disk, dentry); + if (err != ENOERR) + return err; + + // Create '.' and '..' dir entries + + fatfs_initpos(disk, dentry, &pos); + + CYG_TRACE0(TDE, "Creating '.' entry"); + + init_dir_entry(&cdentry, + ".", + 0, + __stat_mode_DIR, + dentry->cluster, + dentry->cluster, + &pos); + + err = fatfs_write_dir_entry(disk, &cdentry); + if (err != ENOERR) + return err; + + pos.cluster_pos += DENTRY_SIZE; + + CYG_TRACE0(TDE, "Creating '..' entry"); + + init_dir_entry(&cdentry, + "..", + 0, + __stat_mode_DIR, + dentry->cluster, + dir->cluster, + &pos); + + err = fatfs_write_dir_entry(disk, &cdentry); + if (err != ENOERR) + return err; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_trunc_file() +// Truncates a file to zero length. + +int +fatfs_trunc_file(fatfs_disk_t *disk, fatfs_dir_entry_t *file) +{ + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(file); + + CYG_TRACE1(TDE, "file='%s'", file->filename); + + if (S_ISDIR(file->mode)) + return EINVAL; + + if (0 == file->size) + return ENOERR; + + err = free_cluster_chain(disk, file->cluster); + if (err != ENOERR) + return err; + + // Update file attributes + + file->cluster = 0; + file->size = 0; + file->mtime = + file->atime = cyg_timestamp(); + + return fatfs_write_dir_entry(disk, file); +} + +// ------------------------------------------------------------------------- +// fatfs_rename_file() +// Renames a file. + +int +fatfs_rename_file(fatfs_disk_t *disk, + fatfs_dir_entry_t *dir1, + fatfs_dir_entry_t *target, + fatfs_dir_entry_t *dir2, + const char *name, + int namelen) +{ + fat_raw_dir_entry_t raw_dentry; + fatfs_data_pos_t new_pos; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dir1); + CYG_CHECK_DATA_PTRC(target); + CYG_CHECK_DATA_PTRC(dir2); + CYG_CHECK_DATA_PTRC(name); + + if (is_root_dir_entry(target)) + return EINVAL; + + strncpy(target->filename, name, namelen); + target->filename[namelen] = '\0'; + + // Moving around in same dir + + if (dir1 == dir2) + { + CYG_TRACE0(TDE, "same dir"); + return fatfs_write_dir_entry(disk, target); + } + + CYG_TRACE0(TDE, "different dirs"); + + // Moving around in different dirs + + fatfs_initpos(disk, dir2, &new_pos); + + CYG_TRACE0(TDE, "writing to new dir"); + + // Get free dir entry in target dir + + err = get_free_raw_dentry(disk, &new_pos); + if (err != ENOERR) + return err; + + // Write file dentry to new location + + dentry_to_raw(target, &raw_dentry); + err = write_raw_dentry(disk, &new_pos, &raw_dentry); + if (err != ENOERR) + return err; + + CYG_TRACE0(TDE, "deleting from old dir"); + + // Delete dentry at old location + + raw_dentry_set_deleted(disk, &raw_dentry); + raw_dentry.size = 0; + raw_dentry.cluster = 0; + raw_dentry.cluster_HI = 0; + err = write_raw_dentry(disk, &target->disk_pos, &raw_dentry); + if (err != ENOERR) + return err; + + // Set file new position and parent cluster + + target->disk_pos = new_pos; + target->parent_cluster = dir2->cluster; + + // If we moved a directory, we also have to correct the '..' entry + + if ( S_ISDIR(target->mode) ) + { + fat_raw_dir_entry_t raw_cdentry; + fatfs_data_pos_t pos; + + fatfs_initpos(disk, target, &pos); + + CYG_TRACE0(TDE, "moving directory - correcting '..' entry"); + + while (true) + { + err = read_next_raw_dentry(disk, &pos, &raw_cdentry); + + if (EEOF == err) + return EINVAL; // This dir doesn't have the '..' entry, + // that means something is very wrong + else if (err != ENOERR) + return err; + + if (0 == strncmp("..", raw_cdentry.name, 2)) + { + raw_cdentry.cluster = dir2->cluster & 0xFFFF; + raw_cdentry.cluster_HI = dir2->cluster >> 16; + err = write_raw_dentry(disk, &pos, &raw_cdentry); + if (err != ENOERR) + return err; + break; + } + + pos.cluster_pos += DENTRY_SIZE; + } + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_read_data() +// Reads data from disk. + +int +fatfs_read_data(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos, + void *data, + cyg_uint32 *len) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(file); + CYG_CHECK_DATA_PTRC(data); + CYG_CHECK_DATA_PTRC(len); + CYG_CHECK_DATA_PTRC(pos); + + return read_data(disk, data, len, pos); +} + +// ------------------------------------------------------------------------- +// fatfs_write_data() +// Writes data to disk. + +int +fatfs_write_data(fatfs_disk_t *disk, + fatfs_dir_entry_t *file, + fatfs_data_pos_t *pos, + void *data, + cyg_uint32 *len) +{ + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(file); + CYG_CHECK_DATA_PTRC(data); + CYG_CHECK_DATA_PTRC(len); + CYG_CHECK_DATA_PTRC(pos); + + // Check if this file has a zero size and no first cluster + + if (0 == file->size && 0 == file->cluster) + { + cyg_uint32 free_cluster; + + CYG_TRACE0(TDO, "new cluster for zero file"); + + err = find_next_free_cluster(disk, 0, &free_cluster, CO_MARK_LAST); + + if (err != ENOERR) + { + *len = 0; + return err; + } + + file->cluster = free_cluster; + fatfs_initpos(disk, file, pos); + } + + return write_data(disk, data, len, pos); +} + +// ------------------------------------------------------------------------- +// Support routines +// These enable the definition of local versions of certain routines +// if the given packages are not present. + +#ifndef CYGPKG_LIBC_I18N + +__externC int +toupper( int c ) +{ + return (('a' <= c) && (c <= 'z')) ? c - 'a' + 'A' : c ; +} + +#endif + +#ifndef CYGFUN_LIBC_STRING_BSD_FUNCS + +__externC int +strcasecmp( const char *s1, const char *s2 ) +{ + int ret; + CYG_REPORT_FUNCNAMETYPE( "strcasecmp", "returning %d" ); + CYG_REPORT_FUNCARG2( "s1=%08x, s2=%08x", s1, s2 ); + + CYG_CHECK_DATA_PTR( s1, "s1 is not a valid pointer!" ); + CYG_CHECK_DATA_PTR( s2, "s2 is not a valid pointer!" ); + + while (*s1 != '\0' && toupper(*s1) == toupper(*s2)) + { + s1++; + s2++; + } + + ret = toupper(*(unsigned char *) s1) - toupper(*(unsigned char *) s2); + CYG_REPORT_RETVAL( ret ); + return ret; +} + +__externC int +strncasecmp( const char *s1, const char *s2, size_t n ) +{ + int ret; + CYG_REPORT_FUNCNAMETYPE( "strncasecmp", "returning %d" ); + CYG_REPORT_FUNCARG3( "s1=%08x, s2=%08x, n=%d", s1, s2, n ); + + if (n == 0) + { + CYG_REPORT_RETVAL(0); + return 0; + } + + CYG_CHECK_DATA_PTR( s1, "s1 is not a valid pointer!" ); + CYG_CHECK_DATA_PTR( s2, "s2 is not a valid pointer!" ); + + while (n-- != 0 && toupper(*s1) == toupper(*s2)) + { + if (n == 0 || *s1 == '\0' || *s2 == '\0') + break; + s1++; + s2++; + } + + ret = toupper(*(unsigned char *) s1) - toupper(*(unsigned char *) s2); + CYG_REPORT_RETVAL( ret ); + return ret; +} + +#endif + +// ------------------------------------------------------------------------- +// EOF fatfs_supp.c diff --git a/ecos/packages/fs/fat/current/src/fatfs_tcache.c b/ecos/packages/fs/fat/current/src/fatfs_tcache.c new file mode 100644 index 0000000..3eaab75 --- /dev/null +++ b/ecos/packages/fs/fat/current/src/fatfs_tcache.c @@ -0,0 +1,285 @@ +//========================================================================== +// +// fatfs_tcache.c +// +// FAT file system FAT table cache functions +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Date: 2003-06-27 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/fs_fat.h> +#include <pkgconf/infra.h> +#include <cyg/kernel/kapi.h> +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/cyg_trac.h> +#include <cyg/infra/diag.h> +#include <sys/types.h> + +#include "fatfs.h" + +//========================================================================== +// Defines & macros + +#define TC_INC FATFS_FAT_TABLE_CACHE_INCREMENT + +#ifdef CYGDBG_USE_ASSERTS +# define USE_ASSERTS 1 +#endif + +#ifdef FATFS_TRACE_FAT_TABLE_CACHE +# define TTC 1 +#else +# define TTC 0 +#endif + +//========================================================================== +// Private functions + +//-------------------------------------------------------------------------- +// tcache_increment() +// Allocates or reallocates memory for FAT table cache. +// Returns true is allocation succeded. + +static bool +tcache_increment(fatfs_disk_t *disk, fatfs_tcache_t *tcache) +{ + CYG_TRACE2(TTC, "max_size=%d size=%d", tcache->max_size, tcache->size); + + if (NULL == tcache->clusters) + { + tcache->clusters = + (cyg_uint32 *)cyg_mempool_var_try_alloc(disk->tcache_mpool_h, + TC_INC*sizeof(cyg_uint32)); + + if (NULL == tcache->clusters) + return false; + + tcache->max_size = TC_INC; + } + else + { + cyg_uint32 *newc; + + newc = (cyg_uint32 *)cyg_mempool_var_try_alloc(disk->tcache_mpool_h, + (tcache->max_size+TC_INC)*sizeof(cyg_uint32)); + + if (NULL == newc) + return false; + + memcpy(newc, tcache->clusters, (tcache->max_size*sizeof(cyg_uint32))); + cyg_mempool_var_free(disk->tcache_mpool_h, tcache->clusters); + + tcache->clusters = newc; + tcache->max_size += TC_INC; + } + + CYG_TRACE2(TTC, "max_size=%d size=%d", tcache->max_size, tcache->size); + + return true; +} + +//========================================================================== +// Exported functions + +//-------------------------------------------------------------------------- +// fatfs_tcache_create() +// Creates FAT table caches memory pool. + +int +fatfs_tcache_create(fatfs_disk_t *disk, cyg_uint32 mem_size) +{ + disk->tcache_mem = (cyg_uint8 *)malloc(mem_size); + if (NULL == disk->tcache_mem) + return ENOMEM; + + cyg_mempool_var_create(disk->tcache_mem, mem_size, + &disk->tcache_mpool_h, &disk->tcache_mpool); + return ENOERR; +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_delete() +// Deletes FAT table caches memory pool. + +void +fatfs_tcache_delete(fatfs_disk_t *disk) +{ + cyg_mempool_var_delete(disk->tcache_mpool_h); + free(disk->tcache_mem); +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_init() +// Initializes FAT table cache structure. + +void +fatfs_tcache_init(fatfs_disk_t *disk, fatfs_tcache_t *tcache) +{ + CYG_CHECK_DATA_PTRC(tcache); + + tcache->clusters = NULL; + tcache->size = 0; + tcache->max_size = 0; +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_flush() +// Frees given tcache. + +void +fatfs_tcache_flush(fatfs_disk_t *disk, fatfs_tcache_t *tcache) +{ + CYG_CHECK_DATA_PTRC(tcache); + + if (tcache->clusters != NULL) + { + cyg_mempool_var_free(disk->tcache_mpool_h, tcache->clusters); + tcache->clusters = NULL; + tcache->size = 0; + tcache->max_size = 0; + } +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_get() +// Gets the cluster from cache at given position. +// Returns true if cluster in cache. + +bool +fatfs_tcache_get(fatfs_disk_t *disk, + fatfs_tcache_t *tcache, + cyg_uint32 num, + cyg_uint32 *cluster) +{ + CYG_CHECK_DATA_PTRC(tcache); + CYG_TRACE2(TTC, "size=%d max_size=%d", tcache->size, tcache->max_size); + + // Check if requested cluster is cached + if (num >= tcache->size) + { + CYG_TRACE1(TTC, "cluster num=%d not in tcache", num); + return false; + } + + *cluster = tcache->clusters[num]; + CYG_TRACE2(TTC, "got cluster=%d num=%d from tcache", *cluster, num); + return true; +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_get() +// Gets last cluster in cache. +// Returns false if cache is empty. + +bool +fatfs_tcache_get_last(fatfs_disk_t *disk, + fatfs_tcache_t *tcache, + cyg_uint32 *num, + cyg_uint32 *cluster) +{ + CYG_CHECK_DATA_PTRC(tcache); + CYG_TRACE2(TTC, "size=%d max_size=%d", tcache->size, tcache->max_size); + + // Check if we have something in cache + if (tcache->size == 0) + return false; + + *num = tcache->size - 1; + *cluster = tcache->clusters[*num]; + CYG_TRACE2(TTC, "got last cluster=%d num=%d from tcache", *cluster, *num); + return true; +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_set() +// Sets given cluster in cache at given position. +// There can't be any holes in cache (ie in order to set cluster +// at position N all clusters from 0 to N-1 must be set before) +// Returns true if set succeded. + +bool +fatfs_tcache_set(fatfs_disk_t *disk, + fatfs_tcache_t *tcache, + cyg_uint32 num, + cyg_uint32 cluster) +{ + CYG_CHECK_DATA_PTRC(tcache); + CYG_TRACE4(TTC, "num=%d cluster=%d tcache size=%d max_size=%d", + num, cluster, tcache->size, tcache->max_size); + + if (num > tcache->size) + { + CYG_TRACE0(TTC, "won't make a hole in tcache"); + return false; + } + + if (num < tcache->size) + { + CYG_TRACE2(TTC, "set cluster=%d num=%d in tcache", cluster, num); + tcache->clusters[num] = cluster; + return true; + } + + // Here num is equal to size - check if we need to increment cache + if (tcache->size == tcache->max_size) + { + // If we can't get memory to increment the cache + // try to flush dead nodes fat table cache + if (!tcache_increment(disk, tcache)) + { + fatfs_node_flush_dead_tcache(disk); + if (!tcache_increment(disk, tcache)) + return false; + } + } + + tcache->size++; + tcache->clusters[num] = cluster; + + CYG_TRACE2(TTC, "added cluster=%d num=%d to tcache", cluster, num); + + return true; +} + +// ------------------------------------------------------------------------- +// EOF fatfs_tcache.c diff --git a/ecos/packages/fs/fat/current/tests/fatfs1.c b/ecos/packages/fs/fat/current/tests/fatfs1.c new file mode 100644 index 0000000..4e9268a --- /dev/null +++ b/ecos/packages/fs/fat/current/tests/fatfs1.c @@ -0,0 +1,773 @@ +//========================================================================== +// +// fatfs1.c +// +// Test fileio system +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public 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/io_fileio.h> +#include <pkgconf/fs_fat.h> + +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> +#include <dirent.h> +#include <stdio.h> + +#include <cyg/fileio/fileio.h> + +#include <cyg/infra/testcase.h> +#include <cyg/infra/diag.h> // HAL polled output +#include <cyg/fs/fatfs.h> + + + +//========================================================================== + +#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); +#ifdef CYGPKG_FS_FAT_RET_DIRENT_DTYPE + diag_printf(" d_type %2x", entry->d_type); +#endif + 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 %ld]", + sbuf.st_mode,sbuf.st_ino,sbuf.st_nlink,(long)sbuf.st_size); + } +#ifdef CYGPKG_FS_FAT_RET_DIRENT_DTYPE + if ((entry->d_type & S_IFMT) != (sbuf.st_mode & S_IFMT)) + CYG_TEST_FAIL("File mode's don't match between dirent and stat"); +#endif + } + + 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 %zd \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, (int)wrote ); + + size -= wrote; + } + + err = close( fd ); + if( err < 0 ) SHOW_RESULT( close, err ); +} + +//========================================================================== + +static void maxfile( char *name ) +{ + char buf[IOSIZE]; + int fd; + ssize_t wrote; + int i; + int err; + size_t size = 0; + size_t prevsize = 0; + + diag_printf("<INFO>: create maximal file %s\n",name); + diag_printf("<INFO>: This may take a few minutes\n"); + + 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 ); + + if( wrote >= 0 ) + size += wrote; + + if( (size-prevsize) > 100000 ) + { + diag_printf("<INFO>: size = %zd \n", size); + prevsize = size; + } + + } while( wrote == IOSIZE ); + + diag_printf("<INFO>: file size == %zd\n",size); + + err = close( fd ); + if( err < 0 ) SHOW_RESULT( close, err ); +} + +//========================================================================== + +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, (int)done ); + + if( done == 0 ) break; + + for( i = 0; i < done; i++ ) + if( buf[i] != i%256 ) + { + diag_printf("buf[%ld+%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 ); +} + +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES +//========================================================================== + +static void checkattrib(const char *name, + const cyg_fs_attrib_t test_attrib ) +{ + int err; + cyg_fs_attrib_t file_attrib; + + diag_printf("<INFO>: check attrib %s\n",name); + + err = cyg_fs_get_attrib(name, &file_attrib); + if( err != 0 ) SHOW_RESULT( stat, err ); + + if ( (file_attrib & S_FATFS_ATTRIB) != test_attrib ) + diag_printf("<FAIL>: attrib %s incorrect\n\tExpected %x Was %x\n", + name,test_attrib,(file_attrib & S_FATFS_ATTRIB)); +} +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + +//========================================================================== + +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, (int)done ); + + if( done == 0 ) break; + + wrote = write( fd1, buf, done ); + if( wrote != done ) SHOW_RESULT( write, (int) 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, (int)done1 ); + + done2 = read( fd2, buf2, IOSIZE ); + if( done2 < 0 ) SHOW_RESULT( read, (int)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, (int)ret ); + + if( strcmp( cwdbuf, cwd ) != 0 ) + { + diag_printf( "cwdbuf %s cwd %s\n",cwdbuf, cwd ); + CYG_TEST_FAIL( "Current directory mismatch"); + } +} + +//========================================================================== +// main + +int main( int argc, char **argv ) +{ + int err; + int existingdirents=-1; +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) + struct cyg_fs_block_usage usage; +#endif + + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + err = mount( CYGDAT_DEVS_DISK_TEST_DEVICE, "/", "fatfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + err = chdir( "/" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/" ); + + listdir( "/", true, -1, &existingdirents ); + + // -------------------------------------------------------------- +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) + err = cyg_fs_getinfo("/", FS_INFO_BLOCK_USAGE, &usage, sizeof(usage)); + if( err < 0 ) SHOW_RESULT( cyg_fs_getinfo, err ); + diag_printf("<INFO>: total size: %6lld blocks, %10lld bytes\n", + usage.total_blocks, usage.total_blocks * usage.block_size); + diag_printf("<INFO>: free size: %6lld blocks, %10lld bytes\n", + usage.free_blocks, usage.free_blocks * usage.block_size); + diag_printf("<INFO>: block size: %6u bytes\n", usage.block_size); +#endif + // -------------------------------------------------------------- + + createfile( "/foo", 20257 ); + 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" ); + +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) + err = cyg_fs_getinfo("/", FS_INFO_BLOCK_USAGE, &usage, sizeof(usage)); + if( err < 0 ) SHOW_RESULT( cyg_fs_getinfo, err ); + diag_printf("<INFO>: total size: %6lld blocks, %10lld bytes\n", + usage.total_blocks, usage.total_blocks * usage.block_size); + diag_printf("<INFO>: free size: %6lld blocks, %10lld bytes\n", + usage.free_blocks, usage.free_blocks * usage.block_size); + diag_printf("<INFO>: block size: %6u bytes\n", usage.block_size); +#endif + // -------------------------------------------------------------- + + 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 ); + + // -------------------------------------------------------------- + +#if 0 + diag_printf("<INFO>: mkdir disk2\n"); + err = mkdir( "/disk2", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); +#else + diag_printf("<INFO>: mount /disk2\n"); + err = mount( CYGDAT_DEVS_DISK_TEST_DEVICE2, "/disk2", "fatfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); +#endif + + listdir( "/disk2" , true, -1, &existingdirents); + + createfile( "/disk2/tinky", 4567 ); + copyfile( "/disk2/tinky", "/disk2/laalaa" ); + checkfile( "/disk2/tinky"); + checkfile( "/disk2/laalaa"); + comparefiles( "/disk2/tinky", "/disk2/laalaa" ); + + diag_printf("<INFO>: cd /disk2\n"); + err = chdir( "/disk2" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/disk2" ); + + diag_printf("<INFO>: mkdir noonoo\n"); + err = mkdir( "noonoo", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + listdir( "/disk2" , true, existingdirents+3, NULL); + + diag_printf("<INFO>: cd noonoo\n"); + err = chdir( "noonoo" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/disk2/noonoo" ); + + createfile( "tinky", 6789 ); + checkfile( "tinky" ); + + createfile( "dipsy", 34567 ); + checkfile( "dipsy" ); + copyfile( "dipsy", "po" ); + checkfile( "po" ); + comparefiles( "dipsy", "po" ); + + listdir( ".", true, 5, NULL ); + listdir( "", true, 5, NULL ); + listdir( "..", true, existingdirents+3, NULL ); + + // -------------------------------------------------------------- + + diag_printf("<INFO>: unlink tinky\n"); + err = unlink( "tinky" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: unlink dipsy\n"); + err = unlink( "dipsy" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: unlink po\n"); + err = unlink( "po" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: cd ..\n"); + err = chdir( ".." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2" ); + + 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 /disk2/x/y/z/w\n"); + err = chdir( "/disk2/x/y/z/w" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2/x/y/z/w" ); + + diag_printf("<INFO>: cd ..\n"); + err = chdir( ".." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2/x/y/z" ); + + diag_printf("<INFO>: cd .\n"); + err = chdir( "." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2/x/y/z" ); + + diag_printf("<INFO>: cd ../../y\n"); + err = chdir( "../../y" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2/x/y" ); + + diag_printf("<INFO>: cd ../..\n"); + err = chdir( "../.." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2" ); + + 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 ); + + // -------------------------------------------------------------- + + checkcwd( "/disk2" ); + + 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( "/" ); + + listdir( "/disk2", true, -1, NULL ); + +#if 0 + diag_printf("<INFO>: rmdir dir\n"); + err = rmdir( "disk2" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); +#else + diag_printf("<INFO>: umount /disk2\n"); + err = umount( "/disk2" ); + if( err < 0 ) SHOW_RESULT( umount, err ); +#endif + +#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES + // Create file + diag_printf("<INFO>: create /foo\n"); + createfile( "/foo", 20257 ); + + // Verify it is created with archive bit set + checkattrib( "/foo", S_FATFS_ARCHIVE ); + + // Make it System + diag_printf("<INFO>: attrib -A+S /foo\n"); + err = cyg_fs_set_attrib( "/foo", S_FATFS_SYSTEM ); + if( err < 0 ) SHOW_RESULT( chmod system , err ); + + // Verify it is now System + checkattrib( "/foo", S_FATFS_SYSTEM ); + + // Make it Hidden + diag_printf("<INFO>: attrib -S+H /foo\n"); + err = cyg_fs_set_attrib( "/foo", S_FATFS_HIDDEN ); + if( err < 0 ) SHOW_RESULT( chmod system , err ); + + // Verify it is now Hidden + checkattrib( "/foo", S_FATFS_HIDDEN ); + + // Make it Read-only + diag_printf("<INFO>: attrib -H+R /foo\n"); + err = cyg_fs_set_attrib( "/foo", S_FATFS_RDONLY ); + if( err < 0 ) SHOW_RESULT( chmod system , err ); + + // Verify it is now Read-only + checkattrib( "/foo", S_FATFS_RDONLY ); + + // Verify we cannot unlink a read-only file + diag_printf("<INFO>: unlink /foo\n"); + err = unlink( "/foo" ); + if( (err != -1) || (errno != EPERM) ) SHOW_RESULT( unlink, err ); + + // Verify we cannot rename a read-only file + diag_printf("<INFO>: rename /foo bundy\n"); + err = rename( "/foo", "bundy" ); + if( (err != -1) || (errno != EPERM) ) SHOW_RESULT( rename, err ); + + // Verify we cannot open read-only file for writing + int fd; + diag_printf("<INFO>: create file /foo\n"); + fd = open( "/foo", O_WRONLY ); + if( (err != -1) || (errno != EACCES) ) SHOW_RESULT( open, err ); + if( err > 0 ) close(fd); + + // Make it Normal + diag_printf("<INFO>: attrib -H /foo\n"); + err = cyg_fs_set_attrib( "/foo", 0 ); + if( err < 0 ) SHOW_RESULT( chmod none , err ); + + // Verify it is now nothing + checkattrib( "/foo", 0 ); + + // Now delete our test file + diag_printf("<INFO>: unlink /foo\n"); + err = unlink( "/foo" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + +#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES + + maxfile("file.max"); + + listdir( "/", true, -1, NULL ); + + diag_printf("<INFO>: unlink file.max\n"); + err = unlink( "file.max" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + diag_printf("<INFO>: umount /\n"); + err = umount( "/" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + CYG_TEST_PASS_FINISH("fatfs1"); +} + +// ------------------------------------------------------------------------- +// EOF fatfs1.c diff --git a/ecos/packages/fs/jffs2/current/ChangeLog b/ecos/packages/fs/jffs2/current/ChangeLog new file mode 100644 index 0000000..ae1567d --- /dev/null +++ b/ecos/packages/fs/jffs2/current/ChangeLog @@ -0,0 +1,545 @@ +2011-02-17 Jonathan Larmour <jifl@eCosCentric.com> + + * cdl/jffs2.cdl: Add CYGNUM_FS_JFFS2_TEST_OFFSET and + CYGNUM_FS_JFFS2_TEST_LENGTH, which should have been included in change + of 2008-11-18. + +2009-04-28 John Dallaway <john@dallaway.org.uk> + + * cdl/jffs2.cdl: Use CYGPKG_IO_FILEIO as the parent. + +2009-02-22 Andrew Lunn <andrew.lunn@ascom.ch> + + * cdl/jffs2.cdl: Add include-fixed to the system include files we + should search. Without this gcc 4.3 breaks. + +2008-11-18 Jonathan Larmour <jifl@eCosCentric.com> + + * tests/jffs2_1.c, tests/jffs2_2.c, tests/jffs2_3.c: + Select device name to use according to configury, including flash + API in use. + +2008-06-09 Jonathan Larmour <jifl@eCosCentric.com> + + * cdl/jffs2.cdl (CYGPKG_FS_JFFS2_CFLAGS_ADD): Conditionalise use of + workaround for building with native linux tools so that it only + applies when using synth target. Otherwise it can cause problems + with some other third-party builds of the toolchain. + +2004-07-14 Andrew Lunn <andrew.lunn@ascom.ch> + + * tests/fileio1.c (main): If we have two filesystem configured + mount the second one on /mnt and list the root of it. + +2008-04-02 Xinghua Yang <yxinghua@sunnorth.com.cn> + Taiyun Wang <taiyun@sunnorth.com.cn> + Andrew Lunn <andrew.lunn@ascom.ch> + + * cdl/jffs2.cdl: Use CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE to control + whether jffs2 sets file type in jffs2_fo_dirread. + * src/fs-ecos.c: Set file type in jffs2_fo_dirread. + * tests/jffs2_1.c: Test the new d_type in dirent when present. + +2006-03-09 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/fs-ecos.c: Generalise the check for broken ARM compilers + and add the MIPS compiler as also being broken. + +2005-08-03 Andrew Lunn <andrew.lunn@ascom.ch> + + * tests/jffs2_1.c: Include <stdio.h> to stop compiler warning. + +2005-07-30 Andrew Lunn <andrew.lunn@ascom.ch> + + * Merge to public MTD + +2005-02-25 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/fs-ecos.c (jffs2_truncate_file): Correctly truncate a file + when opening it with O_TRUNC. Test case from Knud Woehler. + +2005-02-08 Estelle HAMMACHE <estelle.hammache@st.com> + + * src/fs-ecos.c + * src/dir-ecos.c: Fixed the sign of the return codes in various + places. iput the inode only after we have finished with + it etc. + +2005-01-22 Andrew Lunn <andrew.lunn@ascom.ch> + + * Merge from public MTD. + +2004-11-14 Per Hedblom <perhedblom@abem.se> + Andrew Lunn <andrew.lunn@ascom.ch> + + * src/os-ecos.h: Added gc tread support + * src/fs-ecos.c: Removed some includes + * cdl/jffs2.cdl: Added gc thread support + * tests/jffs2_3.c: New text to exersise the garbage collect code. + +2004-12-13 John Dallaway <jld@ecoscentric.com> + + * tests/fileio1.c: Rename to jffs2_1.c. eCos test names should be + unique. + * tests/fseek1.c: Rename to jffs2_2.c. + * cdl/jffs2.cdl: Build the renamed tests. + +2004-12-10 Per Hedblom <per.hedblom@abem.se> + + * src/fs-ecos.c: Use filesystem locking not file locking otherwise + the node cache can be corrupted by multiple operations on + different files at the same time. + +2004-11-11 Andrew Lunn <andrew.lunn@ascom.ch> + + * Merge from public MTD. + +2004-11-11 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/build.c: Removed Oyvind Harboe's patch from 2004-04-19 + since it was rejected upstream. + +2004-10-20 Per Hedblom <perhedblom@abem.se> + + * src/fs-ecos.c (jffs2_open): Call iput on dir node if + jffs2_create fails. + * src/fs-ecos.c (jffs2_umount): use jffs2_free_full_dirent instead + of free. + +2004-10-07 Knud Woehler <knud.woehler@microplex.de> + + * src/fs-ecos.c (find_entry): jffs2_lookup may return error codes. + Check added. + +2004-09-16 Dirk Eibach <eibach@gdsys.de> +2004-09-19 Andrew Lunn <andrew.lunn@ascom.ch> + + * cdl/jffs2.cdl: Fixed outdated definitions for compression options. + * src/fs-ecos.c: Added missing calls for jffs2_compressors_init() + and jffs2_compressors_exit() + +2004-08-13 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/compr.h: Committed this file which if forgot yesterday. + +2004-08-12 Andrew Lunn <andrew.lunn@ascom.ch> + Gary Thomas <gary@mlbassoc.com> + + * Merge from public MTD. + +2004-07-10 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/flashio.c (jffs2_flash_erase): Minor update for new + flash API. + +2004-04-19 Oyvind Harboe <oyvind.harboe@zylin.com> + + * src/build.c: JFFS2 can now be used as a write-once, read many mode + for really small flash disks, e.g. configuration parameters. + +2004-04-21 Gary Thomas <gary@mlbassoc.com> + + * src/fs-ecos.c: Merge from public MTD - verify file position + after each operation (safety check). + +2004-04-06 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/fs-ecos.c (jffs2_extend_file): Fix creation of hole inode's + * src/fs-ecos.c (jffs2_fo_lseek): Allow seeking past EOF. + * tests/fseek1.c (main): fseek and simple test of a hole in a file + * cdl/jffs2.cdl: Added new test program fseek1. + +2004-03-31 David Woodhouse <dwmw2@infradead.org> + + * src/fs-ecos.c (jffs2_fo_write): Set ri.isize so that non-append + writes don't truncate the file. + +2004-03-03 Thomas Koeller <thomas.koeller@baslerweb.com> + + * src/fs-ecos.c: + Make JFFS2 honor O_TRUNC flag (again) when opening files with read + Access. + +2004-03-17 Oyvind Harboe <oyvind.harboe@zylin.com> + + * src/fs-ecos.c: + With CYGOPT_FS_JFFS2_WRITE=1, file creation failed. The problem + was introduced in fs-ecos.c 1.20 + +2004-03-11 Oyvind Harboe <oyvind.harboe@zylin.com> + + * src/fs-ecos.c: + Fixed umount memory leak. root->jffs2_i.dents where not freed. + +2004-02-20 Vincent Catros <Vincent.Catros@elios-informatique.fr> + + * src/fs-ecos.c : + (jffs2_find) Policy to skip path separator is no longer + "if '/' then skip" but "while '/' then skip" allowing + multi '/' separators (i.e : /tmp////foo). + (find_entry) Policy to detect end of path is no longer + "if '\0' then end_of_path" + but "while '/' skip it and then if '\0' then end_of_path" + allowing path terminated with any number of '/' + (i.e : chdir(/tmp///)). + +2004-03-03 Thomas Koeller <thomas.koeller@baslerweb.com> + + * src/fs-ecos.c: + Make JFFS2 honor O_TRUNC flag when opening files /w read access. + +2004-02-17 David Woodhouse <dwmw2@infradead.org> + + * src/fs-ecos.c: + Don't re-initialise the already-locked f->sem. It makes eCos unhappy. + +2004-01-27 David Woodhouse <dwmw2@infradead.org> + + * src/write.c: + Fix bug noted by Howard Gray; dirents belong to, and should dirty, + the _parent_inode, not the child (which may be zero in the case + of an unlink). + +2004-01-09 Thomas Koeller <thomas.koeller@baslerweb.com> + + * src/fs-ecos.c: Fixed inode reference counting in jffs2_ops_link(). + +2004-01-05 Thomas Koeller <thomas.koeller@baslerweb.com> + + * cdl/jffs2.cdl: Re-added CYGPKG_FS_JFFS2_CFLAGS_REMOVE that had been + removed by previous change. + +2003-11-26 David Woodhouse <dwmw2@infradead.org> + + JFFS2 cleanup and import of newer code. Remove last vestiges of + Linuxisms such as 'struct inode' from the core code, leaving eCos + with no excuse, and renaming the eCos 'struct inode' to make that + point. Fix i_count handling throughout. Clean up remaining Linuxisms + such as 'struct qstr' to the point where jffs2port.h can be removed. + Add skeleton for background garbage-collect thread. Fix compression + so that it's actually called, and even with the right pointers. Turn + on -Werror again. Zero tolerance is a good thing. Make the i_mode + conversion functions non-inline to avoid warnings. Fix namespace + pollution (of all but ^jffs2_* at least). Move physical flash I/O + functions into separate file flashio.c for relatively easy + substitution. Various other cruftectomy. + +2003-11-25 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/fs-ecos.c: ARM gcc 3.2.3 is also broken. Complain with any + ARM gcc 3.2 compiler. + +2003-11-21 Thomas Koeller <thomas.koeller@baslerweb.com> + + * cdl/jffs2.cdl: Do not require zlib package if no + compression configured. + +2003-11-20 Thomas Koeller <thomas.koeller@baslerweb.com> + + * cdl/jffs2.cdl: + * src/malloc-ecos.c: + * src/fs-ecos.c: Allocate jffs2_raw_node_ref structs + from pool or malloc depending on a CDL configuration. + +2003-11-20 David Woodhouse <dwmw2@infradead.org> + + * Rearrangement of the compression code into a cleaner API + which can be disabled under control from CDL. + +2003-10-14 Thomas Koeller <thomas.koeller@baslerweb.com> + + * src/os-ecos.h: Made definition of CONFIG_JFFS2_FS_DEBUG + conditional, so it can be overwritten by a -D compiler + option. + +2003-09-23 Thomas Koeller <thomas.koeller@baslerweb.com> + + * src/fs-ecos.c: Another umount() fix. + +2003-09-23 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/fs-ecos.c: Added test to detect known broken ARM compiler + +2003-09-23 Thomas Koeller <thomas.koeller@baslerweb.com> + + * src/fs-ecos.c: Fixed broken hard link creation. + +2003-09-23 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/fs-ecos.c: Minor optimization of previous patch. + +2003-09-21 Thomas Koeller <thomas.koeller@baslerweb.com> + Andrew Lunn <andrew.lunn@ascom.ch> + + * src/fs-ecos.c: Do not decrement mount count for unsuccessful + umount attempts. + +2003-09-19 Thomas Koeller <thomas.koeller@baslerweb.com> + + * src/fs-ecos.c: Another inode number fix. + +2003-09-18 Thomas Koeller <thomas.koeller@baslerweb.com> + + * src/fs-ecos.c: Inode number returned by stat() was bogus. + +2003-07-27 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/os-ecos.h: Added new #defines require for the recent jffs2 + import. + +2003-07-27 Michael Checky <Michael_Checky@ThermoKing.com> + + * src/fs-ecos.c: Changed the return error code to be negative as + expected by jffs2_flash_read() and jffs2_flash_write(). + +2003-04-23 Bob Koninckx <bob.koninckx@mech.kuleuven.ac.be> + + * src/fs-ecos.c: mtab -> cyg_mtab. Ditto cyg_mtab_end. + +2003-03-25 Thomas Koeller <thomas.koeller@baslerweb.com> + + * src/fs-ecos.c + Fixed segmentation fault when unmounting file system. + +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 + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2009 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== + + diff --git a/ecos/packages/fs/jffs2/current/cdl/jffs2.cdl b/ecos/packages/fs/jffs2/current/cdl/jffs2.cdl new file mode 100644 index 0000000..ad6a3cb --- /dev/null +++ b/ecos/packages/fs/jffs2/current/cdl/jffs2.cdl @@ -0,0 +1,318 @@ +# ==================================================================== +# +# jffs2.cdl +# +# JFFS2 Filesystem configuration data +# +# $Id: jffs2.cdl,v 1.19 2005/02/09 09:23:54 pavlov Exp $ +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2009 Free Software Foundation, 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## This exception does not invalidate any other reasons why a work based +## on this file might be covered by the GNU General Public 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 "" + + parent CYGPKG_IO_FILEIO + requires CYGPKG_IO_FILEIO + requires CYGPKG_IO_FLASH + 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 dir-ecos.c read.c compr.c debug.c + # This could be overridden by an alternative direct I/O method. + compile flashio.c + + cdl_component CYGOPT_FS_JFFS2_GCTHREAD { + display "Support garbage-collection background thread" + flavor bool + default_value 0 + compile gcthread.c + requires CYGPKG_KERNEL + description " + Enable background garbage collection thread, for making + free space ahead of time. Leave this off till it's been + implemented. And don't implement it till icache locking has + been made thread-safe. + " + + + cdl_option CYGNUM_JFFS2_GC_THREAD_PRIORITY { + display "jffs2 gc thread priority" + flavor data + default_value { CYGNUM_KERNEL_SCHED_PRIORITIES-2 } + legal_values 0 to CYGNUM_KERNEL_SCHED_PRIORITIES + description "The jffs2 system contains one garbage collect thread." + } + + cdl_option CYGNUM_JFFS2_GC_THREAD_STACK_SIZE { + display "jffs2 gc stackstack size" + flavor data + legal_values 2048 to 0x7fffffff + default_value 8192 + description " + This option sets the size of the stack used + for jffs2 garbage collect thread" + } + + cdl_option CYGNUM_JFFS2_GS_THREAD_TICKS { + display "ticks between each garbage collect" + flavor data + default_value 100 + description " + This option sets how many clock ticks there will be between + each garbage collect operation triggered by the background + thread" + } + } + + cdl_option CYGOPT_FS_JFFS2_WRITE { + display "Include write support for JFFS2" + flavor bool + compile gc.c write.c erase.c + default_value 1 + description " + Enable writing to JFFS2 file systems; not only reading." + } + + cdl_option CYGOPT_FS_JFFS2_NAND { + display "Support for NAND flash" + flavor bool + define CONFIG_JFFS2_FS_WRITEBUFFER + compile wbuf.c + requires 0 + description " + Enable support for JFFS2 on NAND flash." + } + + cdl_option CYGOPT_FS_JFFS2_DEBUG { + display "Debug level" + flavor data + default_value 0 + legal_values 0 to 2 + define CONFIG_JFFS2_FS_DEBUG + description " + Debug verbosity of JFFS2 code. Zero is normal operation + without debugging. Level 1 adds extra sanity checks and + fairly verbose output. Level 2 is insanely loquacious." + } + + cdl_component CYGOPT_FS_JFFS2_COMPRESS { + display "Compress data" + flavor bool + define JFFS2_COMPRESSION + default_value 1 + description " + Compression and decompression are entirely handled by the file + system and are fully transparent to applications. However, + selecting this option increases the amount of RAM required and + slows down read and write operations considerably if you have a + slow CPU." + + cdl_option CYGOPT_FS_JFFS2_COMPRESS_ZLIB { + display "Compress data using zlib" + flavor bool + define CONFIG_JFFS2_ZLIB + requires CYGPKG_COMPRESS_ZLIB + compile compr_zlib.c + default_value 1 + description " + Use zlib for compression of data. This is the slowest of the + compression options available but the most effective." + } + + cdl_option CYGOPT_FS_JFFS2_COMPRESS_RTIME { + display "Compress data using rtime" + flavor bool + define CONFIG_JFFS2_RTIME + compile compr_rtime.c + default_value 1 + description " + Use the rtime algorithm for compression of data. This + simple algorithm often manages to squeeze and extra few + bytes from data already compressed with gzip." + } + + cdl_option CYGOPT_FS_JFFS2_COMPRESS_RUBIN { + display "Compress data using rubin" + flavor bool + define CONFIG_JFFS2_RUBIN + requires CYGOPT_FS_JFFS2_COMPRESS + compile compr_rubin.c + description " + Use the rubin algorithm for compression of data. This + simple algorithm is faster than zlib but not quite as + effective." + } + cdl_option CYGOPT_FS_JFFS2_COMPRESS_CMODE { + display "Set the default compression mode" + flavor data + default_value { "PRIORITY" } + legal_values { "NONE" "PRIORITY" "SIZE" } + define CONFIG_JFFS2_CMODE + description " + You can set here the default compression mode of JFFS2 from + the avaiable compression modes. NONE causes no compression to + be performed. PRIORITY tries the compressors in a predefined + order and chooses the first successfull one. SIZE tries all + compressors and chooses the one which has the smallest result" + } + } + cdl_option CYGNUM_FS_JFFS2_RAW_NODE_REF_CACHE_POOL_SIZE { + display "Memory pool size" + flavor data + default_value 0 + description " + In order to manage data stored in flash, the file system + needs to use data structures (jffs2_raw_node_ref) allocated + in RAM. You can specify here the maximum number of such + structures you expect to be used, which will then be allocated + statically. If this option is set to 0, the structures will + be allocated dynamically via malloc(), which may incur some + memory overhead depending on the particular malloc() + implementation used." + } + + 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 '-nostdinc -iwithprefix include' to avoid picking up + # native <linux/*.h> include files when building on Linux. + default_value { "-D__ECOS " . \ + (CYGPKG_HAL_SYNTH ? " -nostdinc -iwithprefix include -iwithprefix include-fixed" : "") } + 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 + default_value { "" } + 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." + } + + cdl_option CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE { + display "Support for fileio's struct dirent d_type field" + flavor bool + default_value 0 + active_if CYGPKG_FILEIO_DIRENT_DTYPE + description " + This option controls whether the JFFS2 filesystem supports + setting fileio's struct dirent d_type field. + If this option is enabled, d_type will be set. Otherwise, + nothing will be done, d_type's value will be zero because + fileio already sets it." + } + + # ---------------------------------------------------------------- + # Tests + + cdl_component CYGPKG_FS_JFFS2_TESTS { + display "JFFS2 FS tests" + flavor data + no_define + calculated { "tests/jffs2_1 tests/jffs2_2 tests/jffs2_3" } + description " + This option specifies the set of tests for the JFFS2 + FS package. + If supported by the configuration, these tests will preferentially + use a FIS partition (which may be defined using RedBoot) named + \"jffs2test\". If that FIS partition does not exist, then no testing + will be performed to avoid any risk of corruption of live data. + Alternatively if the configuration does not support that activity, + a hard coded specification may be set." + + cdl_component CYGPKG_FS_JFFS2_TEST_DEVICE { + display "Fixed Flash test area specs" + no_define + default_value !CYGFUN_IO_FLASH_BLOCK_FROM_FIS + description " + This component gives specifications of the flash region to use + for testing. It usually only applies when specification via FIS is not + possible." + + cdl_option CYGNUM_FS_JFFS2_TEST_OFFSET { + display "Start offset from flash base" + flavor data + default_value CYGNUM_IO_FLASH_TEST_OFFSET + description " + This gives the offset from the base of the first flash device + of where tests may operate. It is important to set this correctly, + as an incorrect value could allow the tests to write over critical + portions of the FLASH device and possibly render the target + board totally non-functional." + } + cdl_option CYGNUM_FS_JFFS2_TEST_LENGTH { + display "Test area length" + flavor data + default_value CYGNUM_IO_FLASH_TEST_LENGTH + description " + This gives the length of the region of flash used for testing." + } + } + } +} + +# End of jffs2.cdl diff --git a/ecos/packages/fs/jffs2/current/doc/README.Locking b/ecos/packages/fs/jffs2/current/doc/README.Locking new file mode 100644 index 0000000..dffacd5 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/doc/README.Locking @@ -0,0 +1,155 @@ + $Id: README.Locking,v 1.12 2005/04/13 13:22:35 dwmw2 Exp $ + + JFFS2 LOCKING DOCUMENTATION + --------------------------- + +At least theoretically, JFFS2 does not require the Big Kernel Lock +(BKL), which was always helpfully obtained for it by Linux 2.4 VFS +code. It has its own locking, as described below. + +This document attempts to describe the existing locking rules for +JFFS2. It is not expected to remain perfectly up to date, but ought to +be fairly close. + + + alloc_sem + --------- + +The alloc_sem is a per-filesystem semaphore, used primarily to ensure +contiguous allocation of space on the medium. It is automatically +obtained during space allocations (jffs2_reserve_space()) and freed +upon write completion (jffs2_complete_reservation()). Note that +the garbage collector will obtain this right at the beginning of +jffs2_garbage_collect_pass() and release it at the end, thereby +preventing any other write activity on the file system during a +garbage collect pass. + +When writing new nodes, the alloc_sem must be held until the new nodes +have been properly linked into the data structures for the inode to +which they belong. This is for the benefit of NAND flash - adding new +nodes to an inode may obsolete old ones, and by holding the alloc_sem +until this happens we ensure that any data in the write-buffer at the +time this happens are part of the new node, not just something that +was written afterwards. Hence, we can ensure the newly-obsoleted nodes +don't actually get erased until the write-buffer has been flushed to +the medium. + +With the introduction of NAND flash support and the write-buffer, +the alloc_sem is also used to protect the wbuf-related members of the +jffs2_sb_info structure. Atomically reading the wbuf_len member to see +if the wbuf is currently holding any data is permitted, though. + +Ordering constraints: See f->sem. + + + File Semaphore f->sem + --------------------- + +This is the JFFS2-internal equivalent of the inode semaphore i->i_sem. +It protects the contents of the jffs2_inode_info private inode data, +including the linked list of node fragments (but see the notes below on +erase_completion_lock), etc. + +The reason that the i_sem itself isn't used for this purpose is to +avoid deadlocks with garbage collection -- the VFS will lock the i_sem +before calling a function which may need to allocate space. The +allocation may trigger garbage-collection, which may need to move a +node belonging to the inode which was locked in the first place by the +VFS. If the garbage collection code were to attempt to lock the i_sem +of the inode from which it's garbage-collecting a physical node, this +lead to deadlock, unless we played games with unlocking the i_sem +before calling the space allocation functions. + +Instead of playing such games, we just have an extra internal +semaphore, which is obtained by the garbage collection code and also +by the normal file system code _after_ allocation of space. + +Ordering constraints: + + 1. Never attempt to allocate space or lock alloc_sem with + any f->sem held. + 2. Never attempt to lock two file semaphores in one thread. + No ordering rules have been made for doing so. + + + erase_completion_lock spinlock + ------------------------------ + +This is used to serialise access to the eraseblock lists, to the +per-eraseblock lists of physical jffs2_raw_node_ref structures, and +(NB) the per-inode list of physical nodes. The latter is a special +case - see below. + +As the MTD API no longer permits erase-completion callback functions +to be called from bottom-half (timer) context (on the basis that nobody +ever actually implemented such a thing), it's now sufficient to use +a simple spin_lock() rather than spin_lock_bh(). + +Note that the per-inode list of physical nodes (f->nodes) is a special +case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in +the list are protected by the file semaphore f->sem. But the erase +code may remove _obsolete_ nodes from the list while holding only the +erase_completion_lock. So you can walk the list only while holding the +erase_completion_lock, and can drop the lock temporarily mid-walk as +long as the pointer you're holding is to a _valid_ node, not an +obsolete one. + +The erase_completion_lock is also used to protect the c->gc_task +pointer when the garbage collection thread exits. The code to kill the +GC thread locks it, sends the signal, then unlocks it - while the GC +thread itself locks it, zeroes c->gc_task, then unlocks on the exit path. + + + inocache_lock spinlock + ---------------------- + +This spinlock protects the hashed list (c->inocache_list) of the +in-core jffs2_inode_cache objects (each inode in JFFS2 has the +correspondent jffs2_inode_cache object). So, the inocache_lock +has to be locked while walking the c->inocache_list hash buckets. + +This spinlock also covers allocation of new inode numbers, which is +currently just '++->highest_ino++', but might one day get more complicated +if we need to deal with wrapping after 4 milliard inode numbers are used. + +Note, the f->sem guarantees that the correspondent jffs2_inode_cache +will not be removed. So, it is allowed to access it without locking +the inocache_lock spinlock. + +Ordering constraints: + + c->erase_completion_lock and c->inocache_lock has special ordering: + 1. c->erase_completion_lock (must be locked first) + 2. c->inocache_lock + + + erase_free_sem + -------------- + +This semaphore is used by the erase code which frees obsolete +node references and the jffs2_garbage_collect_deletion_dirent() +function. The latter function on NAND flash must read _obsolete_ nodes +to determine whether the 'deletion dirent' under consideration can be +discarded or whether it is still required to show that an inode has +been unlinked. Because reading from the flash may sleep, the +erase_completion_lock can not be held, so an alternative, more +heavyweight lock was required to prevent the erase code from freeing +the jffs2_raw_node_ref structures in question while the garbage +collection code is looking at them. + +The erase_free_sem mutex is also used in the jffs2_mark_node_obsolete() +function which manipulates obsolete nodes (which may be removed +from the list and freed any time) and may sleep (since it reads flash). + + + wbuf_sem + -------- + +This read/write semaphore protects against concurrent access to the +write-behind buffer ('wbuf') used for flash chips where we must write +in blocks. It protects both the contents of the wbuf and the metadata +which indicates which flash region (if any) is currently covered by +the buffer. + +Ordering constraints: + Lock wbuf_sem last, after the alloc_sem or and f->sem. diff --git a/ecos/packages/fs/jffs2/current/doc/TODO b/ecos/packages/fs/jffs2/current/doc/TODO new file mode 100644 index 0000000..9d95901 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/doc/TODO @@ -0,0 +1,35 @@ +$Id: TODO,v 1.17 2004/04/14 20:11:38 gleixner Exp $ + + - support asynchronous operation -- add a per-fs 'reserved_space' count, + let each outstanding write reserve the _maximum_ amount of physical + space it could take. Let GC flush the outstanding writes because the + reservations will necessarily be pessimistic. With this we could even + do shared writable mmap, if we can have a fs hook for do_wp_page() to + make the reservation. + - disable compression in commit_write()? + - fine-tune the allocation / GC thresholds + - chattr support - turning on/off and tuning compression per-inode + - checkpointing (do we need this? scan is quite fast) + - make the scan code populate real inodes so read_inode just after + mount doesn't have to read the flash twice for large files. + Make this a per-inode option, changable with chattr, so you can + decide which inodes should be in-core immediately after mount. + - test, test, test + + - NAND flash support: + - almost done :) + - use bad block check instead of the hardwired byte check + + - Optimisations: + - Split writes so they go to two separate blocks rather than just c->nextblock. + By writing _new_ nodes to one block, and garbage-collected REF_PRISTINE + nodes to a different one, we can separate clean nodes from those which + are likely to become dirty, and end up with blocks which are each far + closer to 100% or 0% clean, hence speeding up later GC progress dramatically. + - Stop keeping name in-core with struct jffs2_full_dirent. If we keep the hash in + the full dirent, we only need to go to the flash in lookup() when we think we've + got a match, and in readdir(). + - Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately? + - Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into + jffs2_mark_node_obsolete(). Can all callers work it out? + - Remove size from jffs2_raw_node_frag. diff --git a/ecos/packages/fs/jffs2/current/doc/TODO.eCos b/ecos/packages/fs/jffs2/current/doc/TODO.eCos new file mode 100644 index 0000000..091fb79 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/doc/TODO.eCos @@ -0,0 +1,25 @@ +$Id: TODO.eCos,v 1.2 2003/11/28 11:15:56 dwmw2 Exp $ + + - Make symlinks work properly. + + - Fill in the skeleton gcthread.c so it actually does something. + + - Check and possibly fix locking of icache mangling in fs-ecos.c + + - Check that os-ecos.h defines 'spin_lock()' to something appropriate. + + - Fix unmount of root file system after chdir(). + + - Fix atomicity of renames. Why was the unlink added before rename? + + - Further cleanup -- should the functions in dir-ecos.c take 'struct + dirsearch' instead of various components thereof, or should each of + those functions just be moved inside its only caller in fs-ecos.c? + + - Improve mount time by using pointer directly into flash chip instead + of jffs2_flash_read() for the initial scan -- look at the #ifdef + __ECOS bit in scan.c for details. + + - Reduce memory usage. There are fields marked for possible removal in + struct _inode, and there's the __totlen field in struct + jffs2_raw_node_ref as discussed recently. diff --git a/ecos/packages/fs/jffs2/current/doc/readme.txt b/ecos/packages/fs/jffs2/current/doc/readme.txt new file mode 100644 index 0000000..e7dbd5b --- /dev/null +++ b/ecos/packages/fs/jffs2/current/doc/readme.txt @@ -0,0 +1,32 @@ +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). + + +Three test files are included jffs2_1.c (which performs the same tests +as used for eCos RamFS), jffs2_2.c (tests is seeking works) and jffs2_3.c +(garbage collection and memory leak detection) + +jffs2_2.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. Note that +this image is little endian and will only work on little endian +targets. For big endian targets it will be necassary to generate a new +image. diff --git a/ecos/packages/fs/jffs2/current/include/linux/jffs2.h b/ecos/packages/fs/jffs2/current/include/linux/jffs2.h new file mode 100644 index 0000000..a66d0a8 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/include/linux/jffs2.h @@ -0,0 +1,155 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: jffs2.h,v 1.36 2005/07/26 13:19:36 havasi 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 0x8519 /* 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; + +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; /* == JFFS2_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; /* == JFFS2_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__ */ diff --git a/ecos/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h b/ecos/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h new file mode 100644 index 0000000..a5db884 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h @@ -0,0 +1,50 @@ +/* $Id: jffs2_fs_i.h,v 1.18 2005/07/17 11:13:48 dedekind Exp $ */ + +#ifndef _JFFS2_FS_I +#define _JFFS2_FS_I + +#include <linux/version.h> +#include <linux/rbtree.h> +#include <asm/semaphore.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; + + /* The target path if this is the inode of a symlink */ + unsigned char *target; + + /* 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 */ diff --git a/ecos/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h b/ecos/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h new file mode 100644 index 0000000..1e21546 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h @@ -0,0 +1,119 @@ +/* $Id: jffs2_fs_sb.h,v 1.52 2005/05/19 16:12:17 gleixner 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> +#include <linux/rwsem.h> + +#define JFFS2_SB_FLAG_RO 1 +#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */ +#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */ + +struct jffs2_inodirty; + +/* 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 completion gc_thread_start; /* GC thread start completion */ + 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; + + /* Number of free blocks there must be before we... */ + uint8_t resv_blocks_write; /* ... allow a normal filesystem write */ + uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */ + uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ + uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ + uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ + + uint32_t nospc_dirty_size; + + 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; + +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + /* Write-behind buffer for NAND flash */ + unsigned char *wbuf; + uint32_t wbuf_ofs; + uint32_t wbuf_len; + uint32_t wbuf_pagesize; + struct jffs2_inodirty *wbuf_inodes; + + struct rw_semaphore wbuf_sem; /* Protects the write buffer */ + + /* Information about out-of-band area usage... */ + struct nand_oobinfo *oobinfo; + uint32_t badblock_pos; + uint32_t fsdata_pos; + uint32_t fsdata_len; +#endif + + /* OS-private pointer for getting back to master superblock info */ + void *os_priv; +}; + +#endif /* _JFFS2_FB_SB */ diff --git a/ecos/packages/fs/jffs2/current/src/LICENCE b/ecos/packages/fs/jffs2/current/src/LICENCE new file mode 100644 index 0000000..f3f9a6f --- /dev/null +++ b/ecos/packages/fs/jffs2/current/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-2003 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.2 2003/10/04 08:33:05 dwmw2 Exp $ diff --git a/ecos/packages/fs/jffs2/current/src/build.c b/ecos/packages/fs/jffs2/current/src/build.c new file mode 100644 index 0000000..f5a0252 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/build.c @@ -0,0 +1,371 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: build.c,v 1.75 2005/07/22 10:32:07 dedekind Exp $ + * + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mtd/mtd.h> +#include "nodelist.h" + +static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); + +static inline struct jffs2_inode_cache * +first_inode_chain(int *i, struct jffs2_sb_info *c) +{ + for (; *i < INOCACHE_HASHSIZE; (*i)++) { + if (c->inocache_list[*i]) + return c->inocache_list[*i]; + } + return NULL; +} + +static inline struct jffs2_inode_cache * +next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c) +{ + /* More in this chain? */ + if (ic->next) + return ic->next; + (*i)++; + return first_inode_chain(i, c); +} + +#define for_each_inode(i, c, ic) \ + for (i = 0, ic = first_inode_chain(&i, (c)); \ + ic; \ + ic = next_inode(&i, ic, (c))) + + +static inline void 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 directory inode #%u\n", 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); + jffs2_mark_node_obsolete(c, fd->raw); + 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 */ + } +} + +/* 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; + struct jffs2_full_dirent *fd; + struct jffs2_full_dirent *dead_fds = NULL; + + /* First, scan the medium and build all the inode caches with + lists of physical nodes */ + + c->flags |= JFFS2_SB_FLAG_SCANNING; + ret = jffs2_scan_medium(c); + c->flags &= ~JFFS2_SB_FLAG_SCANNING; + if (ret) + goto exit; + + D1(printk(KERN_DEBUG "Scanned flash completely\n")); + jffs2_dbg_dump_block_lists_nolock(c); + + c->flags |= JFFS2_SB_FLAG_BUILDING; + /* 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)); + + D1(BUG_ON(ic->ino > c->highest_ino)); + + if (ic->scan_dents) { + jffs2_build_inode_pass1(c, ic); + cond_resched(); + } + } + + D1(printk(KERN_DEBUG "Pass 1 complete\n")); + + /* 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. */ + D1(printk(KERN_DEBUG "Pass 2 starting\n")); + + 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; + + jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); + cond_resched(); + } + + D1(printk(KERN_DEBUG "Pass 2a starting\n")); + + while (dead_fds) { + fd = dead_fds; + dead_fds = fd->next; + + ic = jffs2_get_ino_cache(c, fd->ino); + D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic)); + + if (ic) + jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); + jffs2_free_full_dirent(fd); + } + + D1(printk(KERN_DEBUG "Pass 2 complete\n")); + + /* Finally, we can scan again and free the dirent structs */ + for_each_inode(i, c, ic) { + 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(); + } + c->flags &= ~JFFS2_SB_FLAG_BUILDING; + + D1(printk(KERN_DEBUG "Pass 3 complete\n")); + jffs2_dbg_dump_block_lists_nolock(c); + + /* Rotate the lists by some number to ensure wear levelling */ + jffs2_rotate_lists(c); + + ret = 0; + +exit: + if (ret) { + for_each_inode(i, c, ic) { + while(ic->scan_dents) { + fd = ic->scan_dents; + ic->scan_dents = fd->next; + jffs2_free_full_dirent(fd); + } + } + } + + return ret; +} + +static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds) +{ + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd; + + D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); + + raw = ic->nodes; + while (raw != (void *)ic) { + struct jffs2_raw_node_ref *next = raw->next_in_ino; + D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw))); + jffs2_mark_node_obsolete(c, raw); + raw = next; + } + + 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; + } + + /* Reduce nlink of the child. If it's now zero, stick it on the + dead_fds list to be cleaned up later. Else just free the fd */ + + child_ic->nlink--; + + if (!child_ic->nlink) { + D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n", + fd->ino, fd->name)); + fd->next = *dead_fds; + *dead_fds = fd; + } else { + D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", + fd->ino, fd->name, child_ic->nlink)); + jffs2_free_full_dirent(fd); + } + } + } + + /* + 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. + */ +} + +static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c) +{ + uint32_t size; + + /* Deletion should almost _always_ be allowed. We're fairly + buggered once we stop allowing people to delete stuff + because there's not enough free space... */ + c->resv_blocks_deletion = 2; + + /* Be conservative about how much space we need before we allow writes. + On top of that which is required for deletia, require an extra 2% + of the medium to be available, for overhead caused by nodes being + split across blocks, etc. */ + + size = c->flash_size / 50; /* 2% of flash size */ + size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */ + size += c->sector_size - 1; /* ... and round up */ + + c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size); + + /* When do we let the GC thread run in the background */ + + c->resv_blocks_gctrigger = c->resv_blocks_write + 1; + + /* When do we allow garbage collection to merge nodes to make + long-term progress at the expense of short-term space exhaustion? */ + c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1; + + /* When do we allow garbage collection to eat from bad blocks rather + than actually making progress? */ + c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; + + /* If there's less than this amount of dirty space, don't bother + trying to GC to make more space. It'll be a fruitless task */ + c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); + + D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n", + c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks)); + D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n", + c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n", + c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n", + c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n", + c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n", + c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n", + c->nospc_dirty_size)); +} + +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; +#ifndef __ECOS + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + c->blocks = vmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks); + else +#endif + 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; + c->blocks[i].bad_count = 0; + } + + 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); +#ifndef __ECOS + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + vfree(c->blocks); + else +#endif + kfree(c->blocks); + + return -EIO; + } + + jffs2_calc_trigger_levels(c); + + return 0; +} diff --git a/ecos/packages/fs/jffs2/current/src/compr.c b/ecos/packages/fs/jffs2/current/src/compr.c new file mode 100644 index 0000000..c9e54b9 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/compr.c @@ -0,0 +1,457 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * Created by Arjan van de Ven <arjanv@redhat.com> + * + * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: compr.c,v 1.45 2005/07/26 13:24:40 havasi Exp $ + * + */ + +#include "compr.h" + +static DEFINE_SPINLOCK(jffs2_compressor_list_lock); + +/* Available compressors are on this list */ +static LIST_HEAD(jffs2_compressor_list); + +/* Actual compression mode */ +static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; + +/* Statistics for blocks stored without compression */ +static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; + +/* jffs2_compress: + * @data: Pointer to uncompressed data + * @cdata: Pointer to returned 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: Lower 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. + * Upper byte will be used later. (soon) + * + * 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. + */ +uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen) +{ + int ret = JFFS2_COMPR_NONE; + int compr_ret; + struct jffs2_compressor *this, *best=NULL; + unsigned char *output_buf = NULL, *tmp_buf; + uint32_t orig_slen, orig_dlen; + uint32_t best_slen=0, best_dlen=0; + + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_NONE: + break; + case JFFS2_COMPR_MODE_PRIORITY: + output_buf = kmalloc(*cdatalen,GFP_KERNEL); + if (!output_buf) { + printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); + goto out; + } + orig_slen = *datalen; + orig_dlen = *cdatalen; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; + + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + this->usecount--; + if (!compr_ret) { + ret = this->compr; + this->stat_compr_blocks++; + this->stat_compr_orig_size += *datalen; + this->stat_compr_new_size += *cdatalen; + break; + } + } + spin_unlock(&jffs2_compressor_list_lock); + if (ret == JFFS2_COMPR_NONE) kfree(output_buf); + break; + case JFFS2_COMPR_MODE_SIZE: + orig_slen = *datalen; + orig_dlen = *cdatalen; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; + /* Allocating memory for output buffer if necessary */ + if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) { + spin_unlock(&jffs2_compressor_list_lock); + kfree(this->compr_buf); + spin_lock(&jffs2_compressor_list_lock); + this->compr_buf_size=0; + this->compr_buf=NULL; + } + if (!this->compr_buf) { + spin_unlock(&jffs2_compressor_list_lock); + tmp_buf = kmalloc(orig_dlen,GFP_KERNEL); + spin_lock(&jffs2_compressor_list_lock); + if (!tmp_buf) { + printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); + continue; + } + else { + this->compr_buf = tmp_buf; + this->compr_buf_size = orig_dlen; + } + } + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + this->usecount--; + if (!compr_ret) { + if ((!best_dlen)||(best_dlen>*cdatalen)) { + best_dlen = *cdatalen; + best_slen = *datalen; + best = this; + } + } + } + if (best_dlen) { + *cdatalen = best_dlen; + *datalen = best_slen; + output_buf = best->compr_buf; + best->compr_buf = NULL; + best->compr_buf_size = 0; + best->stat_compr_blocks++; + best->stat_compr_orig_size += best_slen; + best->stat_compr_new_size += best_dlen; + ret = best->compr; + } + spin_unlock(&jffs2_compressor_list_lock); + break; + default: + printk(KERN_ERR "JFFS2: unknow compression mode.\n"); + } + out: + if (ret == JFFS2_COMPR_NONE) { + *cpage_out = data_in; + *datalen = *cdatalen; + none_stat_compr_blocks++; + none_stat_compr_size += *datalen; + } + else { + *cpage_out = output_buf; + } + return ret; +} + +int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + uint16_t comprtype, unsigned char *cdata_in, + unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) +{ + struct jffs2_compressor *this; + int ret; + + /* Older code had a bug where it would write non-zero 'usercompr' + fields. Deal with it. */ + if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) + comprtype &= 0xff; + + switch (comprtype & 0xff) { + case JFFS2_COMPR_NONE: + /* This should be special-cased elsewhere, but we might as well deal with it */ + memcpy(data_out, cdata_in, datalen); + none_stat_decompr_blocks++; + break; + case JFFS2_COMPR_ZERO: + memset(data_out, 0, datalen); + break; + default: + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (comprtype == this->compr) { + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + if (ret) { + printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); + } + else { + this->stat_decompr_blocks++; + } + this->usecount--; + spin_unlock(&jffs2_compressor_list_lock); + return ret; + } + } + printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); + spin_unlock(&jffs2_compressor_list_lock); + return -EIO; + } + return 0; +} + +int jffs2_register_compressor(struct jffs2_compressor *comp) +{ + struct jffs2_compressor *this; + + if (!comp->name) { + printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); + return -1; + } + comp->compr_buf_size=0; + comp->compr_buf=NULL; + comp->usecount=0; + comp->stat_compr_orig_size=0; + comp->stat_compr_new_size=0; + comp->stat_compr_blocks=0; + comp->stat_decompr_blocks=0; + D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); + + spin_lock(&jffs2_compressor_list_lock); + + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (this->priority < comp->priority) { + list_add(&comp->list, this->list.prev); + goto out; + } + } + list_add_tail(&comp->list, &jffs2_compressor_list); +out: + D2(list_for_each_entry(this, &jffs2_compressor_list, list) { + printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); + }) + + spin_unlock(&jffs2_compressor_list_lock); + + return 0; +} + +int jffs2_unregister_compressor(struct jffs2_compressor *comp) +{ + D2(struct jffs2_compressor *this;) + + D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); + + spin_lock(&jffs2_compressor_list_lock); + + if (comp->usecount) { + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); + return -1; + } + list_del(&comp->list); + + D2(list_for_each_entry(this, &jffs2_compressor_list, list) { + printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); + }) + spin_unlock(&jffs2_compressor_list_lock); + return 0; +} + +#ifdef CONFIG_JFFS2_PROC + +#define JFFS2_STAT_BUF_SIZE 16000 + +char *jffs2_list_compressors(void) +{ + struct jffs2_compressor *this; + char *buf, *act_buf; + + act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); + list_for_each_entry(this, &jffs2_compressor_list, list) { + act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); + if ((this->disabled)||(!this->compress)) + act_buf += sprintf(act_buf,"disabled"); + else + act_buf += sprintf(act_buf,"enabled"); + act_buf += sprintf(act_buf,"\n"); + } + return buf; +} + +char *jffs2_stats(void) +{ + struct jffs2_compressor *this; + char *buf, *act_buf; + + act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); + + act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n"); + act_buf += sprintf(act_buf,"%10s ","none"); + act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks, + none_stat_compr_size, none_stat_decompr_blocks); + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + act_buf += sprintf(act_buf,"%10s ",this->name); + if ((this->disabled)||(!this->compress)) + act_buf += sprintf(act_buf,"- "); + else + act_buf += sprintf(act_buf,"+ "); + act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks, + this->stat_compr_new_size, this->stat_compr_orig_size, + this->stat_decompr_blocks); + act_buf += sprintf(act_buf,"\n"); + } + spin_unlock(&jffs2_compressor_list_lock); + + return buf; +} + +char *jffs2_get_compression_mode_name(void) +{ + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_NONE: + return "none"; + case JFFS2_COMPR_MODE_PRIORITY: + return "priority"; + case JFFS2_COMPR_MODE_SIZE: + return "size"; + } + return "unkown"; +} + +int jffs2_set_compression_mode_name(const char *name) +{ + if (!strcmp("none",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; + return 0; + } + if (!strcmp("priority",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; + return 0; + } + if (!strcmp("size",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; + return 0; + } + return 1; +} + +static int jffs2_compressor_Xable(const char *name, int disabled) +{ + struct jffs2_compressor *this; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (!strcmp(this->name, name)) { + this->disabled = disabled; + spin_unlock(&jffs2_compressor_list_lock); + return 0; + } + } + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); + return 1; +} + +int jffs2_enable_compressor_name(const char *name) +{ + return jffs2_compressor_Xable(name, 0); +} + +int jffs2_disable_compressor_name(const char *name) +{ + return jffs2_compressor_Xable(name, 1); +} + +int jffs2_set_compressor_priority(const char *name, int priority) +{ + struct jffs2_compressor *this,*comp; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (!strcmp(this->name, name)) { + this->priority = priority; + comp = this; + goto reinsert; + } + } + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); + return 1; +reinsert: + /* list is sorted in the order of priority, so if + we change it we have to reinsert it into the + good place */ + list_del(&comp->list); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (this->priority < comp->priority) { + list_add(&comp->list, this->list.prev); + spin_unlock(&jffs2_compressor_list_lock); + return 0; + } + } + list_add_tail(&comp->list, &jffs2_compressor_list); + spin_unlock(&jffs2_compressor_list_lock); + return 0; +} + +#endif + +void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) +{ + if (orig != comprbuf) + kfree(comprbuf); +} + +int jffs2_compressors_init(void) +{ +/* Registering compressors */ +#ifdef CONFIG_JFFS2_ZLIB + jffs2_zlib_init(); +#endif +#ifdef CONFIG_JFFS2_RTIME + jffs2_rtime_init(); +#endif +#ifdef CONFIG_JFFS2_RUBIN + jffs2_rubinmips_init(); + jffs2_dynrubin_init(); +#endif +/* Setting default compression mode */ +#ifdef CONFIG_JFFS2_CMODE_NONE + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; + D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) +#else +#ifdef CONFIG_JFFS2_CMODE_SIZE + jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; + D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) +#else + D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) +#endif +#endif + return 0; +} + +int jffs2_compressors_exit(void) +{ +/* Unregistering compressors */ +#ifdef CONFIG_JFFS2_RUBIN + jffs2_dynrubin_exit(); + jffs2_rubinmips_exit(); +#endif +#ifdef CONFIG_JFFS2_RTIME + jffs2_rtime_exit(); +#endif +#ifdef CONFIG_JFFS2_ZLIB + jffs2_zlib_exit(); +#endif + return 0; +} diff --git a/ecos/packages/fs/jffs2/current/src/compr.h b/ecos/packages/fs/jffs2/current/src/compr.h new file mode 100644 index 0000000..9ec6e37 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/compr.h @@ -0,0 +1,107 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: compr.h,v 1.8 2005/07/26 13:24:40 havasi Exp $ + * + */ + +#ifndef __JFFS2_COMPR_H__ +#define __JFFS2_COMPR_H__ + +#include <linux/kernel.h> +#include <linux/vmalloc.h> +#include <linux/list.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/jffs2.h> +#include <linux/jffs2_fs_i.h> +#include <linux/jffs2_fs_sb.h> +#include "nodelist.h" + +#define JFFS2_RUBINMIPS_PRIORITY 10 +#define JFFS2_DYNRUBIN_PRIORITY 20 +#define JFFS2_LZARI_PRIORITY 30 +#define JFFS2_LZO_PRIORITY 40 +#define JFFS2_RTIME_PRIORITY 50 +#define JFFS2_ZLIB_PRIORITY 60 + +#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ +#define JFFS2_DYNRUBIN_DISABLED /* for decompression */ + +#define JFFS2_COMPR_MODE_NONE 0 +#define JFFS2_COMPR_MODE_PRIORITY 1 +#define JFFS2_COMPR_MODE_SIZE 2 + +struct jffs2_compressor { + struct list_head list; + int priority; /* used by prirority comr. mode */ + char *name; + char compr; /* JFFS2_COMPR_XXX */ + int (*compress)(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *srclen, uint32_t *destlen, void *model); + int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, + uint32_t cdatalen, uint32_t datalen, void *model); + int usecount; + int disabled; /* if seted the compressor won't compress */ + unsigned char *compr_buf; /* used by size compr. mode */ + uint32_t compr_buf_size; /* used by size compr. mode */ + uint32_t stat_compr_orig_size; + uint32_t stat_compr_new_size; + uint32_t stat_compr_blocks; + uint32_t stat_decompr_blocks; +}; + +int jffs2_register_compressor(struct jffs2_compressor *comp); +int jffs2_unregister_compressor(struct jffs2_compressor *comp); + +int jffs2_compressors_init(void); +int jffs2_compressors_exit(void); + +uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen); + +int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + uint16_t comprtype, unsigned char *cdata_in, + unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); + +void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); + +#ifdef CONFIG_JFFS2_PROC +int jffs2_enable_compressor_name(const char *name); +int jffs2_disable_compressor_name(const char *name); +int jffs2_set_compression_mode_name(const char *mode_name); +char *jffs2_get_compression_mode_name(void); +int jffs2_set_compressor_priority(const char *mode_name, int priority); +char *jffs2_list_compressors(void); +char *jffs2_stats(void); +#endif + +/* Compressor modules */ +/* These functions will be called by jffs2_compressors_init/exit */ + +#ifdef CONFIG_JFFS2_RUBIN +int jffs2_rubinmips_init(void); +void jffs2_rubinmips_exit(void); +int jffs2_dynrubin_init(void); +void jffs2_dynrubin_exit(void); +#endif +#ifdef CONFIG_JFFS2_RTIME +int jffs2_rtime_init(void); +void jffs2_rtime_exit(void); +#endif +#ifdef CONFIG_JFFS2_ZLIB +int jffs2_zlib_init(void); +void jffs2_zlib_exit(void); +#endif + +#endif /* __JFFS2_COMPR_H__ */ diff --git a/ecos/packages/fs/jffs2/current/src/compr_rtime.c b/ecos/packages/fs/jffs2/current/src/compr_rtime.c new file mode 100644 index 0000000..2376649 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/compr_rtime.c @@ -0,0 +1,132 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 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.15 2005/03/17 20:23:06 gleixner Exp $ + * + * + * Very simple lz77-ish encoder. + * + * Theory of operation: Both encoder and decoder have a list of "last + * occurrences" 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> +#include <linux/jffs2.h> +#include "compr.h" + +/* _compress returns the compressed size, -1 if bigger */ +static int jffs2_rtime_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, + void *model) +{ + short 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; +} + + +static int jffs2_rtime_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, + void *model) +{ + short 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; + } + } + } + return 0; +} + +static struct jffs2_compressor jffs2_rtime_comp = { + .priority = JFFS2_RTIME_PRIORITY, + .name = "rtime", + .compr = JFFS2_COMPR_RTIME, + .compress = &jffs2_rtime_compress, + .decompress = &jffs2_rtime_decompress, +#ifdef JFFS2_RTIME_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_rtime_init(void) +{ + return jffs2_register_compressor(&jffs2_rtime_comp); +} + +void jffs2_rtime_exit(void) +{ + jffs2_unregister_compressor(&jffs2_rtime_comp); +} diff --git a/ecos/packages/fs/jffs2/current/src/compr_rubin.c b/ecos/packages/fs/jffs2/current/src/compr_rubin.c new file mode 100644 index 0000000..2429fd7 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/compr_rubin.c @@ -0,0 +1,379 @@ +/* + * 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.21 2005/05/20 15:39:54 gleixner Exp $ + * + */ + + +#include <linux/string.h> +#include <linux/types.h> +#include <linux/jffs2.h> +#include "compr_rubin.h" +#include "histo_mips.h" +#include "compr.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, void *model) +{ + return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); +} +#endif +static int jffs2_dynrubin_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, + void *model) +{ + 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); + } +} + + +static int jffs2_rubinmips_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t sourcelen, uint32_t dstlen, + void *model) +{ + rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); + return 0; +} + +static int jffs2_dynrubin_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t sourcelen, uint32_t dstlen, + void *model) +{ + 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); + return 0; +} + +static struct jffs2_compressor jffs2_rubinmips_comp = { + .priority = JFFS2_RUBINMIPS_PRIORITY, + .name = "rubinmips", + .compr = JFFS2_COMPR_DYNRUBIN, + .compress = NULL, /*&jffs2_rubinmips_compress,*/ + .decompress = &jffs2_rubinmips_decompress, +#ifdef JFFS2_RUBINMIPS_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_rubinmips_init(void) +{ + return jffs2_register_compressor(&jffs2_rubinmips_comp); +} + +void jffs2_rubinmips_exit(void) +{ + jffs2_unregister_compressor(&jffs2_rubinmips_comp); +} + +static struct jffs2_compressor jffs2_dynrubin_comp = { + .priority = JFFS2_DYNRUBIN_PRIORITY, + .name = "dynrubin", + .compr = JFFS2_COMPR_RUBINMIPS, + .compress = jffs2_dynrubin_compress, + .decompress = &jffs2_dynrubin_decompress, +#ifdef JFFS2_DYNRUBIN_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_dynrubin_init(void) +{ + return jffs2_register_compressor(&jffs2_dynrubin_comp); +} + +void jffs2_dynrubin_exit(void) +{ + jffs2_unregister_compressor(&jffs2_dynrubin_comp); +} diff --git a/ecos/packages/fs/jffs2/current/src/compr_rubin.h b/ecos/packages/fs/jffs2/current/src/compr_rubin.h new file mode 100644 index 0000000..cf51e34 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/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.6 2002/01/25 01:49:26 dwmw2 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]; +}; diff --git a/ecos/packages/fs/jffs2/current/src/compr_zlib.c b/ecos/packages/fs/jffs2/current/src/compr_zlib.c new file mode 100644 index 0000000..83f7e07 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/compr_zlib.c @@ -0,0 +1,222 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: compr_zlib.c,v 1.31 2005/05/20 19:30:06 gleixner 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/sched.h> +#include <linux/slab.h> +#include <linux/zlib.h> +#include <linux/zutil.h> +#include "nodelist.h" +#include "compr.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 */ +#include <linux/vmalloc.h> +#include <linux/init.h> + +static int __init alloc_workspaces(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; +} + +static void free_workspaces(void) +{ + vfree(def_strm.workspace); + vfree(inf_strm.workspace); +} +#else +#define alloc_workspaces() (0) +#define free_workspaces() do { } while(0) +#endif /* __KERNEL__ */ + +static int jffs2_zlib_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, + void *model) +{ + 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; +} + +static int jffs2_zlib_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, + void *model) +{ + 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 1; + } + + 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); + return 0; +} + +static struct jffs2_compressor jffs2_zlib_comp = { + .priority = JFFS2_ZLIB_PRIORITY, + .name = "zlib", + .compr = JFFS2_COMPR_ZLIB, + .compress = &jffs2_zlib_compress, + .decompress = &jffs2_zlib_decompress, +#ifdef JFFS2_ZLIB_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int __init jffs2_zlib_init(void) +{ + int ret; + + ret = alloc_workspaces(); + if (ret) + return ret; + + ret = jffs2_register_compressor(&jffs2_zlib_comp); + if (ret) + free_workspaces(); + + return ret; +} + +void jffs2_zlib_exit(void) +{ + jffs2_unregister_compressor(&jffs2_zlib_comp); + free_workspaces(); +} diff --git a/ecos/packages/fs/jffs2/current/src/debug.c b/ecos/packages/fs/jffs2/current/src/debug.c new file mode 100644 index 0000000..250021c --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/debug.c @@ -0,0 +1,710 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: debug.c,v 1.1 2005/07/30 15:30:42 asl Exp $ + * + */ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pagemap.h> +#include <linux/crc32.h> +#include <linux/jffs2.h> +#include "nodelist.h" +#include "debug.h" + +#ifdef JFFS2_DBG_PARANOIA_CHECKS +/* + * Check the fragtree. + */ +void +__jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f) +{ + down(&f->sem); + __jffs2_dbg_fragtree_paranoia_check_nolock(f); + up(&f->sem); +} + +void +__jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f) +{ + struct jffs2_node_frag *frag; + int bitched = 0; + + for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { + struct jffs2_full_dnode *fn = frag->node; + + if (!fn || !fn->raw) + continue; + + if (ref_flags(fn->raw) == REF_PRISTINE) { + if (fn->frags > 1) { + JFFS2_ERROR("REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2.\n", + ref_offset(fn->raw), fn->frags); + bitched = 1; + } + + /* 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 && frag_prev(frag)->node) { + JFFS2_ERROR("REF_PRISTINE node at 0x%08x had a previous non-hole frag " + "in the same page. Tell dwmw2.\n", ref_offset(fn->raw)); + bitched = 1; + } + + if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) + && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) { + JFFS2_ERROR("REF_PRISTINE node at 0x%08x (%08x-%08x) had a following " + "non-hole frag in the same page. Tell dwmw2.\n", + ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size); + bitched = 1; + } + } + } + + if (bitched) { + JFFS2_ERROR("fragtree is corrupted.\n"); + __jffs2_dbg_dump_fragtree_nolock(f); + BUG(); + } +} + +/* + * Check if the flash contains all 0xFF before we start writing. + */ +void +__jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, + uint32_t ofs, int len) +{ + size_t retlen; + int ret, i; + unsigned char *buf; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return; + + ret = jffs2_flash_read(c, ofs, len, &retlen, buf); + if (ret || (retlen != len)) { + JFFS2_WARNING("read %d bytes failed or short. ret %d, retlen %zd.\n", + len, ret, retlen); + kfree(buf); + return; + } + + ret = 0; + for (i = 0; i < len; i++) + if (buf[i] != 0xff) + ret = 1; + + if (ret) { + JFFS2_ERROR("argh, about to write node to %#08x on flash, but there are data " + "already there. The first corrupted byte is at %#08x offset.\n", ofs, ofs + i); + __jffs2_dbg_dump_buffer(buf, len, ofs); + kfree(buf); + BUG(); + } + + kfree(buf); +} + +/* + * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'. + */ +void +__jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + spin_lock(&c->erase_completion_lock); + __jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); +} + +void +__jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + uint32_t my_used_size = 0; + uint32_t my_unchecked_size = 0; + uint32_t my_dirty_size = 0; + struct jffs2_raw_node_ref *ref2 = jeb->first_node; + + while (ref2) { + uint32_t totlen = ref_totlen(c, jeb, ref2); + + if (ref2->flash_offset < jeb->offset || + ref2->flash_offset > jeb->offset + c->sector_size) { + JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n", + ref_offset(ref2), jeb->offset); + goto error; + + } + if (ref_flags(ref2) == REF_UNCHECKED) + my_unchecked_size += totlen; + else if (!ref_obsolete(ref2)) + my_used_size += totlen; + else + my_dirty_size += totlen; + + if ((!ref2->next_phys) != (ref2 == jeb->last_node)) { + JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next_phys at %#08x (mem %p), " + "last_node is at %#08x (mem %p).\n", + ref_offset(ref2), ref2, ref_offset(ref2->next_phys), ref2->next_phys, + ref_offset(jeb->last_node), jeb->last_node); + goto error; + } + ref2 = ref2->next_phys; + } + + if (my_used_size != jeb->used_size) { + JFFS2_ERROR("Calculated used size %#08x != stored used size %#08x.\n", + my_used_size, jeb->used_size); + goto error; + } + + if (my_unchecked_size != jeb->unchecked_size) { + JFFS2_ERROR("Calculated unchecked size %#08x != stored unchecked size %#08x.\n", + my_unchecked_size, jeb->unchecked_size); + goto error; + } + +#if 0 + /* This should work when we implement ref->__totlen elemination */ + if (my_dirty_size != jeb->dirty_size + jeb->wasted_size) { + JFFS2_ERROR("Calculated dirty+wasted size %#08x != stored dirty + wasted size %#08x\n", + my_dirty_size, jeb->dirty_size + jeb->wasted_size); + goto error; + } + + if (jeb->free_size == 0 + && my_used_size + my_unchecked_size + my_dirty_size != c->sector_size) { + JFFS2_ERROR("The sum of all nodes in block (%#x) != size of block (%#x)\n", + my_used_size + my_unchecked_size + my_dirty_size, + c->sector_size); + goto error; + } +#endif + + return; + +error: + __jffs2_dbg_dump_node_refs_nolock(c, jeb); + __jffs2_dbg_dump_jeb_nolock(jeb); + __jffs2_dbg_dump_block_lists_nolock(c); + BUG(); + +} +#endif /* JFFS2_DBG_PARANOIA_CHECKS */ + +#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS) +/* + * Dump the node_refs of the 'jeb' JFFS2 eraseblock. + */ +void +__jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + spin_lock(&c->erase_completion_lock); + __jffs2_dbg_dump_node_refs_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); +} + +void +__jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + struct jffs2_raw_node_ref *ref; + int i = 0; + + JFFS2_DEBUG("Dump node_refs of the eraseblock %#08x\n", jeb->offset); + if (!jeb->first_node) { + JFFS2_DEBUG("no nodes in the eraseblock %#08x\n", jeb->offset); + return; + } + + printk(JFFS2_DBG_LVL); + for (ref = jeb->first_node; ; ref = ref->next_phys) { + printk("%#08x(%#x)", ref_offset(ref), ref->__totlen); + if (ref->next_phys) + printk("->"); + else + break; + if (++i == 4) { + i = 0; + printk("\n" JFFS2_DBG_LVL); + } + } + printk("\n"); +} + +/* + * Dump an eraseblock's space accounting. + */ +void +__jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + spin_lock(&c->erase_completion_lock); + __jffs2_dbg_dump_jeb_nolock(jeb); + spin_unlock(&c->erase_completion_lock); +} + +void +__jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb) +{ + if (!jeb) + return; + + JFFS2_DEBUG("dump space accounting for the eraseblock at %#08x:\n", + jeb->offset); + + printk(JFFS2_DBG_LVL "used_size: %#08x\n", jeb->used_size); + printk(JFFS2_DBG_LVL "dirty_size: %#08x\n", jeb->dirty_size); + printk(JFFS2_DBG_LVL "wasted_size: %#08x\n", jeb->wasted_size); + printk(JFFS2_DBG_LVL "unchecked_size: %#08x\n", jeb->unchecked_size); + printk(JFFS2_DBG_LVL "free_size: %#08x\n", jeb->free_size); +} + +void +__jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c) +{ + spin_lock(&c->erase_completion_lock); + __jffs2_dbg_dump_block_lists_nolock(c); + spin_unlock(&c->erase_completion_lock); +} + +void +__jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) +{ + JFFS2_DEBUG("dump JFFS2 blocks lists:\n"); + + printk(JFFS2_DBG_LVL "flash_size: %#08x\n", c->flash_size); + printk(JFFS2_DBG_LVL "used_size: %#08x\n", c->used_size); + printk(JFFS2_DBG_LVL "dirty_size: %#08x\n", c->dirty_size); + printk(JFFS2_DBG_LVL "wasted_size: %#08x\n", c->wasted_size); + printk(JFFS2_DBG_LVL "unchecked_size: %#08x\n", c->unchecked_size); + printk(JFFS2_DBG_LVL "free_size: %#08x\n", c->free_size); + printk(JFFS2_DBG_LVL "erasing_size: %#08x\n", c->erasing_size); + printk(JFFS2_DBG_LVL "bad_size: %#08x\n", c->bad_size); + printk(JFFS2_DBG_LVL "sector_size: %#08x\n", c->sector_size); + printk(JFFS2_DBG_LVL "jffs2_reserved_blocks size: %#08x\n", + c->sector_size * c->resv_blocks_write); + + if (c->nextblock) + printk(JFFS2_DBG_LVL "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(JFFS2_DBG_LVL "nextblock: NULL\n"); + + if (c->gcblock) + printk(JFFS2_DBG_LVL "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(JFFS2_DBG_LVL "gcblock: NULL\n"); + + if (list_empty(&c->clean_list)) { + printk(JFFS2_DBG_LVL "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; + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "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 (JFFS2_DBG_LVL "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(JFFS2_DBG_LVL "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; + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "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 (JFFS2_DBG_LVL "Contains %d blocks with total dirty size %u, average dirty size: %u\n", + numblocks, dirty, dirty / numblocks); + } + + if (list_empty(&c->dirty_list)) { + printk(JFFS2_DBG_LVL "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; + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "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 (JFFS2_DBG_LVL "contains %d blocks with total dirty size %u, average dirty size: %u\n", + numblocks, dirty, dirty / numblocks); + } + + if (list_empty(&c->erasable_list)) { + printk(JFFS2_DBG_LVL "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); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "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(JFFS2_DBG_LVL "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); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "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(JFFS2_DBG_LVL "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); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "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(JFFS2_DBG_LVL "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); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "erasable_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(JFFS2_DBG_LVL "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); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "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(JFFS2_DBG_LVL "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); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "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(JFFS2_DBG_LVL "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); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "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); + } + } + } +} + +void +__jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f) +{ + down(&f->sem); + jffs2_dbg_dump_fragtree_nolock(f); + up(&f->sem); +} + +void +__jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f) +{ + struct jffs2_node_frag *this = frag_first(&f->fragtree); + uint32_t lastofs = 0; + int buggy = 0; + + JFFS2_DEBUG("dump fragtree of ino #%u\n", f->inocache->ino); + while(this) { + if (this->node) + printk(JFFS2_DBG_LVL "frag %#04x-%#04x: %#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(JFFS2_DBG_LVL "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 (f->metadata) + printk(JFFS2_DBG_LVL "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); + + if (buggy) { + JFFS2_ERROR("frag tree got a hole in it.\n"); + BUG(); + } +} + +#define JFFS2_BUFDUMP_BYTES_PER_LINE 32 +void +__jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs) +{ + int skip; + int i; + + JFFS2_DEBUG("dump from offset %#08x to offset %#08x (%x bytes).\n", + offs, offs + len, len); + i = skip = offs % JFFS2_BUFDUMP_BYTES_PER_LINE; + offs = offs & ~(JFFS2_BUFDUMP_BYTES_PER_LINE - 1); + + if (skip != 0) + printk(JFFS2_DBG_LVL "%#08x: ", offs); + + while (skip--) + printk(" "); + + while (i < len) { + if ((i % JFFS2_BUFDUMP_BYTES_PER_LINE) == 0 && i != len -1) { + if (i != 0) + printk("\n"); + offs += JFFS2_BUFDUMP_BYTES_PER_LINE; + printk(JFFS2_DBG_LVL "%0#8x: ", offs); + } + + printk("%02x ", buf[i]); + + i += 1; + } + + printk("\n"); +} + +/* + * Dump a JFFS2 node. + */ +void +__jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs) +{ + union jffs2_node_union node; + int len = sizeof(union jffs2_node_union); + size_t retlen; + uint32_t crc; + int ret; + + JFFS2_DEBUG("dump node at offset %#08x.\n", ofs); + + ret = jffs2_flash_read(c, ofs, len, &retlen, (unsigned char *)&node); + if (ret || (retlen != len)) { + JFFS2_ERROR("read %d bytes failed or short. ret %d, retlen %zd.\n", + len, ret, retlen); + return; + } + + printk(JFFS2_DBG_LVL "magic:\t%#04x\n", + je16_to_cpu(node.u.magic)); + printk(JFFS2_DBG_LVL "nodetype:\t%#04x\n", + je16_to_cpu(node.u.nodetype)); + printk(JFFS2_DBG_LVL "totlen:\t%#08x\n", + je32_to_cpu(node.u.totlen)); + printk(JFFS2_DBG_LVL "hdr_crc:\t%#08x\n", + je32_to_cpu(node.u.hdr_crc)); + + crc = crc32(0, &node.u, sizeof(node.u) - 4); + if (crc != je32_to_cpu(node.u.hdr_crc)) { + JFFS2_ERROR("wrong common header CRC.\n"); + return; + } + + if (je16_to_cpu(node.u.magic) != JFFS2_MAGIC_BITMASK && + je16_to_cpu(node.u.magic) != JFFS2_OLD_MAGIC_BITMASK) + { + JFFS2_ERROR("wrong node magic: %#04x instead of %#04x.\n", + je16_to_cpu(node.u.magic), JFFS2_MAGIC_BITMASK); + return; + } + + switch(je16_to_cpu(node.u.nodetype)) { + + case JFFS2_NODETYPE_INODE: + + printk(JFFS2_DBG_LVL "the node is inode node\n"); + printk(JFFS2_DBG_LVL "ino:\t%#08x\n", + je32_to_cpu(node.i.ino)); + printk(JFFS2_DBG_LVL "version:\t%#08x\n", + je32_to_cpu(node.i.version)); + printk(JFFS2_DBG_LVL "mode:\t%#08x\n", + node.i.mode.m); + printk(JFFS2_DBG_LVL "uid:\t%#04x\n", + je16_to_cpu(node.i.uid)); + printk(JFFS2_DBG_LVL "gid:\t%#04x\n", + je16_to_cpu(node.i.gid)); + printk(JFFS2_DBG_LVL "isize:\t%#08x\n", + je32_to_cpu(node.i.isize)); + printk(JFFS2_DBG_LVL "atime:\t%#08x\n", + je32_to_cpu(node.i.atime)); + printk(JFFS2_DBG_LVL "mtime:\t%#08x\n", + je32_to_cpu(node.i.mtime)); + printk(JFFS2_DBG_LVL "ctime:\t%#08x\n", + je32_to_cpu(node.i.ctime)); + printk(JFFS2_DBG_LVL "offset:\t%#08x\n", + je32_to_cpu(node.i.offset)); + printk(JFFS2_DBG_LVL "csize:\t%#08x\n", + je32_to_cpu(node.i.csize)); + printk(JFFS2_DBG_LVL "dsize:\t%#08x\n", + je32_to_cpu(node.i.dsize)); + printk(JFFS2_DBG_LVL "compr:\t%#02x\n", + node.i.compr); + printk(JFFS2_DBG_LVL "usercompr:\t%#02x\n", + node.i.usercompr); + printk(JFFS2_DBG_LVL "flags:\t%#04x\n", + je16_to_cpu(node.i.flags)); + printk(JFFS2_DBG_LVL "data_crc:\t%#08x\n", + je32_to_cpu(node.i.data_crc)); + printk(JFFS2_DBG_LVL "node_crc:\t%#08x\n", + je32_to_cpu(node.i.node_crc)); + crc = crc32(0, &node.i, sizeof(node.i) - 8); + if (crc != je32_to_cpu(node.i.node_crc)) { + JFFS2_ERROR("wrong node header CRC.\n"); + return; + } + break; + + case JFFS2_NODETYPE_DIRENT: + + printk(JFFS2_DBG_LVL "the node is dirent node\n"); + printk(JFFS2_DBG_LVL "pino:\t%#08x\n", + je32_to_cpu(node.d.pino)); + printk(JFFS2_DBG_LVL "version:\t%#08x\n", + je32_to_cpu(node.d.version)); + printk(JFFS2_DBG_LVL "ino:\t%#08x\n", + je32_to_cpu(node.d.ino)); + printk(JFFS2_DBG_LVL "mctime:\t%#08x\n", + je32_to_cpu(node.d.mctime)); + printk(JFFS2_DBG_LVL "nsize:\t%#02x\n", + node.d.nsize); + printk(JFFS2_DBG_LVL "type:\t%#02x\n", + node.d.type); + printk(JFFS2_DBG_LVL "node_crc:\t%#08x\n", + je32_to_cpu(node.d.node_crc)); + printk(JFFS2_DBG_LVL "name_crc:\t%#08x\n", + je32_to_cpu(node.d.name_crc)); + + node.d.name[node.d.nsize] = '\0'; + printk(JFFS2_DBG_LVL "name:\t\"%s\"\n", node.d.name); + + crc = crc32(0, &node.d, sizeof(node.d) - 8); + if (crc != je32_to_cpu(node.d.node_crc)) { + JFFS2_ERROR("wrong node header CRC.\n"); + return; + } + break; + + default: + printk(JFFS2_DBG_LVL "node type is unknown\n"); + break; + } +} +#endif /* JFFS2_DBG_DUMPS || JFFS2_DBG_PARANOIA_CHECKS */ diff --git a/ecos/packages/fs/jffs2/current/src/debug.h b/ecos/packages/fs/jffs2/current/src/debug.h new file mode 100644 index 0000000..7807177 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/debug.h @@ -0,0 +1,276 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: debug.h,v 1.1 2005/07/30 15:30:42 asl Exp $ + * + */ +#ifndef _JFFS2_DEBUG_H_ +#define _JFFS2_DEBUG_H_ + +#include <linux/config.h> + +#ifndef CONFIG_JFFS2_FS_DEBUG +#define CONFIG_JFFS2_FS_DEBUG 0 +#endif + +#if CONFIG_JFFS2_FS_DEBUG == 1 +/* Enable "paranoia" checks and dumps */ +#define JFFS2_DBG_PARANOIA_CHECKS +#define JFFS2_DBG_DUMPS +#define JFFS2_DBG_READINODE_MESSAGES +#define JFFS2_DBG_FRAGTREE_MESSAGES +#define JFFS2_DBG_DENTLIST_MESSAGES +#define JFFS2_DBG_NODEREF_MESSAGES +#define JFFS2_DBG_INOCACHE_MESSAGES +#endif + +#if CONFIG_JFFS2_FS_DEBUG == 2 +#define JFFS2_DBG_FRAGTREE2_MESSAGES +#endif + +/* Enable JFFS2 sanity checks by default */ +#define JFFS2_DBG_SANITY_CHECKS + +/* + * Dx() are mainly used for debugging messages, they must go away and be + * superseded by nicer JFFS2_DBG_XXX() macros... + */ +#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 + +/* The prefixes of JFFS2 messages */ +#define JFFS2_DBG_MSG_PREFIX "[JFFS2 DBG]" +#define JFFS2_ERR_MSG_PREFIX "JFFS2 error: " +#define JFFS2_WARN_MSG_PREFIX "JFFS2 warning: " +#define JFFS2_NOTICE_MSG_PREFIX "JFFS2 notice: " + +#define JFFS2_ERR_LVL KERN_ERR +#define JFFS2_WARN_LVL KERN_WARNING +#define JFFS2_NOTICE_LVL KERN_NOTICE +#define JFFS2_DBG_LVL KERN_DEBUG + +/* JFFS2 message macros */ +#define JFFS2_ERROR(fmt, ...) \ + do { \ + printk(JFFS2_ERR_LVL JFFS2_ERR_MSG_PREFIX " %s: " \ + fmt, __FUNCTION__, ##__VA_ARGS__); \ + } while(0) + +#define JFFS2_WARNING(fmt, ...) \ + do { \ + printk(JFFS2_WARN_LVL JFFS2_WARN_MSG_PREFIX " %s: " \ + fmt, __FUNCTION__, ##__VA_ARGS__); \ + } while(0) + +#define JFFS2_NOTICE(fmt, ...) \ + do { \ + printk(JFFS2_NOTICE_LVL JFFS2_NOTICE_MSG_PREFIX " %s: " \ + fmt, __FUNCTION__, ##__VA_ARGS__); \ + } while(0) + +#define JFFS2_DEBUG(fmt, ...) \ + do { \ + printk(JFFS2_DBG_LVL JFFS2_DBG_MSG_PREFIX " %s: " \ + fmt, __FUNCTION__, ##__VA_ARGS__); \ + } while(0) + +/* + * We split our debugging messages on several parts, depending on the JFFS2 + * subsystem the message belongs to. + */ +/* Read inode debugging messages */ +#ifdef JFFS2_DBG_READINODE_MESSAGES +#define JFFS2_DBG_READINODE(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_READINODE(fmt, ...) +#endif + +/* Fragtree build debugging messages */ +#ifdef JFFS2_DBG_FRAGTREE_MESSAGES +#define JFFS2_DBG_FRAGTREE(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_FRAGTREE(fmt, ...) +#endif + +/* Directory entry list manilulation debugging messages */ +#ifdef JFFS2_DBG_DENTLIST_MESSAGES +#define JFFS2_DBG_DENTLIST(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_DENTLIST(fmt, ...) +#endif +#ifdef JFFS2_DBG_FRAGTREE2_MESSAGES +#define JFFS2_DBG_FRAGTREE2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_FRAGTREE2(fmt, ...) +#endif + +/* Print the messages about manipulating node_refs */ +#ifdef JFFS2_DBG_NODEREF_MESSAGES +#define JFFS2_DBG_NODEREF(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_NODEREF(fmt, ...) +#endif + +/* Manipulations with the list of inodes (JFFS2 inocache) */ +#ifdef JFFS2_DBG_INOCACHE_MESSAGES +#define JFFS2_DBG_INOCACHE(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_INOCACHE(fmt, ...) +#endif + +/* Watch the object allocations */ +#ifdef JFFS2_DBG_MEMALLOC_MESSAGES +#define JFFS2_DBG_MEMALLOC(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_MEMALLOC(fmt, ...) +#endif + + +/* "Paranoia" checks */ +void +__jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f); +void +__jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f); +void +__jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, + uint32_t ofs, int len); + +/* "Dump" functions */ +void +__jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c); +void +__jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c); +void +__jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f); +void +__jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f); +void +__jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs); +void +__jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs); + +#ifdef JFFS2_DBG_PARANOIA_CHECKS +#define jffs2_dbg_fragtree_paranoia_check(f) \ + __jffs2_dbg_fragtree_paranoia_check(f) +#define jffs2_dbg_fragtree_paranoia_check_nolock(f) \ + __jffs2_dbg_fragtree_paranoia_check_nolock(f) +#define jffs2_dbg_acct_paranoia_check(c, jeb) \ + __jffs2_dbg_acct_paranoia_check(c,jeb) +#define jffs2_dbg_acct_paranoia_check_nolock(c, jeb) \ + __jffs2_dbg_acct_paranoia_check_nolock(c,jeb) +#define jffs2_dbg_prewrite_paranoia_check(c, ofs, len) \ + __jffs2_dbg_prewrite_paranoia_check(c, ofs, len) +#else +#define jffs2_dbg_fragtree_paranoia_check(f) +#define jffs2_dbg_fragtree_paranoia_check_nolock(f) +#define jffs2_dbg_acct_paranoia_check(c, jeb) +#define jffs2_dbg_acct_paranoia_check_nolock(c, jeb) +#define jffs2_dbg_prewrite_paranoia_check(c, ofs, len) +#endif /* !JFFS2_PARANOIA_CHECKS */ + +#ifdef JFFS2_DBG_DUMPS +#define jffs2_dbg_dump_jeb(c, jeb) \ + __jffs2_dbg_dump_jeb(c, jeb); +#define jffs2_dbg_dump_jeb_nolock(jeb) \ + __jffs2_dbg_dump_jeb_nolock(jeb); +#define jffs2_dbg_dump_block_lists(c) \ + __jffs2_dbg_dump_block_lists(c) +#define jffs2_dbg_dump_block_lists_nolock(c) \ + __jffs2_dbg_dump_block_lists_nolock(c) +#define jffs2_dbg_dump_fragtree(f) \ + __jffs2_dbg_dump_fragtree(f); +#define jffs2_dbg_dump_fragtree_nolock(f) \ + __jffs2_dbg_dump_fragtree_nolock(f); +#define jffs2_dbg_dump_buffer(buf, len, offs) \ + __jffs2_dbg_dump_buffer(*buf, len, offs); +#define jffs2_dbg_dump_node(c, ofs) \ + __jffs2_dbg_dump_node(c, ofs); +#else +#define jffs2_dbg_dump_jeb(c, jeb) +#define jffs2_dbg_dump_jeb_nolock(jeb) +#define jffs2_dbg_dump_block_lists(c) +#define jffs2_dbg_dump_block_lists_nolock(c) +#define jffs2_dbg_dump_fragtree(f) +#define jffs2_dbg_dump_fragtree_nolock(f) +#define jffs2_dbg_dump_buffer(buf, len, offs) +#define jffs2_dbg_dump_node(c, ofs) +#endif /* !JFFS2_DBG_DUMPS */ + +/* + * Sanity checks are supposed to be light-weight and enabled by default. + */ +#ifdef JFFS2_DBG_SANITY_CHECKS +/* + * Check the space accounting of the file system and of + * the JFFS2 erasable block 'jeb'. + */ +static inline void +jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + if (unlikely(jeb && jeb->used_size + jeb->dirty_size + + jeb->free_size + jeb->wasted_size + + jeb->unchecked_size != c->sector_size)) { + JFFS2_ERROR("eeep, space accounting for block at 0x%08x is screwed.\n", jeb->offset); + JFFS2_ERROR("free %#08x + dirty %#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 (unlikely(c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + + c->wasted_size + c->unchecked_size != c->flash_size)) { + JFFS2_ERROR("eeep, space accounting superblock info is screwed.\n"); + JFFS2_ERROR("free %#08x + dirty %#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(); + } +} + +static inline void +jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + spin_lock(&c->erase_completion_lock); + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); +} +#else +#define jffs2_dbg_acct_sanity_check(c, jeb) +#define jffs2_dbg_acct_sanity_check_nolock(c, jeb) +#endif /* !JFFS2_DBG_SANITY_CHECKS */ + +#endif /* _JFFS2_DEBUG_H_ */ diff --git a/ecos/packages/fs/jffs2/current/src/dir-ecos.c b/ecos/packages/fs/jffs2/current/src/dir-ecos.c new file mode 100644 index 0000000..16b62cc --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/dir-ecos.c @@ -0,0 +1,371 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Free Software Foundation, 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.11 2005/02/08 19:36:27 lunn Exp $ + * + */ + +#include <linux/kernel.h> +#include <linux/crc32.h> +#include "nodelist.h" + +/***********************************************************************/ + +/* Takes length argument because it can be either NUL-terminated or '/'-terminated */ +struct _inode *jffs2_lookup(struct _inode *dir_i, const unsigned char *d_name, int namelen) +{ + struct jffs2_inode_info *dir_f; + struct jffs2_sb_info *c; + struct jffs2_full_dirent *fd = NULL, *fd_list; + uint32_t ino = 0; + uint32_t hash = full_name_hash(d_name, namelen); + 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 <= hash; fd_list = fd_list->next) { + if (fd_list->nhash == hash && + (!fd || fd_list->version > fd->version) && + strlen((char *)fd_list->name) == namelen && + !strncmp((char *)fd_list->name, (char *)d_name, namelen)) { + fd = fd_list; + } + } + if (fd) + ino = fd->ino; + up(&dir_f->sem); + if (ino) { + inode = jffs2_iget(dir_i->i_sb, ino); + if (IS_ERR(inode)) { + printk("jffs2_iget() failed for ino #%u\n", ino); + return inode; + } + } + + return inode; +} + +/***********************************************************************/ + + + +int jffs2_create(struct _inode *dir_i, const unsigned char *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, + (const char *)d_name, + strlen((char *)d_name)); + + if (ret) { + inode->i_nlink = 0; + jffs2_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, const unsigned char *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, (const char *)d_name, + strlen((char *)d_name), 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, const unsigned char *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, + (const char * )d_name, + strlen((char *)d_name)); + + 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, const unsigned char *d_name, int mode) +{ + 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; + 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 = strlen((char *)d_name); + 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, ALLOC_NORMAL); + + jffs2_free_raw_inode(ri); + + if (IS_ERR(fn)) { + /* Eeek. Wave bye bye */ + up(&f->sem); + jffs2_complete_reservation(c); + inode->i_nlink = 0; + jffs2_iput(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); + + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { + /* Eep. */ + inode->i_nlink = 0; + jffs2_iput(inode); + return ret; + } + + rd = jffs2_alloc_raw_dirent(); + if (!rd) { + /* Argh. Now we treat it like a normal delete */ + jffs2_complete_reservation(c); + inode->i_nlink = 0; + jffs2_iput(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, namelen)); + + fd = jffs2_write_dirent(c, dir_f, rd, d_name, namelen, phys_ofs, ALLOC_NORMAL); + + 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); + inode->i_nlink = 0; + jffs2_iput(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); + + jffs2_iput(inode); + return 0; +} + +int jffs2_rmdir (struct _inode *dir_i, struct _inode *d_inode, const unsigned char *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, const unsigned char *old_d_name, + struct _inode *new_dir_i, const unsigned char *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, + (const char *)new_d_name, + strlen((char *)new_d_name)); + + 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), + (const char *)old_d_name, + strlen((char *)old_d_name), 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; +} + diff --git a/ecos/packages/fs/jffs2/current/src/erase.c b/ecos/packages/fs/jffs2/current/src/erase.c new file mode 100644 index 0000000..a8a0908 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/erase.c @@ -0,0 +1,459 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: erase.c,v 1.83 2005/07/22 10:32:08 dedekind 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, uint32_t bad_offset); +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); + +static void jffs2_erase_block(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + int ret; + uint32_t bad_offset; +#ifdef __ECOS + ret = jffs2_flash_erase(c, jeb); + if (!ret) { + jffs2_erase_succeeded(c, jeb); + return; + } + bad_offset = jeb->offset; +#else /* Linux */ + struct erase_info *instr; + + D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#08x (range %#08x-%#08x)\n", + jeb->offset, jeb->offset, jeb->offset + c->sector_size)); + 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; + c->dirty_size += c->sector_size; + jeb->dirty_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]); + instr->fail_addr = 0xffffffff; + + ((struct erase_priv_struct *)instr->priv)->jeb = jeb; + ((struct erase_priv_struct *)instr->priv)->c = c; + + ret = c->mtd->erase(c->mtd, instr); + if (!ret) + return; + + bad_offset = instr->fail_addr; + 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; + c->dirty_size += c->sector_size; + jeb->dirty_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); + + jffs2_erase_failed(c, jeb, bad_offset); +} + +void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) +{ + 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); + + if (!--count) { + D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n")); + goto done; + } + + } 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->wasted_size -= jeb->wasted_size; + c->free_size -= jeb->free_size; + c->used_size -= jeb->used_size; + c->dirty_size -= jeb->dirty_size; + jeb->wasted_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); + done: + 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, uint32_t bad_offset) +{ + /* For NAND, if the failure did not occur at the device level for a + specific physical page, don't bother updating the bad block table. */ + if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) { + /* We had a device-level failure to erase. Let's see if we've + failed too many times. */ + if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { + /* We'd like to give this block another try. */ + spin_lock(&c->erase_completion_lock); + list_del(&jeb->list); + list_add(&jeb->list, &c->erase_pending_list); + c->erasing_size -= c->sector_size; + c->dirty_size += c->sector_size; + jeb->dirty_size = c->sector_size; + spin_unlock(&c->erase_completion_lock); + return; + } + } + + 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, instr->fail_addr); + } 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 (SECTOR_ADDR((*prev)->flash_offset) == 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 && ic->nlink == 0) + jffs2_del_ino_cache(c, 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; +} + +static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset) +{ + void *ebuf; + uint32_t ofs; + size_t retlen; + int ret = -EIO; + + ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!ebuf) { + printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset); + return -EAGAIN; + } + + D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); + + for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) { + uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); + int i; + + *bad_offset = ofs; + + 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 fail; + } + if (retlen != readlen) { + printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); + goto fail; + } + for (i=0; i<readlen; i += sizeof(unsigned long)) { + /* It's OK. We know it's properly aligned */ + unsigned long *datum = ebuf + i; + if (*datum + 1) { + *bad_offset += i; + printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", *datum, *bad_offset); + goto fail; + } + } + ofs += readlen; + cond_resched(); + } + ret = 0; +fail: + kfree(ebuf); + return ret; +} + +static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + struct jffs2_raw_node_ref *marker_ref = NULL; + size_t retlen; + int ret; + uint32_t bad_offset; + + switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { + case -EAGAIN: goto refile; + case -EIO: goto filebad; + } + + /* Write the erase complete marker */ + D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); + bad_offset = jeb->offset; + + /* Cleanmarker in oob area or no cleanmarker at all ? */ + if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) { + + if (jffs2_cleanmarker_oob(c)) { + if (jffs2_write_nand_cleanmarker(c, jeb)) + goto filebad; + } + + 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 kvec vecs[1]; + 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_ref = jffs2_alloc_raw_node_ref(); + if (!marker_ref) { + printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n"); + goto refile; + } + + marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); + + vecs[0].iov_base = (unsigned char *) ▮ + vecs[0].iov_len = sizeof(marker); + ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen); + + if (ret || retlen != sizeof(marker)) { + if (ret) + printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", + jeb->offset, ret); + else + printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", + jeb->offset, sizeof(marker), retlen); + + jffs2_free_raw_node_ref(marker_ref); + goto filebad; + } + + marker_ref->next_in_ino = NULL; + marker_ref->next_phys = NULL; + marker_ref->flash_offset = jeb->offset | REF_NORMAL; + marker_ref->__totlen = c->cleanmarker_size; + + jeb->first_node = jeb->last_node = marker_ref; + + jeb->free_size = c->sector_size - c->cleanmarker_size; + jeb->used_size = c->cleanmarker_size; + 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; + + jffs2_dbg_acct_sanity_check_nolock(c,jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, 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); + return; + +filebad: + spin_lock(&c->erase_completion_lock); + /* Stick it on a list (any list) so erase_failed can take it + right off again. Silly, but shouldn't happen often. */ + list_add(&jeb->list, &c->erasing_list); + spin_unlock(&c->erase_completion_lock); + jffs2_erase_failed(c, jeb, bad_offset); + return; + +refile: + /* 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; +} diff --git a/ecos/packages/fs/jffs2/current/src/flashio.c b/ecos/packages/fs/jffs2/current/src/flashio.c new file mode 100644 index 0000000..13a12e3 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/flashio.c @@ -0,0 +1,165 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 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: flashio.c,v 1.1 2003/11/26 14:09:29 dwmw2 Exp $ + * + */ + +#include <linux/kernel.h> +#include "nodelist.h" + +#include <cyg/io/io.h> +#include <cyg/io/config_keys.h> +#include <cyg/io/flash.h> + +cyg_bool jffs2_flash_read(struct jffs2_sb_info * c, + cyg_uint32 read_buffer_offset, const size_t size, + size_t * return_size, unsigned 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) ? ENOERR : -EIO); +} + +cyg_bool jffs2_flash_write(struct jffs2_sb_info * c, + cyg_uint32 write_buffer_offset, const size_t size, + size_t * return_size, unsigned 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) ? ENOERR : -EIO); +} + +int +jffs2_flash_direct_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; + cyg_flashaddr_t 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); +} + diff --git a/ecos/packages/fs/jffs2/current/src/fs-ecos.c b/ecos/packages/fs/jffs2/current/src/fs-ecos.c new file mode 100644 index 0000000..c38b719 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/fs-ecos.c @@ -0,0 +1,2191 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Free Software Foundation, 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.44 2005/07/24 15:29:57 dedekind Exp $ + * + */ + +#include <linux/kernel.h> +#include "nodelist.h" +#include <linux/pagemap.h> +#include <linux/crc32.h> +#include "compr.h" +#include <errno.h> +#include <string.h> +#include <cyg/io/config_keys.h> + +#if (__GNUC__ == 3) && (__GNUC_MINOR__ == 2) && \ + (defined (__arm__) || defined (_mips)) +#error This compiler is known to be broken. Please see: +#error "http://ecos.sourceware.org/ml/ecos-patches/2003-08/msg00006.html" +#endif + +//========================================================================== +// 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); +#ifdef CYGOPT_FS_JFFS2_WRITE +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); +#endif +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); +#ifdef CYGOPT_FS_JFFS2_WRITE +static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +#endif +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); + + +static int jffs2_read_inode (struct _inode *inode); +static void jffs2_clear_inode (struct _inode *inode); +static int jffs2_truncate_file (struct _inode *inode); + +//========================================================================== +// 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. + +#ifdef CYGOPT_FS_JFFS2_WRITE +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); +#else +FSTAB_ENTRY(jffs2_fste, "jffs2", 0, + CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILESYSTEM, + jffs2_mount, + jffs2_umount, + jffs2_open, + (cyg_fsop_unlink *)cyg_fileio_erofs, + (cyg_fsop_mkdir *)cyg_fileio_erofs, + (cyg_fsop_rmdir *)cyg_fileio_erofs, + (cyg_fsop_rename *)cyg_fileio_erofs, + (cyg_fsop_link *)cyg_fileio_erofs, + jffs2_opendir, + jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo); +#endif + +// ------------------------------------------------------------------------- +// File operations. +// This set of file operations are used for normal open files. + +static cyg_fileops jffs2_fileops = { + jffs2_fo_read, +#ifdef CYGOPT_FS_JFFS2_WRITE + jffs2_fo_write, +#else + (cyg_fileop_write *) cyg_fileio_erofs, +#endif + 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 unsigned char gc_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure +static unsigned char n_fs_mounted = 0; // a counter to track the number of jffs2 instances mounted + +//========================================================================== +// Directory operations + +struct jffs2_dirsearch { + struct _inode *dir; // directory to search + const unsigned char *path; // path to follow + struct _inode *node; // Node found + const unsigned 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 + + +// 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 *this = root_i, *next; + + restart: + 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) + while (this) { + next = this->i_cache_next; + if (this != i && this->i_count == 0) { + struct _inode *parent = this->i_parent; + if (this->i_cache_next) + this->i_cache_next->i_cache_prev = this->i_cache_prev; + if (this->i_cache_prev) + this->i_cache_prev->i_cache_next = this->i_cache_next; + jffs2_clear_inode(this); + memset(this, 0x5a, sizeof(*this)); + free(this); + if (parent && parent != this) { + parent->i_count--; + this = root_i; + goto restart; + } + } + this = next; + } +} + +//========================================================================== +// Directory search + +// ------------------------------------------------------------------------- +// init_dirsearch() +// Initialize a dirsearch object to start a search + +static void init_dirsearch(jffs2_dirsearch * ds, + struct _inode *dir, const unsigned char *name) +{ + D2(printf("init_dirsearch name = %s\n", name)); + D2(printf("init_dirsearch dir = %x\n", dir)); + + dir->i_count++; + 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) +{ + struct _inode *dir = ds->dir; + const unsigned char *name = ds->path; + const unsigned 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++; + + // Check if this is the last path element. + while( *n == '/') n++; + 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->dir = ds->node; + ds->node = ds->dir->i_parent; + ds->node->i_count++; + return ENOERR; + case 1: + // Dot is consumed + D2(printf("find_entry found .\n")); + ds->node = ds->dir; + ds->dir->i_count++; + return ENOERR; + } + + // Here we have the name and its length set up. + // Search the directory for a matching entry + + D2(printf("find_entry for name = %s\n", ds->path)); + d = jffs2_lookup(dir, name, namelen); + D2(printf("find_entry got dir = %x\n", d)); + + if (d == NULL) + return ENOENT; + if (IS_ERR(d)) + return -PTR_ERR(d); + + // If it's a new directory inode, increase refcount on its parent + if (S_ISDIR(d->i_mode) && !d->i_parent) { + d->i_parent = dir; + dir->i_count++; + } + + // 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. + +// Returns with use count incremented on both the sought object and +// the directory it was found in +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') { + d->node->i_count++; + 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; + + /* We're done with it, although it we found a subdir that + will have caused the refcount to have been increased */ + jffs2_iput(d->dir); + + // Update dirsearch object to search next directory. + d->dir = d->node; + d->path += d->namelen; + while (*(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; + 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")); + sb->s_root = jffs2_iget(sb, 1); + if (IS_ERR(sb->s_root)) { + D1(printk(KERN_WARNING "get root inode failed\n")); + err = PTR_ERR(sb->s_root); + sb->s_root = NULL; + goto out_nodes; + } + + return 0; + + 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 cyg_mtab[], cyg_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 = &cyg_mtab[0]; m != &cyg_mtab_end; m++) { + // stop if there are more than the configured maximum + if (m - &cyg_mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) { + m = &cyg_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); + if (n_fs_mounted++ == 0) { + jffs2_create_slab_caches(); // No error check, cannot fail + jffs2_compressors_init(); + } + + err = jffs2_read_super(jffs2_sb); + + if (err) { + if (--n_fs_mounted == 0) { + jffs2_destroy_slab_caches(); + jffs2_compressors_exit(); + } + + 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")); +#ifdef CYGOPT_FS_JFFS2_WRITE + if (!jffs2_is_readonly(c)) + jffs2_erase_pending_blocks(c,0); +#endif +#ifdef CYGOPT_FS_JFFS2_GCTHREAD + jffs2_start_garbage_collect_thread(c); +#endif + } + 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; +} + +extern cyg_dir cyg_cdir_dir; +extern cyg_mtab_entry *cyg_cdir_mtab_entry; + +// ------------------------------------------------------------------------- +// 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); + struct jffs2_full_dirent *fd, *next; + + D2(printf("jffs2_umount\n")); + + // Only really umount if this is the only mount + if (jffs2_sb->s_mount_count == 1) { + icache_evict(root, NULL); + if (root->i_cache_next != NULL) { + struct _inode *inode = root; + printf("Refuse to unmount.\n"); + while (inode) { + printf("Ino #%u has use count %d\n", + inode->i_ino, inode->i_count); + inode = inode->i_cache_next; + } + // root icount was set to 1 on mount + return EBUSY; + } + if (root->i_count == 2 && + cyg_cdir_mtab_entry == mte && + cyg_cdir_dir == (cyg_dir)root && + !strcmp(mte->name, "/")) { + /* If we were mounted on root, there's no + way for the cwd to change out and free + the file system for unmounting. So we hack + it -- if cwd is '/' we unset it. Perhaps + we should allow chdir(NULL) to unset + cyg_cdir_dir? */ + cyg_cdir_dir = CYG_DIR_NULL; + jffs2_iput(root); + } + /* Argh. The fileio code sets this; never clears it */ + if (cyg_cdir_mtab_entry == mte) + cyg_cdir_mtab_entry = NULL; + + if (root->i_count != 1) { + printf("Ino #1 has use count %d\n", + root->i_count); + return EBUSY; + } +#ifdef CYGOPT_FS_JFFS2_GCTHREAD + jffs2_stop_garbage_collect_thread(c); +#endif + jffs2_iput(root); // Time to free the root inode + + // free directory entries + for (fd = root->jffs2_i.dents; fd; fd = next) { + next=fd->next; + jffs2_free_full_dirent(fd); + } + + free(root); + //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 superblock & root pointer + mte->root = CYG_DIR_NULL; + mte->data = 0; + 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")); + } else { + jffs2_sb->s_mount_count--; + } + if (--n_fs_mounted == 0) { + jffs2_destroy_slab_caches(); + jffs2_compressors_exit(); + } + 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")); + + /* If no chdir has been called and we were the first file system + mounted, we get called with dir == NULL. Deal with it */ + if (!dir) + dir = mte->root; + +#ifndef CYGOPT_FS_JFFS2_WRITE + if (mode & (O_CREAT|O_TRUNC|O_WRONLY)) + return EROFS; +#endif + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); + + err = jffs2_find(&ds); + + if (err == ENOENT) { +#ifdef CYGOPT_FS_JFFS2_WRITE + if (ds.last && (mode & O_CREAT)) { + + // No node there, if the O_CREAT bit is set then we must + // create a new one. The dir and name fields of the dirsearch + // object will have been updated so we know where to put it. + + err = jffs2_create(ds.dir, ds.name, S_IRUGO|S_IXUGO|S_IWUSR|S_IFREG, &node); + + if (err != 0) { + //Possible orphaned inode on the flash - but will be gc'd + jffs2_iput(ds.dir); + return -err; + } + + err = ENOERR; + } +#endif + } 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)) { + jffs2_iput(ds.node); + err = EEXIST; + } else + node = ds.node; + } + + // Finished with the directory now + jffs2_iput(ds.dir); + + if (err != ENOERR) + return err; + + // Check that we actually have a file here + if (S_ISDIR(node->i_mode)) { + jffs2_iput(node); + return EISDIR; + } + + // If the O_TRUNC bit is set we must clean out the file data. + if (mode & O_TRUNC) { +#ifdef CYGOPT_FS_JFFS2_WRITE + err = jffs2_truncate_file(node); + if (err) { + jffs2_iput(node); + return err; + } +#else + jffs2_iput(node); + return EROFS; +#endif + } + + // Initialise 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; +} + +#ifdef CYGOPT_FS_JFFS2_WRITE +// ------------------------------------------------------------------------- +// 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) +{ + jffs2_dirsearch ds; + int err; + + D2(printf("jffs2_ops_unlink\n")); + + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *)name); + + err = jffs2_find(&ds); + + if (err != ENOERR) { + jffs2_iput(ds.dir); + return err; + } + + // Cannot unlink directories, use rmdir() instead + if (S_ISDIR(ds.node->i_mode)) { + jffs2_iput(ds.dir); + jffs2_iput(ds.node); + return EPERM; + } + + // Delete it from its directory + + err = jffs2_unlink(ds.dir, ds.node, ds.name); + jffs2_iput(ds.dir); + jffs2_iput(ds.node); + + 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; + int err; + + D2(printf("jffs2_ops_mkdir\n")); + + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *)name); + + err = jffs2_find(&ds); + + if (err == ENOENT) { + if (ds.last) { + // The entry does not exist, and it is the last element in + // the pathname, so we can create it here. + + err = -jffs2_mkdir(ds.dir, ds.name, S_IRUGO|S_IXUGO|S_IWUSR); + } + // If this was not the last element, then an 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) { + jffs2_iput(ds.node); + err = EEXIST; + } + } + jffs2_iput(ds.dir); + return err; +} + +// ------------------------------------------------------------------------- +// jffs2_ops_rmdir() +// Remove a directory. + +static int jffs2_ops_rmdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name) +{ + jffs2_dirsearch ds; + int err; + + D2(printf("jffs2_ops_rmdir\n")); + + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *)name); + + err = jffs2_find(&ds); + + if (err != ENOERR) { + jffs2_iput(ds.dir); + return err; + } + + // Check that this is actually a directory. + if (!S_ISDIR(ds.node->i_mode)) { + jffs2_iput(ds.dir); + jffs2_iput(ds.node); + return EPERM; + } + + err = jffs2_rmdir(ds.dir, ds.node, ds.name); + + jffs2_iput(ds.dir); + jffs2_iput(ds.node); + return -err; +} + +// ------------------------------------------------------------------------- +// 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) +{ + jffs2_dirsearch ds1, ds2; + int err; + + D2(printf("jffs2_ops_rename\n")); + + init_dirsearch(&ds1, (struct _inode *) dir1, + (const unsigned char *)name1); + + err = jffs2_find(&ds1); + + if (err != ENOERR) { + jffs2_iput(ds1.dir); + return err; + } + + init_dirsearch(&ds2, (struct _inode *) dir2, + (const unsigned char *)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) { + jffs2_iput(ds1.dir); + jffs2_iput(ds1.node); + jffs2_iput(ds2.dir); + return err; + } + + // Null rename, just return + if (ds1.node == ds2.node) { + err = ENOERR; + goto out; + } + + // 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)) { + err = EISDIR; + goto out; + } + + if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode)) { + err = ENOTDIR; + goto out; + } + + // Now delete the destination directory entry + /* Er, what happened to atomicity of rename()? */ + err = -jffs2_unlink(ds2.dir, ds2.node, ds2.name); + + if (err != 0) + goto out; + + } + // 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, ds1.name, ds2.dir, ds2.name); + + // Update directory times + if (!err) + ds1.dir->i_ctime = + ds1.dir->i_mtime = + ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp(); + out: + jffs2_iput(ds1.dir); + if (S_ISDIR(ds1.node->i_mode)) { + /* Renamed a directory to elsewhere... so fix up its + i_parent pointer and the i_counts of its old and + new parents. */ + jffs2_iput(ds1.node->i_parent); + ds1.node->i_parent = ds2.dir; + /* We effectively increase its use count by not... */ + } else { + jffs2_iput(ds2.dir); /* ... doing this */ + } + jffs2_iput(ds1.node); + if (ds2.node) + jffs2_iput(ds2.node); + + 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) +{ + 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, + (const unsigned char *) name1); + + err = jffs2_find(&ds1); + + if (err != ENOERR) { + jffs2_iput(ds1.dir); + return err; + } + + init_dirsearch(&ds2, (struct _inode *) dir2, + (const unsigned char *) name2); + + err = jffs2_find(&ds2); + + // Don't allow links to existing objects + if (err == ENOERR) { + jffs2_iput(ds1.dir); + jffs2_iput(ds1.node); + jffs2_iput(ds2.dir); + jffs2_iput(ds2.node); + return EEXIST; + } + + // Allow through links to non-existing terminal objects + if (ds2.last && err == ENOENT) { + ds2.node = NULL; + err = ENOERR; + } + + if (err != ENOERR) { + jffs2_iput(ds1.dir); + jffs2_iput(ds1.node); + jffs2_iput(ds2.dir); + return err; + } + + // Now we know that there is no existing node at the destination, + // make a new direntry at the destination. + + err = jffs2_link(ds1.node, ds2.dir, ds2.name); + + if (err == 0) + ds1.node->i_ctime = + ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp(); + + jffs2_iput(ds1.dir); + jffs2_iput(ds1.node); + jffs2_iput(ds2.dir); + + return -err; +} +#endif /* CYGOPT_FS_JFFS2_WRITE */ +// ------------------------------------------------------------------------- +// 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")); + + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); + + err = jffs2_find(&ds); + + jffs2_iput(ds.dir); + + if (err != ENOERR) + return err; + + // check it is really a directory. + if (!S_ISDIR(ds.node->i_mode)) { + jffs2_iput(ds.node); + return ENOTDIR; + } + + // Initialize the file object, setting the f_ops field to a + // special set of file ops. + + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &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; + + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); + + err = jffs2_find(&ds); + jffs2_iput(ds.dir); + + if (err != ENOERR) + return err; + + // check it is a directory + if (!S_ISDIR(ds.node->i_mode)) { + jffs2_iput(ds.node); + return ENOTDIR; + } + + // 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. + jffs2_iput(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")); + + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); + + err = jffs2_find(&ds); + jffs2_iput(ds.dir); + + if (err != ENOERR) + return err; + + // Fill in the status + buf->st_mode = ds.node->i_mode; + buf->st_ino = ds.node->i_ino; + buf->st_dev = 0; + buf->st_nlink = ds.node->i_nlink; + buf->st_uid = ds.node->i_uid; + buf->st_gid = ds.node->i_gid; + 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; + + jffs2_iput(ds.node); + + 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")); + + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); + + err = jffs2_find(&ds); + jffs2_iput(ds.dir); + + 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; + } + + jffs2_iput(ds.node); + return err; +} + +// ------------------------------------------------------------------------- +// 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; +} + + +#ifdef CYGOPT_FS_JFFS2_WRITE +// ------------------------------------------------------------------------- +// jffs2_fo_write() +// Write data to file. +static int jffs2_extend_file (struct _inode *inode, struct jffs2_raw_inode *ri, + unsigned long offset) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dnode *fn; + uint32_t phys_ofs, alloc_len; + int ret = 0; + + /* Make new hole frag from old EOF to new page */ + D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", + (unsigned int)inode->i_size, offset)); + + ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloc_len, ALLOC_NORMAL); + if (ret) + return ret; + + down(&f->sem); + + 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->version = cpu_to_je32(++f->highest_version); + ri->isize = cpu_to_je32(max((uint32_t)inode->i_size, offset)); + + ri->offset = cpu_to_je32(inode->i_size); + ri->dsize = cpu_to_je32(offset - 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, ALLOC_NORMAL); + 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 = offset; + up(&f->sem); + return 0; +} + +// jffs2_fo_open() +// Truncate a file +static int jffs2_truncate_file (struct _inode *inode) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_full_dnode *new_metadata, * old_metadata; + struct jffs2_raw_inode *ri; + uint32_t phys_ofs, alloclen; + int err; + + ri = jffs2_alloc_raw_inode(); + if (!ri) { + return ENOMEM; + } + err = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL); + + if (err) { + jffs2_free_raw_inode(ri); + return err; + } + down(&f->sem); + 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(inode->i_ino); + ri->version = cpu_to_je32(++f->highest_version); + + ri->uid = cpu_to_je16(inode->i_uid); + ri->gid = cpu_to_je16(inode->i_gid); + ri->mode = cpu_to_jemode(inode->i_mode); + ri->isize = cpu_to_je32(0); + ri->atime = cpu_to_je32(inode->i_atime); + ri->mtime = cpu_to_je32(cyg_timestamp()); + ri->offset = cpu_to_je32(0); + ri->csize = ri->dsize = cpu_to_je32(0); + ri->compr = JFFS2_COMPR_NONE; + ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + ri->data_crc = cpu_to_je32(0); + new_metadata = jffs2_write_dnode(c, f, ri, NULL, 0, + phys_ofs, ALLOC_NORMAL); + if (IS_ERR(new_metadata)) { + jffs2_complete_reservation(c); + jffs2_free_raw_inode(ri); + up(&f->sem); + return PTR_ERR(new_metadata); + } + + /* It worked. Update the inode */ + inode->i_mtime = cyg_timestamp(); + inode->i_size = 0; + old_metadata = f->metadata; + jffs2_truncate_fragtree (c, &f->fragtree, 0); + f->metadata = new_metadata; + if (old_metadata) { + jffs2_mark_node_obsolete(c, old_metadata->raw); + jffs2_free_full_dnode(old_metadata); + } + jffs2_free_raw_inode(ri); + + up(&f->sem); + jffs2_complete_reservation(c); + + return 0; +} + +static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + struct _inode *inode = (struct _inode *) fp->f_data; + off_t pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + struct jffs2_raw_inode ri; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + int i; + + // If the APPEND mode bit was supplied, force all writes to + // the end of the file. + if (fp->f_flag & CYG_FAPPEND) + pos = fp->f_offset = inode->i_size; + + if (pos < 0) + return EINVAL; + + memset(&ri, 0, sizeof(ri)); + + ri.ino = cpu_to_je32(f->inocache->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.atime = ri.ctime = ri.mtime = cpu_to_je32(cyg_timestamp()); + + if (pos > inode->i_size) { + int err; + ri.version = cpu_to_je32(++f->highest_version); + err = jffs2_extend_file(inode, &ri, pos); + if (err) + return -err; + } + ri.isize = cpu_to_je32(inode->i_size); + + // 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]; + unsigned char *buf = iov->iov_base; + off_t len = iov->iov_len; + + uint32_t writtenlen; + int err; + + D2(printf("jffs2_fo_write page_start_pos %d\n", pos)); + D2(printf("jffs2_fo_write transfer size %d\n", len)); + + err = jffs2_write_inode_range(c, f, &ri, buf, + pos, len, &writtenlen); + if (err) + return -err; + + if (writtenlen != len) + return ENOSPC; + + pos += len; + resid -= len; + } + + // We wrote some data successfully, update the modified and access + // times of the inode, increase its size appropriately, and update + // the file offset and transfer residue. + inode->i_mtime = inode->i_ctime = je32_to_cpu(ri.mtime); + if (pos > inode->i_size) + inode->i_size = pos; + + uio->uio_resid = resid; + fp->f_offset = pos; + + return ENOERR; +} +#endif /* CYGOPT_FS_JFFS2_WRITE */ + +// ------------------------------------------------------------------------- +// 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; + } + + if (pos < 0 ) + 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")); + + jffs2_iput(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 = node->i_ino; + buf->st_dev = 0; + buf->st_nlink = node->i_nlink; + buf->st_uid = node->i_uid; + buf->st_gid = node->i_gid; + 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 unsigned 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; +#ifdef CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE + struct _inode *c_ino; +#endif + 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, (const unsigned char *) ".", 1); +#ifdef CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE + // Flags here are the same as jffs2_mkdir. Make sure + // d_type is the same as st_mode of calling stat. + ent->d_type = + jemode_to_cpu(cpu_to_jemode(S_IRUGO|S_IXUGO|S_IWUSR|S_IFDIR)); +#endif + goto out; + } + if (offset == 1) { + filldir(nbuf, nlen, (const unsigned char *) "..", 2); +#ifdef CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE + // Flags here are the same as jffs2_mkdir. Make sure + // d_type is the same as st_mode of calling stat. + ent->d_type = + jemode_to_cpu(cpu_to_jemode(S_IRUGO|S_IXUGO|S_IWUSR|S_IFDIR)); +#endif + 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((char *)fd->name)); +#ifdef CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE + c_ino = jffs2_iget(inode->i_sb, fd->ino); + if(IS_ERR(c_ino)) { + D1(printk(KERN_WARNING "get entry inode failed\n")); + // fileio already set it to zero, so not needed here + // ent->d_type = 0; + } + else { + ent->d_type = c_ino->i_mode; + jffs2_iput(c_ino); + } +#endif + 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 +// =============== +// +// +//========================================================================== + +unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + unsigned long offset, + unsigned long *priv) +{ + /* FIXME: This works only with one file system mounted at a time */ + int ret; + + ret = jffs2_read_inode_range(c, f, gc_buffer, + offset & ~(PAGE_CACHE_SIZE-1), PAGE_CACHE_SIZE); + if (ret) + return ERR_PTR(ret); + + return gc_buffer; +} + +void jffs2_gc_release_page(struct jffs2_sb_info *c, + unsigned char *ptr, + unsigned long *priv) +{ + /* Do nothing */ +} + +static 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 = 1; + 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; +} + +static struct _inode *ilookup(struct super_block *sb, cyg_uint32 ino) +{ + struct _inode *inode = NULL; + + D2(printf("ilookup\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) { + inode->i_count++; + break; + } + } + return inode; +} + +struct _inode *jffs2_iget(struct super_block *sb, cyg_uint32 ino) +{ + // 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; + int err; + + D2(printf("jffs2_iget\n")); + + inode = ilookup(sb, ino); + if (inode) + return inode; + + // Not cached, so malloc it + inode = new_inode(sb); + if (inode == NULL) + return ERR_PTR(-ENOMEM); + + inode->i_ino = ino; + + err = jffs2_read_inode(inode); + if (err) { + printf("jffs2_read_inode() failed\n"); + inode->i_nlink = 0; // free _this_ bad inode right now + jffs2_iput(inode); + inode = NULL; + return ERR_PTR(err); + } + return inode; +} + +// ------------------------------------------------------------------------- +// Decrement the reference count on an inode. If this makes the ref count +// zero, then this inode can be freed. + +void jffs2_iput(struct _inode *i) +{ + // Called in jffs2_find + // (and jffs2_open and jffs2_ops_mkdir?) + // super.c jffs2_read_super, + // and gc.c jffs2_garbage_collect_pass + recurse: + if (!i) { + printf("jffs2_iput() called with NULL inode\n"); + // and let it fault... + } + + i->i_count--; + + if (i->i_count < 0) + BUG(); + + if (i->i_count) + return; + + if (!i->i_nlink) { + struct _inode *parent; + + // Remove from the icache linked list and free immediately + if (i->i_cache_prev) + i->i_cache_prev->i_cache_next = i->i_cache_next; + if (i->i_cache_next) + i->i_cache_next->i_cache_prev = i->i_cache_prev; + + parent = i->i_parent; + jffs2_clear_inode(i); + memset(i, 0x5a, sizeof(*i)); + free(i); + + if (parent && parent != i) { + i = parent; + goto recurse; + } + + } else { + // Evict some _other_ inode with i_count zero, leaving + // this latest one in the cache for a while + icache_evict(i->i_sb->s_root, i); + } +} + + +// ------------------------------------------------------------------------- +// EOF jffs2.c + + +static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) +{ + memset(f, 0, sizeof(*f)); + init_MUTEX_LOCKED(&f->sem); +} + +static 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) { + // forceful evict: f->sem is locked already, and the + // inode is bad. + if (inode->i_cache_prev) + inode->i_cache_prev->i_cache_next = inode->i_cache_next; + if (inode->i_cache_next) + inode->i_cache_next->i_cache_prev = inode->i_cache_prev; + up(&(f->sem)); + jffs2_clear_inode(inode); + memset(inode, 0x6a, sizeof(*inode)); + free(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; + + return inode; +} + + +static int 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) { + up(&f->sem); + return ret; + } + 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")); + return 0; +} + + +void jffs2_gc_release_inode(struct jffs2_sb_info *c, + struct jffs2_inode_info *f) +{ + jffs2_iput(OFNI_EDONI_2SFFJ(f)); +} + +struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, + int inum, int nlink) +{ + struct _inode *inode; + struct jffs2_inode_cache *ic; + if (!nlink) { + /* The inode has zero nlink but its nodes weren't yet marked + obsolete. This has to be because we're still waiting for + the final (close() and) jffs2_iput() to happen. + + There's a possibility that the final jffs2_iput() could have + happened while we were contemplating. In order to ensure + that we don't cause a new read_inode() (which would fail) + for the inode in question, we use ilookup() in this case + instead of jffs2_iget(). + + The nlink can't _become_ zero at this point because we're + holding the alloc_sem, and jffs2_do_unlink() would also + need that while decrementing nlink on any inode. + */ + inode = ilookup(OFNI_BS_2SFFJ(c), inum); + if (!inode) { + D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", + inum)); + + spin_lock(&c->inocache_lock); + ic = jffs2_get_ino_cache(c, inum); + if (!ic) { + D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); + spin_unlock(&c->inocache_lock); + return NULL; + } + if (ic->state != INO_STATE_CHECKEDABSENT) { + /* Wait for progress. Don't just loop */ + D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", + ic->ino, ic->state)); + sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); + } else { + spin_unlock(&c->inocache_lock); + } + + return NULL; + } + } else { + /* Inode has links to it still; they're not going away because + jffs2_do_unlink() would need the alloc_sem and we have it. + Just jffs2_iget() it, and if read_inode() is necessary that's OK. + */ + inode = jffs2_iget(OFNI_BS_2SFFJ(c), inum); + if (IS_ERR(inode)) + return (void *)inode; + } + + return JFFS2_INODE_INFO(inode); +} + + + +uint32_t jffs2_from_os_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; +} + +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; +} diff --git a/ecos/packages/fs/jffs2/current/src/gc.c b/ecos/packages/fs/jffs2/current/src/gc.c new file mode 100644 index 0000000..362cfee --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/gc.c @@ -0,0 +1,1271 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: gc.c,v 1.152 2005/07/24 15:14:14 dedekind 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" +#include "compr.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_info *f); + +/* 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. */ +again: + if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_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 if (!list_empty(&c->erasable_pending_wbuf_list)) { + /* There are blocks are wating for the wbuf sync */ + D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n")); + spin_unlock(&c->erase_completion_lock); + jffs2_flush_wbuf_pad(c); + spin_lock(&c->erase_completion_lock); + goto again; + } else { + /* Eep. All were empty */ + D1(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; + } + + 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_info *f; + struct jffs2_inode_cache *ic; + struct jffs2_eraseblock *jeb; + struct jffs2_raw_node_ref *raw; + int ret = 0, inum, nlink; + + if (down_interruptible(&c->alloc_sem)) + return -EINTR; + + for (;;) { + 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); + jffs2_dbg_dump_block_lists_nolock(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#%u\n", ic->ino)); + + ret = jffs2_do_crccheck_inode(c, ic); + if (ret) + printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino); + + 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) { + D1 (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))); + raw = raw->next_phys; + if (unlikely(!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); + jeb->gc_node = raw; + spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); + BUG(); + } + } + jeb->gc_node = raw; + + 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; + } + + ic = jffs2_raw_ref_to_ic(raw); + + /* We need to hold the inocache. Either the erase_completion_lock or + the inocache_lock are sufficient; we trade down since the inocache_lock + causes less contention. */ + spin_lock(&c->inocache_lock); + + 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), ic->ino)); + + /* 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. + */ + + 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(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", + ic->ino)); + } + break; + + case INO_STATE_PRESENT: + /* It's in-core. GC must iget() it. */ + break; + + case INO_STATE_UNCHECKED: + case INO_STATE_CHECKING: + case INO_STATE_GC: + /* Should never happen. We should have finished checking + by the time we actually start doing any GC, and since + we're holding the alloc_sem, no other garbage collection + can happen. + */ + printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", + ic->ino, ic->state); + up(&c->alloc_sem); + spin_unlock(&c->inocache_lock); + 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", + ic->ino, 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; + } + + /* 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) { + spin_unlock(&c->inocache_lock); + + ret = jffs2_garbage_collect_pristine(c, ic, raw); + + spin_lock(&c->inocache_lock); + ic->state = INO_STATE_CHECKEDABSENT; + wake_up(&c->inocache_wq); + + if (ret != -EBADFD) { + spin_unlock(&c->inocache_lock); + goto release_sem; + } + + /* Fall through if it wanted us to, with inocache_lock held */ + } + + /* Prevent the fairly unlikely race where the gcblock is + entirely obsoleted by the final close of a file which had + the only valid nodes in the block, followed by erasure, + followed by freeing of the ic because the erased block(s) + held _all_ the nodes of that inode.... never been seen but + it's vaguely possible. */ + + inum = ic->ino; + nlink = ic->nlink; + spin_unlock(&c->inocache_lock); + + f = jffs2_gc_fetch_inode(c, inum, nlink); + if (IS_ERR(f)) { + ret = PTR_ERR(f); + goto release_sem; + } + if (!f) { + ret = 0; + goto release_sem; + } + + ret = jffs2_garbage_collect_live(c, jeb, raw, f); + + jffs2_gc_release_inode(c, f); + + 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_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; + int ret = 0; + + down(&f->sem); + + /* Now we have the lock for this inode. Check that it's still the one at the head + of the list. */ + + spin_lock(&c->erase_completion_lock); + + if (c->gcblock != jeb) { + spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n")); + goto upnout; + } + if (ref_obsolete(raw)) { + spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n")); + /* They'll call again */ + goto upnout; + } + spin_unlock(&c->erase_completion_lock); + + /* 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 (!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, f->inocache, raw); + if (!ret) { + /* Urgh. Return it sensibly. */ + frag->node->raw = f->inocache->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 { + jffs2_dbg_dump_node(c, ref_offset(raw)); + BUG(); + } + } + upnout: + up(&f->sem); + + 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, rawlen; + int retried = 0; + + D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); + + rawlen = ref_totlen(c, c->gcblock, 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, + rawlen), &phys_ofs, &alloclen); + if (ret) + return ret; + + if (alloclen < rawlen) { + /* Doesn't fit untouched. We'll go the old route and split it */ + return -EBADFD; + } + + node = kmalloc(rawlen, GFP_KERNEL); + if (!node) + return -ENOMEM; + + ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); + if (!ret && retlen != rawlen) + 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; + } + + /* OK, all the CRCs are good; this node can just be copied as-is. */ + retry: + nraw->flash_offset = phys_ofs; + nraw->__totlen = rawlen; + nraw->next_phys = NULL; + + ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node); + + if (ret || (retlen != rawlen)) { + printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", + rawlen, 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(nraw); + } + if (!retried && (nraw = jffs2_alloc_raw_node_ref())) { + /* Try to reallocate space and retry */ + uint32_t dummy; + struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size]; + + retried = 1; + + D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n")); + + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); + + ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy); + + if (!ret) { + D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs)); + + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); + + goto retry; + } + D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); + jffs2_free_raw_node_ref(nraw); + } + + jffs2_free_raw_node_ref(nraw); + 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-core, the 'nraw' pointer is then + going to be fetched from ic->nodes by our caller. */ + spin_lock(&c->erase_completion_lock); + nraw->next_in_ino = ic->nodes; + ic->nodes = nraw; + spin_unlock(&c->erase_completion_lock); + + 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; + struct jffs2_node_frag *last_frag; + jint16_t dev; + char *mdata = NULL, mdatalen = 0; + uint32_t alloclen, phys_ofs, ilen; + 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 = cpu_to_je16(((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, f, 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; + } + + last_frag = frag_last(&f->fragtree); + if (last_frag) + /* Fetch the inode length from the fragtree rather then + * from i_size since i_size may have not been updated yet */ + ilen = last_frag->ofs + last_frag->size; + else + ilen = JFFS2_F_I_SIZE(f); + + 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(ilen); + 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, ALLOC_GC); + + 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, ALLOC_GC); + + 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); + uint32_t rawlen = ref_totlen(c, jeb, fd->raw); + + rd = kmalloc(rawlen, GFP_KERNEL); + if (!rd) + return -ENOMEM; + + /* 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; + + /* Any dirent with the same name is going to have the same length... */ + if (ref_totlen(c, NULL, raw) != rawlen) + 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 (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset)) + continue; + + D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw))); + + /* This is an obsolete node belonging to the same directory, and it's of the right + length. We need to take a closer look...*/ + ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd); + if (ret) { + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading 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 != rawlen) { + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %u) reading header from obsolete node at %08x\n", + retlen, rawlen, ref_offset(raw)); + continue; + } + + if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT) + 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 (memcmp(rd->name, 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 */ + up(&c->erase_free_sem); + + D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", + ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); + kfree(rd); + + return jffs2_garbage_collect_dirent(c, jeb, f, fd); + } + + up(&c->erase_free_sem); + kfree(rd); + } + + /* 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, ilen; + 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; + } + + frag = frag_last(&f->fragtree); + if (frag) + /* Fetch the inode length from the fragtree rather then + * from i_size since i_size may have not been updated yet */ + ilen = frag->ofs + frag->size; + else + ilen = JFFS2_F_I_SIZE(f); + + 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(ilen); + 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, ALLOC_GC); + + 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)); + }); + + /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */ + mark_ref_normal(new_fn->raw); + + 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, orig_start; + int ret = 0; + unsigned char *comprbuf = NULL, *writebuf; + unsigned long pg; + unsigned char *pg_ptr; + + 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; + orig_start = start; + + if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) { + /* Attempt to do some merging. But only expand to cover logically + adjacent frags if the block containing them is already considered + to be dirty. Otherwise we end up with GC just going round in + circles dirtying the nodes it already wrote out, especially + on NAND where we have small eraseblocks and hence a much higher + chance of nodes having to be split to cross boundaries. */ + + struct jffs2_node_frag *frag; + uint32_t min, max; + + min = start & ~(PAGE_CACHE_SIZE-1); + max = min + PAGE_CACHE_SIZE; + + frag = jffs2_lookup_node_frag(&f->fragtree, start); + + /* BUG_ON(!frag) but that'll happen anyway... */ + + BUG_ON(frag->ofs != start); + + /* First grow down... */ + while((frag = frag_prev(frag)) && frag->ofs >= min) { + + /* If the previous frag doesn't even reach the beginning, there's + excessive fragmentation. Just merge. */ + if (frag->ofs > min) { + D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n", + frag->ofs, frag->ofs+frag->size)); + start = frag->ofs; + continue; + } + /* OK. This frag holds the first byte of the page. */ + if (!frag->node || !frag->node->raw) { + D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n", + frag->ofs, frag->ofs+frag->size)); + break; + } else { + + /* OK, it's a frag which extends to the beginning of the page. Does it live + in a block which is still considered clean? If so, don't obsolete it. + If not, cover it anyway. */ + + struct jffs2_raw_node_ref *raw = frag->node->raw; + struct jffs2_eraseblock *jeb; + + jeb = &c->blocks[raw->flash_offset / c->sector_size]; + + if (jeb == c->gcblock) { + D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n", + frag->ofs, frag->ofs+frag->size, ref_offset(raw))); + start = frag->ofs; + break; + } + if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { + D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n", + frag->ofs, frag->ofs+frag->size, jeb->offset)); + break; + } + + D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n", + frag->ofs, frag->ofs+frag->size, jeb->offset)); + start = frag->ofs; + break; + } + } + + /* ... then up */ + + /* Find last frag which is actually part of the node we're to GC. */ + frag = jffs2_lookup_node_frag(&f->fragtree, end-1); + + while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) { + + /* If the previous frag doesn't even reach the beginning, there's lots + of fragmentation. Just merge. */ + if (frag->ofs+frag->size < max) { + D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n", + frag->ofs, frag->ofs+frag->size)); + end = frag->ofs + frag->size; + continue; + } + + if (!frag->node || !frag->node->raw) { + D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n", + frag->ofs, frag->ofs+frag->size)); + break; + } else { + + /* OK, it's a frag which extends to the beginning of the page. Does it live + in a block which is still considered clean? If so, don't obsolete it. + If not, cover it anyway. */ + + struct jffs2_raw_node_ref *raw = frag->node->raw; + struct jffs2_eraseblock *jeb; + + jeb = &c->blocks[raw->flash_offset / c->sector_size]; + + if (jeb == c->gcblock) { + D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n", + frag->ofs, frag->ofs+frag->size, ref_offset(raw))); + end = frag->ofs + frag->size; + break; + } + if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { + D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n", + frag->ofs, frag->ofs+frag->size, jeb->offset)); + break; + } + + D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n", + frag->ofs, frag->ofs+frag->size, jeb->offset)); + end = frag->ofs + frag->size; + break; + } + } + D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n", + orig_start, orig_end, start, end)); + + D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size)); + BUG_ON(end < orig_end); + BUG_ON(start > orig_start); + } + + /* 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. + */ + pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); + + if (IS_ERR(pg_ptr)) { + printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr)); + return PTR_ERR(pg_ptr); + } + + offset = start; + while(offset < orig_end) { + uint32_t datalen; + uint32_t cdatalen; + uint16_t 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)); + + comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &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 & 0xff; + ri.usercompr = (comprtype >> 8) & 0xff; + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); + ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); + + new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC); + + jffs2_free_comprbuf(comprbuf, writebuf); + + 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; + } + } + + jffs2_gc_release_page(c, pg_ptr, &pg); + return ret; +} + diff --git a/ecos/packages/fs/jffs2/current/src/gcthread.c b/ecos/packages/fs/jffs2/current/src/gcthread.c new file mode 100644 index 0000000..f762c53 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/gcthread.c @@ -0,0 +1,124 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@redhat.com> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: gcthread.c,v 1.3 2005/01/22 16:01:12 lunn Exp $ + * + */ +#include <linux/kernel.h> +#include "nodelist.h" +#include <cyg/kernel/kapi.h> + +#define GC_THREAD_FLAG_TRIG 1 +#define GC_THREAD_FLAG_STOP 2 +#define GC_THREAD_FLAG_HAS_EXIT 4 + +static cyg_thread_entry_t jffs2_garbage_collect_thread; + +void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) +{ + struct super_block *sb=OFNI_BS_2SFFJ(c); + + /* Wake up the thread */ + D1(printk("jffs2_garbage_collect_trigger\n")); + + cyg_flag_setbits(&sb->s_gc_thread_flags,GC_THREAD_FLAG_TRIG); +} + + +void +jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) +{ + struct super_block *sb=OFNI_BS_2SFFJ(c); + + CYG_ASSERTC(c); + CYG_ASSERTC(!sb->s_gc_thread_handle); + + cyg_flag_init(&sb->s_gc_thread_flags); + cyg_mutex_init(&sb->s_lock); + + D1(printk("jffs2_start_garbage_collect_thread\n")); + /* Start the thread. Doesn't matter if it fails -- it's only an + * optimisation anyway */ + cyg_thread_create(CYGNUM_JFFS2_GC_THREAD_PRIORITY, + jffs2_garbage_collect_thread, + (cyg_addrword_t)c,"jffs2 gc thread", + (void*)sb->s_gc_thread_stack, + sizeof(sb->s_gc_thread_stack), + &sb->s_gc_thread_handle, + &sb->s_gc_thread); + + cyg_thread_resume(sb->s_gc_thread_handle); +} + +void +jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) +{ + struct super_block *sb=OFNI_BS_2SFFJ(c); + + CYG_ASSERTC(sb->s_gc_thread_handle); + + D1(printk("jffs2_stop_garbage_collect_thread\n")); + /* Stop the thread and wait for it if necessary */ + + cyg_flag_setbits(&sb->s_gc_thread_flags,GC_THREAD_FLAG_STOP); + + D1(printk("jffs2_stop_garbage_collect_thread wait\n")); + + cyg_flag_wait(&sb->s_gc_thread_flags, + GC_THREAD_FLAG_HAS_EXIT, + CYG_FLAG_WAITMODE_OR| CYG_FLAG_WAITMODE_CLR); + + // Kill and free the resources ... this is safe due to the flag + // from the thread. + cyg_thread_kill(sb->s_gc_thread_handle); + cyg_thread_delete(sb->s_gc_thread_handle); + + cyg_mutex_destroy(&sb->s_lock); + cyg_flag_destroy(&sb->s_gc_thread_flags); +} + + +static void +jffs2_garbage_collect_thread(cyg_addrword_t data) +{ + struct jffs2_sb_info *c=(struct jffs2_sb_info *)data; + struct super_block *sb=OFNI_BS_2SFFJ(c); + cyg_flag_value_t flag; + cyg_mtab_entry *mte; + + D1(printk("jffs2_garbage_collect_thread START\n")); + + while(1) { + flag=cyg_flag_timed_wait(&sb->s_gc_thread_flags, + GC_THREAD_FLAG_TRIG|GC_THREAD_FLAG_STOP, + CYG_FLAG_WAITMODE_OR| CYG_FLAG_WAITMODE_CLR, + cyg_current_time()+ + CYGNUM_JFFS2_GS_THREAD_TICKS); + + if (flag & GC_THREAD_FLAG_STOP) + break; + + D1(printk("jffs2: GC THREAD GC BEGIN\n")); + + mte=cyg_fs_root_lookup((cyg_dir *) sb->s_root); + CYG_ASSERT(mte, "Bad mount point"); + cyg_fs_lock(mte, mte->fs->syncmode); + + if (jffs2_garbage_collect_pass(c) == -ENOSPC) { + printf("No space for garbage collection. " + "Aborting JFFS2 GC thread\n"); + break; + } + cyg_fs_unlock(mte, mte->fs->syncmode); + D1(printk("jffs2: GC THREAD GC END\n")); + } + + D1(printk("jffs2_garbage_collect_thread EXIT\n")); + cyg_flag_setbits(&sb->s_gc_thread_flags,GC_THREAD_FLAG_HAS_EXIT); +} diff --git a/ecos/packages/fs/jffs2/current/src/histo.h b/ecos/packages/fs/jffs2/current/src/histo.h new file mode 100644 index 0000000..84f184f --- /dev/null +++ b/ecos/packages/fs/jffs2/current/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 */ diff --git a/ecos/packages/fs/jffs2/current/src/histo_mips.h b/ecos/packages/fs/jffs2/current/src/histo_mips.h new file mode 100644 index 0000000..9a44326 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/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 */ diff --git a/ecos/packages/fs/jffs2/current/src/malloc-ecos.c b/ecos/packages/fs/jffs2/current/src/malloc-ecos.c new file mode 100644 index 0000000..a06cf5f --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/malloc-ecos.c @@ -0,0 +1,163 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Free Software Foundation, 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.4 2003/11/26 15:55:35 dwmw2 Exp $ + * + */ + +#include <linux/kernel.h> +#include <cyg/hal/drv_api.h> +#include "nodelist.h" + +#if !defined(CYGNUM_FS_JFFS2_RAW_NODE_REF_CACHE_POOL_SIZE) +# define CYGNUM_FS_JFFS2_RAW_NODE_REF_CACHE_POOL_SIZE 0 +#endif + +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_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); +} + +#if CYGNUM_FS_JFFS2_RAW_NODE_REF_CACHE_POOL_SIZE == 0 + +int jffs2_create_slab_caches(void) +{ + return 0; +} + +void jffs2_destroy_slab_caches(void) +{ +} + +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); +} + +#else // CYGNUM_FS_JFFS2_RAW_NODE_REF_CACHE_POOL_SIZE == 0 + +static struct jffs2_raw_node_ref + rnr_pool[CYGNUM_FS_JFFS2_RAW_NODE_REF_CACHE_POOL_SIZE] __attribute__ ((aligned (4))), + * first = NULL; +static cyg_drv_mutex_t mutex; + +int jffs2_create_slab_caches(void) +{ + struct jffs2_raw_node_ref * p; + cyg_drv_mutex_init(&mutex); + for ( + p = rnr_pool; + p < rnr_pool + CYGNUM_FS_JFFS2_RAW_NODE_REF_CACHE_POOL_SIZE - 1; + p++ + ) + p->next_phys = p + 1; + rnr_pool[CYGNUM_FS_JFFS2_RAW_NODE_REF_CACHE_POOL_SIZE - 1].next_phys = NULL; + first = &rnr_pool[0]; + return 0; +} + +void jffs2_destroy_slab_caches(void) +{ +} + +struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void) +{ + struct jffs2_raw_node_ref * p; + + cyg_drv_mutex_lock(&mutex); + p = first; + if (p != NULL) + first = p->next_phys; + cyg_drv_mutex_unlock(&mutex); + return p; +} + +void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x) +{ + cyg_drv_mutex_lock(&mutex); + x->next_phys = first; + first = x; + cyg_drv_mutex_unlock(&mutex); +} + +#endif // CYGNUM_FS_JFFS2_RAW_NODE_REF_CACHE_POOL_SIZE == 0 + +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); +} + diff --git a/ecos/packages/fs/jffs2/current/src/nodelist.c b/ecos/packages/fs/jffs2/current/src/nodelist.c new file mode 100644 index 0000000..7997f52 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/nodelist.c @@ -0,0 +1,534 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: nodelist.c,v 1.102 2005/07/28 12:45:10 dedekind 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; + + JFFS2_DBG_DENTLIST("add dirent \"%s\", ino #%u\n", new->name, new->ino); + + while ((*prev) && (*prev)->nhash <= new->nhash) { + if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) { + /* Duplicate. Free one */ + if (new->version < (*prev)->version) { + JFFS2_DBG_DENTLIST("Eep! Marking new dirent node is obsolete, old is \"%s\", ino #%u\n", + (*prev)->name, (*prev)->ino); + jffs2_mark_node_obsolete(c, new->raw); + jffs2_free_full_dirent(new); + } else { + JFFS2_DBG_DENTLIST("marking old dirent \"%s\", ino #%u bsolete\n", + (*prev)->name, (*prev)->ino); + new->next = (*prev)->next; + jffs2_mark_node_obsolete(c, ((*prev)->raw)); + jffs2_free_full_dirent(*prev); + *prev = new; + } + return; + } + prev = &((*prev)->next); + } + new->next = *prev; + *prev = new; +} + +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 */ + JFFS2_DBG_FRAGTREE2("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 { + JFFS2_DBG_FRAGTREE2("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); +} + +static 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; + + JFFS2_DBG_FRAGTREE2("insert frag (0x%04x-0x%04x)\n", newfrag->ofs, newfrag->ofs + newfrag->size); + + while (*link) { + parent = *link; + base = rb_entry(parent, struct jffs2_node_frag, rb); + + JFFS2_DBG_FRAGTREE2("considering frag at 0x%08x\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 { + JFFS2_ERROR("duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); + BUG(); + } + } + + rb_link_node(&newfrag->rb, &base->rb, link); +} + +/* 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) { + JFFS2_DBG_FRAGTREE2("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 { + JFFS2_DBG_FRAGTREE2("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 && (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) { + jffs2_free_node_frag(newfrag); + 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 */ + JFFS2_DBG_FRAGTREE2("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 { + JFFS2_DBG_FRAGTREE2("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 new fragment */ + JFFS2_DBG_FRAGTREE2("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 { + JFFS2_DBG_FRAGTREE2("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; + } + + JFFS2_DBG_FRAGTREE2("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; + } + if (this->node) + JFFS2_DBG_FRAGTREE2("split old frag 0x%04x-0x%04x, phys 0x%08x\n", + this->ofs, this->ofs+this->size, ref_offset(this->node->raw)); + else + JFFS2_DBG_FRAGTREE2("split old hole frag 0x%04x-0x%04x\n", + this->ofs, this->ofs+this->size, ref_offset(this->node->raw)); + + /* 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 */ + JFFS2_DBG_FRAGTREE2("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) { + JFFS2_DBG_FRAGTREE2("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. */ + JFFS2_DBG_FRAGTREE2("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; +} + +/* 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; + + if (unlikely(!fn->size)) + return 0; + + newfrag = jffs2_alloc_node_frag(); + if (unlikely(!newfrag)) + return -ENOMEM; + + JFFS2_DBG_FRAGTREE("adding node %#04x-%#04x @0x%08x on flash, newfrag *%p\n", + fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag); + + 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 (unlikely(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); + } + } + jffs2_dbg_fragtree_paranoia_check_nolock(f); + jffs2_dbg_dump_fragtree_nolock(f); + return 0; +} + + +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, uint32_t ino) +{ + struct jffs2_inode_cache *ret; + + ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; + while (ret && ret->ino < ino) { + ret = ret->next; + } + + if (ret && ret->ino != ino) + ret = NULL; + + return ret; +} + +void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new) +{ + struct jffs2_inode_cache **prev; + + spin_lock(&c->inocache_lock); + if (!new->ino) + new->ino = ++c->highest_ino; + + JFFS2_DBG_INOCACHE("add %p (ino #%u)\n", new, new->ino); + + 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; + + JFFS2_DBG_INOCACHE("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; + } + + /* Free it now unless it's in READING or CLEARING state, which + are the transitions upon read_inode() and clear_inode(). The + rest of the time we know nobody else is looking at it, and + if it's held by read_inode() or clear_inode() they'll free it + for themselves. */ + if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING) + jffs2_free_inode_cache(old); + + 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; + 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; + + JFFS2_DBG_FRAGTREE2("root %p, offset %d\n", fragtree, offset); + + next = fragtree->rb_node; + + while(next) { + frag = rb_entry(next, struct jffs2_node_frag, rb); + + JFFS2_DBG_FRAGTREE2("considering frag %#04x-%#04x (%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) { + JFFS2_DBG_FRAGTREE2("going right from frag %#04x-%#04x, 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) { + JFFS2_DBG_FRAGTREE2("going left from frag %#04x-%#04x, after the region we care about\n", + frag->ofs, frag->ofs+frag->size); + next = frag->rb.rb_left; + } else { + JFFS2_DBG_FRAGTREE2("returning frag %#04x-%#04x, 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) + JFFS2_DBG_FRAGTREE2("no match. Returning frag %#04x-%#04x, closest previous\n", + prev->ofs, prev->ofs+prev->size); + else + JFFS2_DBG_FRAGTREE2("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; + + JFFS2_DBG_FRAGTREE("killing\n"); + + frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb)); + while(frag) { + if (frag->rb.rb_left) { + JFFS2_DBG_FRAGTREE2("going left from frag (%p) %#04x-%#04x\n", + frag, frag->ofs, frag->ofs+frag->size); + frag = frag_left(frag); + continue; + } + if (frag->rb.rb_right) { + JFFS2_DBG_FRAGTREE2("going right from frag (%p) %#04x-%#04x\n", + frag, frag->ofs, frag->ofs+frag->size); + frag = frag_right(frag); + continue; + } + + JFFS2_DBG_FRAGTREE2("frag %#04x-%#04x: 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; + + cond_resched(); + } +} diff --git a/ecos/packages/fs/jffs2/current/src/nodelist.h b/ecos/packages/fs/jffs2/current/src/nodelist.h new file mode 100644 index 0000000..452fc81 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/nodelist.h @@ -0,0 +1,394 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: nodelist.h,v 1.135 2005/07/27 14:46:11 dedekind 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 compatibility with older kernels */ +#include "os-linux.h" +#endif + +#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 + +/* + 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; /* This may die; use ref_totlen(c, jeb, ) below */ +}; + + /* 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, which indicate the node's status; see below: */ +#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) + +/* 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 INO_STATE_CLEARING 6 /* In clear_inode() */ + +#define INOCACHE_HASHSIZE 128 + +/* + 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; /* The offset to which the data of this node belongs */ + 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 rb_node rb; + 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; /* The offset to which this fragment belongs */ +}; + +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 */ +}; + +/* Calculate totlen from surrounding nodes or eraseblock */ +static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *ref) +{ + uint32_t ref_end; + + if (ref->next_phys) + ref_end = ref_offset(ref->next_phys); + else { + if (!jeb) + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + + /* Last node in block. Use free_space */ + BUG_ON(ref != jeb->last_node); + ref_end = jeb->offset + c->sector_size - jeb->free_size; + } + return ref_end - ref_offset(ref); +} + +static inline uint32_t ref_totlen(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *ref) +{ + uint32_t ret; + +#if CONFIG_JFFS2_FS_DEBUG > 0 + if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { + printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n", + jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref)); + BUG(); + } +#endif + +#if 1 + ret = ref->__totlen; +#else + /* This doesn't actually work yet */ + ret = __ref_totlen(c, jeb, ref); + if (ret != ref->__totlen) { + printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n", + ref, ref_offset(ref), ref_offset(ref)+ref->__totlen, + ret, ref->__totlen); + if (!jeb) + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + jffs2_dbg_dump_node_refs_nolock(c, jeb); + BUG(); + } +#endif + return ret; +} + +#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 ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */ + +/* 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 struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw) +{ + while(raw->next_in_ino) { + raw = raw->next_in_ino; + } + + return ((struct jffs2_inode_cache *)raw); +} + +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); +} + +static inline struct jffs2_node_frag *frag_last(struct rb_root *root) +{ + struct rb_node *node = root->rb_node; + + if (!node) + return NULL; + while(node->rb_right) + node = node->rb_right; + 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 */ +void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); +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, uint32_t 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); +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); +void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this); +int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); + +/* nodemgmt.c */ +int jffs2_thread_should_wake(struct jffs2_sb_info *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); + +/* 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, int alloc_mode); +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, int alloc_mode); +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_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); +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_inode_info *f, + 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); + +/* 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_pending_blocks(struct jffs2_sb_info *c, int count); + +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER +/* wbuf.c */ +int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); +int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); +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); +#endif + +#include "debug.h" + +#endif /* __JFFS2_NODELIST_H__ */ diff --git a/ecos/packages/fs/jffs2/current/src/nodemgmt.c b/ecos/packages/fs/jffs2/current/src/nodemgmt.c new file mode 100644 index 0000000..fe7e70a --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/nodemgmt.c @@ -0,0 +1,680 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: nodemgmt.c,v 1.124 2005/07/20 15:32:28 dedekind 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 = c->resv_blocks_write; + /* align it */ + minsize = PAD(minsize); + + 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 (true <tglx> :)) */ + while(ret == -EAGAIN) { + while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { + int ret; + uint32_t dirty, avail; + + /* calculate real dirty size + * dirty_size contains blocks on erase_pending_list + * those blocks are counted in c->nr_erasing_blocks. + * If one block is actually erased, it is not longer counted as dirty_space + * but it is counted in c->nr_erasing_blocks, so we add it and subtract it + * with c->nr_erasing_blocks * c->sector_size again. + * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks + * This helps us to force gc and pick eventually a clean block to spread the load. + * We add unchecked_size here, as we hopefully will find some space to use. + * This will affect the sum only once, as gc first finishes checking + * of nodes. + */ + dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; + if (dirty < c->nospc_dirty_size) { + if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { + D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n")); + break; + } + D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n", + dirty, c->unchecked_size, c->sector_size)); + + spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); + return -ENOSPC; + } + + /* Calc possibly available space. Possibly available means that we + * don't know, if unchecked size contains obsoleted nodes, which could give us some + * more usable space. This will affect the sum only once, as gc first finishes checking + * of nodes. + + Return -ENOSPC, if the maximum possibly available space is less or equal than + * blocksneeded * sector_size. + * This blocks endless gc looping on a filesystem, which is nearly full, even if + * the check above passes. + */ + avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size; + if ( (avail / c->sector_size) <= blocksneeded) { + if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { + D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n")); + break; + } + + D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", + avail, blocksneeded * c->sector_size)); + spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); + return -ENOSPC; + } + + up(&c->alloc_sem); + + 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 (jffs2_wbuf_dirty(c)) { + spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); + jffs2_flush_wbuf_pad(c); + spin_lock(&c->erase_completion_lock); + jeb = c->nextblock; + goto restart; + } + 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)) { + + 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_pad(c); + 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; + } + + spin_unlock(&c->erase_completion_lock); + /* Don't wait for it; just erase one right now */ + jffs2_erase_pending_blocks(c, 1); + spin_lock(&c->erase_completion_lock); + + /* 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; + + jeb = &c->blocks[new->flash_offset / c->sector_size]; + len = ref_totlen(c, jeb, new); + + 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 + /* we could get some obsolete nodes after nextblock was refiled + in wbuf.c */ + if ((c->nextblock || !ref_obsolete(new)) + &&(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 && !ISDIRTY(jeb->wasted_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 (jffs2_wbuf_dirty(c)) { + /* Flush the last write in the block if it's outstanding */ + spin_unlock(&c->erase_completion_lock); + jffs2_flush_wbuf_pad(c); + spin_lock(&c->erase_completion_lock); + } + + list_add_tail(&jeb->list, &c->clean_list); + c->nextblock = NULL; + } + jffs2_dbg_acct_sanity_check_nolock(c,jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, 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); +} + +static inline int on_list(struct list_head *obj, struct list_head *head) +{ + struct list_head *this; + + list_for_each(this, head) { + if (this == obj) { + D1(printk("%p is on list at %p\n", obj, head)); + return 1; + + } + } + return 0; +} + +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, addedsize; + 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]; + + if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) && + !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) { + /* Hm. This may confuse static lock analysis. If any of the above + three conditions is false, we're going to return from this + function without actually obliterating any nodes or freeing + any jffs2_raw_node_refs. So we don't need to stop erases from + happening, or protect against people holding an obsolete + jffs2_raw_node_ref without the erase_completion_lock. */ + down(&c->erase_free_sem); + } + + spin_lock(&c->erase_completion_lock); + + if (ref_flags(ref) == REF_UNCHECKED) { + D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) { + 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(c, jeb, ref), 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(c, jeb, ref))); + jeb->unchecked_size -= ref_totlen(c, jeb, ref); + c->unchecked_size -= ref_totlen(c, jeb, ref); + } else { + D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) { + 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(c, jeb, ref), 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(c, jeb, ref))); + jeb->used_size -= ref_totlen(c, jeb, ref); + c->used_size -= ref_totlen(c, jeb, ref); + } + + // Take care, that wasted size is taken into concern + if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { + D1(printk(KERN_DEBUG "Dirtying\n")); + addedsize = ref_totlen(c, jeb, ref); + jeb->dirty_size += ref_totlen(c, jeb, ref); + c->dirty_size += ref_totlen(c, jeb, ref); + + /* Convert wasted space to dirty, if not a bad block */ + if (jeb->wasted_size) { + if (on_list(&jeb->list, &c->bad_used_list)) { + D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n", + jeb->offset)); + addedsize = 0; /* To fool the refiling code later */ + } else { + D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n", + jeb->wasted_size, jeb->offset)); + addedsize += 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; + } + } + } else { + D1(printk(KERN_DEBUG "Wasting\n")); + addedsize = 0; + jeb->wasted_size += ref_totlen(c, jeb, ref); + c->wasted_size += ref_totlen(c, jeb, ref); + } + ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; + + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + + if (c->flags & JFFS2_SB_FLAG_SCANNING) { + /* Flash scanning is 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); + /* We didn't lock the erase_free_sem */ + 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 (jffs2_wbuf_dirty(c)) { + D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); + list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list); + } 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 - addedsize)) { + 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 - addedsize)) { + 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) || jffs2_is_readonly(c) || + (c->flags & JFFS2_SB_FLAG_BUILDING)) { + /* We didn't lock the erase_free_sem */ + return; + } + + /* The erase_free_sem is locked, and has been since before we marked the node obsolete + and potentially put its eraseblock onto the erase_pending_list. Thus, we know that + the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet + by jffs2_free_all_node_refs() in erase.c. Which is nice. */ + + 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); + goto out_erase_sem; + } + if (retlen != sizeof(n)) { + printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); + goto out_erase_sem; + } + if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { + printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); + goto out_erase_sem; + } + 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))); + goto out_erase_sem; + } + /* 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); + goto out_erase_sem; + } + if (retlen != sizeof(n)) { + printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); + goto out_erase_sem; + } + + /* Nodes which have been marked obsolete no longer need to be + associated with any inode. Remove them from the per-inode list. + + Note we can't do this for NAND at the moment because we need + obsolete dirent nodes to stay on the lists, because of the + horridness in jffs2_garbage_collect_deletion_dirent(). Also + because we delete the inocache, and on NAND we need that to + stay around until all the nodes are actually erased, in order + to stop us from giving the same inode number to another newly + created inode. */ + if (ref->next_in_ino) { + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref **p; + + spin_lock(&c->erase_completion_lock); + + ic = jffs2_raw_ref_to_ic(ref); + for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino)) + ; + + *p = ref->next_in_ino; + ref->next_in_ino = NULL; + + if (ic->nodes == (void *)ic && ic->nlink == 0) + jffs2_del_ino_cache(c, ic); + + spin_unlock(&c->erase_completion_lock); + } + + + /* Merge with the next node in the physical list, if there is one + and if it's also obsolete and if it doesn't belong to any inode */ + if (ref->next_phys && ref_obsolete(ref->next_phys) && + !ref->next_phys->next_in_ino) { + struct jffs2_raw_node_ref *n = ref->next_phys; + + spin_lock(&c->erase_completion_lock); + + ref->__totlen += n->__totlen; + ref->next_phys = n->next_phys; + if (jeb->last_node == n) jeb->last_node = ref; + if (jeb->gc_node == n) { + /* gc will be happy continuing gc on this node */ + jeb->gc_node=ref; + } + spin_unlock(&c->erase_completion_lock); + + jffs2_free_raw_node_ref(n); + } + + /* Also merge with the previous node in the list, if there is one + and that one is obsolete */ + if (ref != jeb->first_node ) { + struct jffs2_raw_node_ref *p = jeb->first_node; + + spin_lock(&c->erase_completion_lock); + + while (p->next_phys != ref) + p = p->next_phys; + + if (ref_obsolete(p) && !ref->next_in_ino) { + p->__totlen += ref->__totlen; + if (jeb->last_node == ref) { + jeb->last_node = p; + } + if (jeb->gc_node == ref) { + /* gc will be happy continuing gc on this node */ + jeb->gc_node=p; + } + p->next_phys = ref->next_phys; + jffs2_free_raw_node_ref(ref); + } + spin_unlock(&c->erase_completion_lock); + } + out_erase_sem: + up(&c->erase_free_sem); +} + +int jffs2_thread_should_wake(struct jffs2_sb_info *c) +{ + int ret = 0; + uint32_t dirty; + + if (c->unchecked_size) { + D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", + c->unchecked_size, c->checked_ino)); + return 1; + } + + /* dirty_size contains blocks on erase_pending_list + * those blocks are counted in c->nr_erasing_blocks. + * If one block is actually erased, it is not longer counted as dirty_space + * but it is counted in c->nr_erasing_blocks, so we add it and subtract it + * with c->nr_erasing_blocks * c->sector_size again. + * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks + * This helps us to force gc and pick eventually a clean block to spread the load. + */ + dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; + + if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && + (dirty > c->nospc_dirty_size)) + ret = 1; + + D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", + c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); + + return ret; +} diff --git a/ecos/packages/fs/jffs2/current/src/os-ecos.h b/ecos/packages/fs/jffs2/current/src/os-ecos.h new file mode 100644 index 0000000..bb4abe7 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/os-ecos.h @@ -0,0 +1,226 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2002-2003 Free Software Foundation, 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.24 2005/02/09 09:23:55 pavlov Exp $ + * + */ + +#ifndef __JFFS2_OS_ECOS_H__ +#define __JFFS2_OS_ECOS_H__ + +#include <pkgconf/fs_jffs2.h> +#include <cyg/io/io.h> +#include <sys/types.h> +#include <asm/atomic.h> +#include <linux/stat.h> +#include <linux/compiler.h> + +#include <pkgconf/system.h> +#include <pkgconf/hal.h> +#include <pkgconf/io_fileio.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 <linux/types.h> +#include <linux/list.h> +#include <asm/bug.h> + +#define printf diag_printf + +struct _inode; +struct super_block; + +struct iovec { + void *iov_base; + ssize_t iov_len; +}; + +static inline unsigned int full_name_hash(const unsigned char * name, unsigned int len) { + + unsigned hash = 0; + while (len--) { + hash = (hash << 4) | (hash >> 28); + hash ^= *(name++); + } + return hash; +} + +#ifdef CYGOPT_FS_JFFS2_WRITE +#define jffs2_is_readonly(c) (0) +#else +#define jffs2_is_readonly(c) (1) +#endif + +/* 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) ((OFNI_EDONI_2SFFJ(f)->i_rdev)&0xff) +#define JFFS2_F_I_RDEV_MAJ(f) ((OFNI_EDONI_2SFFJ(f)->i_rdev)>>8) + +#define get_seconds cyg_timestamp + +struct _inode { + cyg_uint32 i_ino; + + int i_count; + mode_t i_mode; + nlink_t i_nlink; // Could we dispense with this? + uid_t i_uid; + gid_t i_gid; + time_t i_atime; + time_t i_mtime; + time_t i_ctime; +// union { + unsigned short i_rdev; // For devices only + struct _inode * i_parent; // For directories only + off_t i_size; // For files only +// }; + struct super_block * i_sb; + + struct jffs2_inode_info jffs2_i; + + struct _inode * i_cache_prev; // We need doubly-linked? + 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 { + struct jffs2_sb_info jffs2_sb; + struct _inode * s_root; + unsigned long s_mount_count; + cyg_io_handle_t s_dev; + +#ifdef CYGOPT_FS_JFFS2_GCTHREAD + cyg_mutex_t s_lock; // Lock the inode cache + cyg_flag_t s_gc_thread_flags; // Communication with the gcthread + cyg_handle_t s_gc_thread_handle; + cyg_thread s_gc_thread; +#if (CYGNUM_JFFS2_GC_THREAD_STACK_SIZE >= CYGNUM_HAL_STACK_SIZE_MINIMUM) + char s_gc_thread_stack[CYGNUM_JFFS2_GC_THREAD_STACK_SIZE]; +#else + char s_gc_thread_stack[CYGNUM_HAL_STACK_SIZE_MINIMUM]; +#endif + cyg_mtab_entry *mte; +#endif +}; + +#define sleep_on_spinunlock(wq, sl) spin_unlock(sl) +#define EBADFD 32767 + +/* background.c */ +#ifdef CYGOPT_FS_JFFS2_GCTHREAD +void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); +void jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); +void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); +#else +static inline void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) +{ + /* We don't have a GC thread in eCos (yet) */ +} +#endif + +/* fs-ecos.c */ +struct _inode *jffs2_new_inode (struct _inode *dir_i, int mode, struct jffs2_raw_inode *ri); +struct _inode *jffs2_iget(struct super_block *sb, cyg_uint32 ino); +void jffs2_iput(struct _inode * i); +void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); +struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink); +unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + unsigned long offset, unsigned long *priv); +void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *pg, unsigned long *priv); + +/* Avoid polluting eCos namespace with names not starting in jffs2_ */ +#define os_to_jffs2_mode(x) jffs2_from_os_mode(x) +uint32_t jffs2_from_os_mode(uint32_t osmode); +uint32_t jffs2_to_os_mode (uint32_t jmode); + + +/* flashio.c */ +cyg_bool jffs2_flash_read(struct jffs2_sb_info *c, cyg_uint32 read_buffer_offset, + const size_t size, size_t * return_size, unsigned 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, unsigned char * read_buffer); +int jffs2_flash_direct_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); + +// dir-ecos.c +struct _inode *jffs2_lookup(struct _inode *dir_i, const unsigned char *name, int namelen); +int jffs2_create(struct _inode *dir_i, const unsigned char *d_name, int mode, struct _inode **new_i); +int jffs2_mkdir (struct _inode *dir_i, const unsigned char *d_name, int mode); +int jffs2_link (struct _inode *old_d_inode, struct _inode *dir_i, const unsigned char *d_name); +int jffs2_unlink(struct _inode *dir_i, struct _inode *d_inode, const unsigned char *d_name); +int jffs2_rmdir (struct _inode *dir_i, struct _inode *d_inode, const unsigned char *d_name); +int jffs2_rename (struct _inode *old_dir_i, struct _inode *d_inode, const unsigned char *old_d_name, + struct _inode *new_dir_i, const unsigned char *new_d_name); + +/* erase.c */ +static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) +{ } + +#ifndef CONFIG_JFFS2_FS_WRITEBUFFER +#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) ) +#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_pad(c) (c=c) +#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; }) +#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0) +#define jffs2_write_nand_badblock(c,jeb,p) (0) +#define jffs2_flash_setup(c) (0) +#define jffs2_nand_flash_cleanup(c) do {} while(0) +#define jffs2_wbuf_dirty(c) (0) +#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e) +#define jffs2_wbuf_timeout NULL +#define jffs2_wbuf_process NULL +#define jffs2_nor_ecc(c) (0) +#else +#error no nand yet +#endif + +#ifndef BUG_ON +#define BUG_ON(x) do { if (unlikely(x)) BUG(); } while(0) +#endif + +#define __init + +#endif /* __JFFS2_OS_ECOS_H__ */ diff --git a/ecos/packages/fs/jffs2/current/src/pushpull.h b/ecos/packages/fs/jffs2/current/src/pushpull.h new file mode 100644 index 0000000..c0c2a91 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/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@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 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__ */ diff --git a/ecos/packages/fs/jffs2/current/src/read.c b/ecos/packages/fs/jffs2/current/src/read.c new file mode 100644 index 0000000..e38e6c5 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/read.c @@ -0,0 +1,215 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: read.c,v 1.41 2005/07/22 10:32:08 dedekind Exp $ + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/crc32.h> +#include <linux/pagemap.h> +#include <linux/mtd/mtd.h> +#include <linux/compiler.h> +#include "nodelist.h" +#include "compr.h" + +int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + 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(c, f, ri->compr | (ri->usercompr << 8), 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 (unlikely(!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(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 (unlikely(!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, f, 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; +} + diff --git a/ecos/packages/fs/jffs2/current/src/readinode.c b/ecos/packages/fs/jffs2/current/src/readinode.c new file mode 100644 index 0000000..ba9744a --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/readinode.c @@ -0,0 +1,895 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: readinode.c,v 1.132 2005/07/28 14:46:40 dedekind Exp $ + * + */ + +#include <linux/kernel.h> +#include <linux/sched.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" + +void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) +{ + struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size); + + JFFS2_DBG_FRAGTREE("truncating fragtree 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) { + JFFS2_DBG_FRAGTREE2("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); + + JFFS2_DBG_FRAGTREE("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; + } +} + +/* + * Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in + * order of increasing version. + */ +static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list) +{ + struct rb_node **p = &list->rb_node; + struct rb_node * parent = NULL; + struct jffs2_tmp_dnode_info *this; + + while (*p) { + parent = *p; + this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb); + + /* There may actually be a collision here, but it doesn't + actually matter. As long as the two nodes with the same + version are together, it's all fine. */ + if (tn->version < this->version) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&tn->rb, parent, p); + rb_insert_color(&tn->rb, list); +} + +static void jffs2_free_tmp_dnode_info_list(struct rb_root *list) +{ + struct rb_node *this; + struct jffs2_tmp_dnode_info *tn; + + this = list->rb_node; + + /* Now at bottom of tree */ + while (this) { + if (this->rb_left) + this = this->rb_left; + else if (this->rb_right) + this = this->rb_right; + else { + tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb); + jffs2_free_full_dnode(tn->fn); + jffs2_free_tmp_dnode_info(tn); + + this = this->rb_parent; + if (!this) + break; + + if (this->rb_left == &tn->rb) + this->rb_left = NULL; + else if (this->rb_right == &tn->rb) + this->rb_right = NULL; + else BUG(); + } + } + list->rb_node = NULL; +} + +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; + } +} + +/* Returns first valid node after 'ref'. May return 'ref' */ +static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref) +{ + while (ref && ref->next_in_ino) { + if (!ref_obsolete(ref)) + return ref; + JFFS2_DBG_NODEREF("node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)); + ref = ref->next_in_ino; + } + return NULL; +} + +/* + * Helper function for jffs2_get_inode_nodes(). + * It is called every time an directory entry node is found. + * + * Returns: 0 on succes; + * 1 if the node should be marked obsolete; + * negative error code on failure. + */ +static inline int +read_direntry(struct jffs2_sb_info *c, + struct jffs2_raw_node_ref *ref, + struct jffs2_raw_dirent *rd, + uint32_t read, + struct jffs2_full_dirent **fdp, + int32_t *latest_mctime, + uint32_t *mctime_ver) +{ + struct jffs2_full_dirent *fd; + + /* The direntry nodes are checked during the flash scanning */ + BUG_ON(ref_flags(ref) == REF_UNCHECKED); + /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ + BUG_ON(ref_obsolete(ref)); + + /* Sanity check */ + if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { + JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", + ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); + return 1; + } + + fd = jffs2_alloc_full_dirent(rd->nsize + 1); + if (unlikely(!fd)) + return -ENOMEM; + + fd->raw = ref; + fd->version = je32_to_cpu(rd->version); + fd->ino = je32_to_cpu(rd->ino); + fd->type = rd->type; + + /* Pick out the mctime of the latest dirent */ + if(fd->version > *mctime_ver) { + *mctime_ver = fd->version; + *latest_mctime = je32_to_cpu(rd->mctime); + } + + /* + * Copy as much of the name as possible from the raw + * dirent we've already read from the flash. + */ + if (read > sizeof(*rd)) + memcpy(&fd->name[0], &rd->name[0], + min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) )); + + /* Do we need to copy any more of the name directly from the flash? */ + if (rd->nsize + sizeof(*rd) > read) { + /* FIXME: point() */ + int err; + int already = read - sizeof(*rd); + + err = jffs2_flash_read(c, (ref_offset(ref)) + read, + rd->nsize - already, &read, &fd->name[already]); + if (unlikely(read != rd->nsize - already) && likely(!err)) + return -EIO; + + if (unlikely(err)) { + JFFS2_ERROR("read remainder of name: error %d\n", err); + jffs2_free_full_dirent(fd); + return -EIO; + } + } + + fd->nhash = full_name_hash(fd->name, rd->nsize); + fd->next = NULL; + fd->name[rd->nsize] = '\0'; + + /* + * Wheee. We now have a complete jffs2_full_dirent structure, with + * the name in it and everything. Link it into the list + */ + jffs2_add_fd_to_list(c, fd, fdp); + + return 0; +} + +/* + * Helper function for jffs2_get_inode_nodes(). + * It is called every time an inode node is found. + * + * Returns: 0 on succes; + * 1 if the node should be marked obsolete; + * negative error code on failure. + */ +static inline int +read_dnode(struct jffs2_sb_info *c, + struct jffs2_raw_node_ref *ref, + struct jffs2_raw_inode *rd, + uint32_t read, + struct rb_root *tnp, + int32_t *latest_mctime, + uint32_t *mctime_ver) +{ + struct jffs2_eraseblock *jeb; + struct jffs2_tmp_dnode_info *tn; + + /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ + BUG_ON(ref_obsolete(ref)); + + /* If we've never checked the CRCs on this node, check them now */ + if (ref_flags(ref) == REF_UNCHECKED) { + uint32_t crc, len; + + crc = crc32(0, rd, sizeof(*rd) - 8); + if (unlikely(crc != je32_to_cpu(rd->node_crc))) { + JFFS2_NOTICE("header CRC failed on node at %#08x: read %#08x, calculated %#08x\n", + ref_offset(ref), je32_to_cpu(rd->node_crc), crc); + return 1; + } + + /* Sanity checks */ + if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) || + unlikely(PAD(je32_to_cpu(rd->csize) + sizeof(*rd)) != PAD(je32_to_cpu(rd->totlen)))) { + JFFS2_WARNING("inode node header CRC is corrupted at %#08x\n", ref_offset(ref)); + jffs2_dbg_dump_node(c, ref_offset(ref)); + return 1; + } + + if (rd->compr != JFFS2_COMPR_ZERO && je32_to_cpu(rd->csize)) { + unsigned char *buf = NULL; + uint32_t pointed = 0; + int err; +#ifndef __ECOS + if (c->mtd->point) { + err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize), + &read, &buf); + if (unlikely(read < je32_to_cpu(rd->csize)) && likely(!err)) { + JFFS2_ERROR("MTD point returned len too short: 0x%zx\n", read); + c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd), + je32_to_cpu(rd->csize)); + } else if (unlikely(err)){ + JFFS2_ERROR("MTD point failed %d\n", err); + } else + pointed = 1; /* succefully pointed to device */ + } +#endif + if(!pointed){ + buf = kmalloc(je32_to_cpu(rd->csize), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = jffs2_flash_read(c, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize), + &read, buf); + if (unlikely(read != je32_to_cpu(rd->csize)) && likely(!err)) + err = -EIO; + if (err) { + kfree(buf); + return err; + } + } + crc = crc32(0, buf, je32_to_cpu(rd->csize)); + if(!pointed) + kfree(buf); +#ifndef __ECOS + else + c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize)); +#endif + + if (crc != je32_to_cpu(rd->data_crc)) { + JFFS2_NOTICE("data CRC failed on node at %#08x: read %#08x, calculated %#08x\n", + ref_offset(ref), je32_to_cpu(rd->data_crc), crc); + return 1; + } + + } + + /* Mark the node as having been checked and fix the accounting accordingly */ + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + len = ref_totlen(c, jeb, ref); + + spin_lock(&c->erase_completion_lock); + jeb->used_size += len; + jeb->unchecked_size -= len; + c->used_size += len; + c->unchecked_size -= len; + + /* 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(rd->dsize) >= PAGE_CACHE_SIZE) || + ( ((je32_to_cpu(rd->offset) & (PAGE_CACHE_SIZE-1))==0) && + (je32_to_cpu(rd->dsize) + je32_to_cpu(rd->offset) == je32_to_cpu(rd->isize)))) { + JFFS2_DBG_READINODE("marking node at %#08x REF_PRISTINE\n", ref_offset(ref)); + ref->flash_offset = ref_offset(ref) | REF_PRISTINE; + } else { + JFFS2_DBG_READINODE("marking node at %#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) { + JFFS2_ERROR("alloc tn failed\n"); + return -ENOMEM; + } + + tn->fn = jffs2_alloc_full_dnode(); + if (!tn->fn) { + JFFS2_ERROR("alloc fn failed\n"); + jffs2_free_tmp_dnode_info(tn); + return -ENOMEM; + } + + tn->version = je32_to_cpu(rd->version); + tn->fn->ofs = je32_to_cpu(rd->offset); + tn->fn->raw = ref; + + /* There was a bug where we wrote hole nodes out with + csize/dsize swapped. Deal with it */ + if (rd->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(rd->dsize) && je32_to_cpu(rd->csize)) + tn->fn->size = je32_to_cpu(rd->csize); + else // normal case... + tn->fn->size = je32_to_cpu(rd->dsize); + + JFFS2_DBG_READINODE("dnode @%08x: ver %u, offset %#04x, dsize %#04x\n", + ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize)); + + jffs2_add_tn_to_tree(tn, tnp); + + return 0; +} + +/* + * Helper function for jffs2_get_inode_nodes(). + * It is called every time an unknown node is found. + * + * Returns: 0 on succes; + * 1 if the node should be marked obsolete; + * negative error code on failure. + */ +static inline int +read_unknown(struct jffs2_sb_info *c, + struct jffs2_raw_node_ref *ref, + struct jffs2_unknown_node *un, + uint32_t read) +{ + /* We don't mark unknown nodes as REF_UNCHECKED */ + BUG_ON(ref_flags(ref) == REF_UNCHECKED); + + un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); + + if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) { + /* Hmmm. This should have been caught at scan time. */ + JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref)); + jffs2_dbg_dump_node(c, ref_offset(ref)); + return 1; + } else { + switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { + + case JFFS2_FEATURE_INCOMPAT: + JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + /* EEP */ + BUG(); + break; + + case JFFS2_FEATURE_ROCOMPAT: + JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); + break; + + case JFFS2_FEATURE_RWCOMPAT_COPY: + JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + break; + + case JFFS2_FEATURE_RWCOMPAT_DELETE: + JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + return 1; + } + } + + return 0; +} + +/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated + with this ino, returning the former in order of version */ + +static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct rb_root *tnp, struct jffs2_full_dirent **fdp, + uint32_t *highest_version, uint32_t *latest_mctime, + uint32_t *mctime_ver) +{ + struct jffs2_raw_node_ref *ref, *valid_ref; + struct rb_root ret_tn = RB_ROOT; + struct jffs2_full_dirent *ret_fd = NULL; + union jffs2_node_union node; + size_t retlen; + int err; + + *mctime_ver = 0; + + JFFS2_DBG_READINODE("ino #%u\n", f->inocache->ino); + + spin_lock(&c->erase_completion_lock); + + valid_ref = jffs2_first_valid_node(f->inocache->nodes); + + if (!valid_ref && (f->inocache->ino != 1)) + JFFS2_WARNING("no valid nodes for ino #%u\n", f->inocache->ino); + + while (valid_ref) { + /* 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. So if we mark 'ref' obsolete while we're + not holding the lock, it can go away immediately. For that reason, + we find the next valid node first, before processing 'ref'. + */ + ref = valid_ref; + valid_ref = jffs2_first_valid_node(ref->next_in_ino); + spin_unlock(&c->erase_completion_lock); + + cond_resched(); + + /* FIXME: point() */ + err = jffs2_flash_read(c, (ref_offset(ref)), + min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)), + &retlen, (void *)&node); + if (err) { + JFFS2_ERROR("error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref)); + goto free_out; + } + + switch (je16_to_cpu(node.u.nodetype)) { + + case JFFS2_NODETYPE_DIRENT: + JFFS2_DBG_READINODE("node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)); + + if (retlen < sizeof(node.d)) { + JFFS2_ERROR("short read dirent at %#08x\n", ref_offset(ref)); + err = -EIO; + goto free_out; + } + + err = read_direntry(c, ref, &node.d, retlen, &ret_fd, latest_mctime, mctime_ver); + if (err == 1) { + jffs2_mark_node_obsolete(c, ref); + break; + } else if (unlikely(err)) + goto free_out; + + if (je32_to_cpu(node.d.version) > *highest_version) + *highest_version = je32_to_cpu(node.d.version); + + break; + + case JFFS2_NODETYPE_INODE: + JFFS2_DBG_READINODE("node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)); + + if (retlen < sizeof(node.i)) { + JFFS2_ERROR("short read dnode at %#08x\n", ref_offset(ref)); + err = -EIO; + goto free_out; + } + + err = read_dnode(c, ref, &node.i, retlen, &ret_tn, latest_mctime, mctime_ver); + if (err == 1) { + jffs2_mark_node_obsolete(c, ref); + break; + } else if (unlikely(err)) + goto free_out; + + if (je32_to_cpu(node.i.version) > *highest_version) + *highest_version = je32_to_cpu(node.i.version); + + JFFS2_DBG_READINODE("version %d, highest_version now %d\n", + je32_to_cpu(node.i.version), *highest_version); + + break; + + default: + /* Check we've managed to read at least the common node header */ + if (retlen < sizeof(struct jffs2_unknown_node)) { + JFFS2_ERROR("short read unknown node at %#08x\n", ref_offset(ref)); + return -EIO; + } + + err = read_unknown(c, ref, &node.u, retlen); + if (err == 1) { + jffs2_mark_node_obsolete(c, ref); + break; + } else if (unlikely(err)) + goto free_out; + + } + 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; +} + +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 = NULL; + struct rb_root tn_list; + struct rb_node *rb, *repl_rb; + 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; + + JFFS2_DBG_READINODE("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, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); + + if (ret) { + JFFS2_ERROR("cannot read nodes for ino %u, returned error is %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; + + rb = rb_first(&tn_list); + + while (rb) { + tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb); + fn = tn->fn; + + if (f->metadata) { + if (likely(tn->version >= mdata_ver)) { + JFFS2_DBG_READINODE("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; + } else { + /* This should never happen. */ + JFFS2_ERROR("Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n", + ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw)); + jffs2_mark_node_obsolete(c, fn->raw); + jffs2_free_full_dnode(fn); + /* Fill in latest_node from the metadata, not this one we're about to free... */ + fn = f->metadata; + goto next_tn; + } + } + + 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 */ + JFFS2_DBG_READINODE("metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version); + f->metadata = fn; + mdata_ver = tn->version; + } + next_tn: + BUG_ON(rb->rb_left); + if (rb->rb_parent && rb->rb_parent->rb_left == rb) { + /* We were then left-hand child of our parent. We need + to move our own right-hand child into our place. */ + repl_rb = rb->rb_right; + if (repl_rb) + repl_rb->rb_parent = rb->rb_parent; + } else + repl_rb = NULL; + + rb = rb_next(rb); + + /* Remove the spent tn from the tree; don't bother rebalancing + but put our right-hand child in our own place. */ + if (tn->rb.rb_parent) { + if (tn->rb.rb_parent->rb_left == &tn->rb) + tn->rb.rb_parent->rb_left = repl_rb; + else if (tn->rb.rb_parent->rb_right == &tn->rb) + tn->rb.rb_parent->rb_right = repl_rb; + else BUG(); + } else if (tn->rb.rb_right) + tn->rb.rb_right->rb_parent = NULL; + + jffs2_free_tmp_dnode_info(tn); + } + jffs2_dbg_fragtree_paranoia_check_nolock(f); + + if (!fn) { + /* No data nodes for this inode. */ + if (f->inocache->ino != 1) { + JFFS2_WARNING("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; + } + JFFS2_NOTICE("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)) { + JFFS2_ERROR("failed to read from flash: error %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)) { + JFFS2_ERROR("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_fragtree(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; + + if (f->inocache->state != INO_STATE_CHECKING) { + /* Symlink's inode data is the target path. Read it and + * keep in RAM to facilitate quick follow symlink + * operation. */ + f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); + if (!f->target) { + JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -ENOMEM; + } + + ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node), + je32_to_cpu(latest_node->csize), &retlen, (char *)f->target); + + if (ret || retlen != je32_to_cpu(latest_node->csize)) { + if (retlen != je32_to_cpu(latest_node->csize)) + ret = -EIO; + kfree(f->target); + f->target = NULL; + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -ret; + } + + f->target[je32_to_cpu(latest_node->csize)] = '\0'; + JFFS2_DBG_READINODE("symlink's target '%s' cached\n", f->target); + } + + /* 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) { + JFFS2_ERROR("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)) { + JFFS2_ERROR("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))) { + JFFS2_ERROR("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; +} + +/* Scan the list of all nodes present for this ino, build map of versions, etc. */ +int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + uint32_t ino, struct jffs2_raw_inode *latest_node) +{ + JFFS2_DBG_READINODE("read inode #%u\n", ino); + + retry_inocache: + spin_lock(&c->inocache_lock); + f->inocache = jffs2_get_ino_cache(c, ino); + + 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. */ + JFFS2_DBG_READINODE("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. */ + JFFS2_ERROR("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) { + JFFS2_ERROR("cannot allocate inocache for root inode\n"); + return -ENOMEM; + } + JFFS2_DBG_READINODE("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) { + JFFS2_ERROR("requestied to read an 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); + int ret; + + if (!f) + return -ENOMEM; + + memset(f, 0, sizeof(*f)); + init_MUTEX_LOCKED(&f->sem); + f->inocache = ic; + + ret = jffs2_do_read_inode_internal(c, f, &n); + if (!ret) { + up(&f->sem); + jffs2_do_clear_inode(c, f); + } + kfree (f); + return ret; +} + +void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) +{ + struct jffs2_full_dirent *fd, *fds; + int deleted; + + down(&f->sem); + deleted = f->inocache && !f->inocache->nlink; + + if (f->inocache && f->inocache->state != INO_STATE_CHECKING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING); + + 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); + + if (f->target) { + kfree(f->target); + f->target = 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); + if (f->inocache->nodes == (void *)f->inocache) + jffs2_del_ino_cache(c, f->inocache); + } + + up(&f->sem); +} diff --git a/ecos/packages/fs/jffs2/current/src/scan.c b/ecos/packages/fs/jffs2/current/src/scan.c new file mode 100644 index 0000000..fcd6314 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/scan.c @@ -0,0 +1,927 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: scan.c,v 1.121 2005/07/20 15:32:28 dedekind 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 DEFAULT_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 + +static inline int min_free(struct jffs2_sb_info *c) +{ + uint32_t min = 2 * sizeof(struct jffs2_raw_inode); +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) + return c->wbuf_pagesize; +#endif + return min; + +} + +static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) { + if (sector_size < DEFAULT_EMPTY_SCAN_SIZE) + return sector_size; + else + return DEFAULT_EMPTY_SCAN_SIZE; +} + +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; + + /* Respect kmalloc limitations */ + if (buf_size > 128*1024) + buf_size = 128*1024; + + 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) + goto out; + + jffs2_dbg_acct_paranoia_check_nolock(c, 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. */ + /* We want to remember the block with most free space + and stick it in the 'nextblock' position to start writing to it. */ + if (jeb->free_size > min_free(c) && + (!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; + } +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + 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; + } +#endif + if (c->nr_erasing_blocks) { + if ( !c->used_size && ((c->nr_free_blocks+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); + ret = -EIO; + goto out; + } + jffs2_erase_pending_trigger(c); + } + ret = 0; + out: + if (buf_size) + kfree(flashbuf); +#ifndef __ECOS + else + c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); +#endif + return ret; +} + +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; +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + 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_WRITEBUFFER + 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(c->sector_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(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) + ofs += 4; + + if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) { +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + 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; + default: return ret; + } + } +#endif + D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); + if (c->cleanmarker_size == 0) + return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */ + else + 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; + +scan_more: + while(ofs < jeb->offset + c->sector_size) { + + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + + cond_resched(); + + if (ofs & 3) { + printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs); + ofs = PAD(ofs); + 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; + uint32_t empty_start; + + empty_start = ofs; + ofs += 4; + + D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); + more_empty: + inbuf_ofs = ofs - buf_ofs; + while (inbuf_ofs < buf_len) { + if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) { + printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", + empty_start, ofs); + DIRTY_SPACE(ofs-empty_start); + goto scan_more; + } + + inbuf_ofs+=4; + ofs += 4; + } + /* Ran off end. */ + D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs)); + + /* If we're only checking the beginning of a block with a cleanmarker, + bail now */ + if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && + c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) { + D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size))); + return BLK_STATE_CLEANMARKER; + } + + /* See how much more there is to read in this eraseblock... */ + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + if (!buf_len) { + /* No more to read. Break out of main loop without marking + this range of empty space as dirty (because it's not) */ + D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n", + empty_start)); + break; + } + D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; + goto more_empty; + } + + 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 "Dirty 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_phys) ) + 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; + + if (ino > c->highest_ino) + c->highest_ino = ino; + + 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))); + jffs2_free_raw_node_ref(raw); + 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")); + } +} diff --git a/ecos/packages/fs/jffs2/current/src/write.c b/ecos/packages/fs/jffs2/current/src/write.c new file mode 100644 index 0000000..4c418e6 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/src/write.c @@ -0,0 +1,702 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: write.c,v 1.94 2005/07/20 15:50:51 dedekind 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" +#include "compr.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)); + + f->inocache = ic; + f->inocache->nlink = 1; + f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; + f->inocache->state = INO_STATE_PRESENT; + + + jffs2_add_ino_cache(c, f->inocache); + D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); + ri->ino = cpu_to_je32(f->inocache->ino); + + 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; +} + +/* 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, int alloc_mode) + +{ + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dnode *fn; + size_t retlen; + struct kvec vecs[2]; + int ret; + int retried = 0; + 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; + + jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); + + 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); + } + + fn->ofs = je32_to_cpu(ri->offset); + fn->size = je32_to_cpu(ri->dsize); + fn->frags = 0; + + /* check number of valid vecs */ + if (!datalen || !data) + cnt = 1; + retry: + fn->raw = raw; + + raw->flash_offset = flash_ofs; + raw->__totlen = PAD(sizeof(*ri)+datalen); + raw->next_phys = NULL; + + if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) { + BUG_ON(!retried); + D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, " + "highest version %d -> updating dnode\n", + je32_to_cpu(ri->version), f->highest_version)); + ri->version = cpu_to_je32(++f->highest_version); + ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + } + + ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, + (alloc_mode==ALLOC_GC)?0:f->inocache->ino); + + 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); + } + if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) { + /* Try to reallocate space and retry */ + uint32_t dummy; + struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; + + retried = 1; + + D1(printk(KERN_DEBUG "Retrying failed write.\n")); + + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); + + if (alloc_mode == ALLOC_GC) { + ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy); + } else { + /* Locking pain */ + up(&f->sem); + jffs2_complete_reservation(c); + + ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode); + down(&f->sem); + } + + if (!ret) { + D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); + + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); + + goto retry; + } + D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); + jffs2_free_raw_node_ref(raw); + } + /* Release the full_dnode which is now useless, and return */ + jffs2_free_full_dnode(fn); + 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 */ + spin_lock(&c->erase_completion_lock); + raw->next_in_ino = f->inocache->nodes; + f->inocache->nodes = raw; + spin_unlock(&c->erase_completion_lock); + + 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 (retried) { + jffs2_dbg_acct_sanity_check(c,NULL); + } + + 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, int alloc_mode) +{ + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd; + size_t retlen; + struct kvec vecs[2]; + int retried = 0; + 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(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; + + jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); + + 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); + } + + 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; + + retry: + fd->raw = raw; + + raw->flash_offset = flash_ofs; + raw->__totlen = PAD(sizeof(*rd)+namelen); + raw->next_phys = NULL; + + if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) { + BUG_ON(!retried); + D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, " + "highest version %d -> updating dirent\n", + je32_to_cpu(rd->version), f->highest_version)); + rd->version = cpu_to_je32(++f->highest_version); + fd->version = je32_to_cpu(rd->version); + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); + } + + ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen, + (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino)); + 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->next_in_ino = NULL; + 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); + } + if (!retried && (raw = jffs2_alloc_raw_node_ref())) { + /* Try to reallocate space and retry */ + uint32_t dummy; + struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; + + retried = 1; + + D1(printk(KERN_DEBUG "Retrying failed write.\n")); + + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); + + if (alloc_mode == ALLOC_GC) { + ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy); + } else { + /* Locking pain */ + up(&f->sem); + jffs2_complete_reservation(c); + + ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode); + down(&f->sem); + } + + if (!ret) { + D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); + goto retry; + } + D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); + jffs2_free_raw_node_ref(raw); + } + /* Release the full_dnode which is now useless, and return */ + jffs2_free_full_dirent(fd); + return ERR_PTR(ret?ret:-EIO); + } + /* Mark the space used */ + raw->flash_offset |= REF_PRISTINE; + jffs2_add_physical_node_ref(c, raw); + + spin_lock(&c->erase_completion_lock); + raw->next_in_ino = f->inocache->nodes; + f->inocache->nodes = raw; + spin_unlock(&c->erase_completion_lock); + + if (retried) { + jffs2_dbg_acct_sanity_check(c,NULL); + } + + 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; + uint16_t comprtype = JFFS2_COMPR_NONE; + uint32_t phys_ofs, alloclen; + uint32_t datalen, cdatalen; + int retried = 0; + + retry: + 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 = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); + cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); + + comprtype = jffs2_compress(c, f, buf, &comprbuf, &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->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 & 0xff; + ri->usercompr = (comprtype >> 8 ) & 0xff; + 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, ALLOC_NORETRY); + + jffs2_free_comprbuf(comprbuf, buf); + + if (IS_ERR(fn)) { + ret = PTR_ERR(fn); + up(&f->sem); + jffs2_complete_reservation(c); + if (!retried) { + /* Write error to be retried */ + retried = 1; + D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n")); + goto retry; + } + 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; + 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, ALLOC_NORMAL); + + 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; + + up(&f->sem); + 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, ALLOC_NORMAL); + + 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; + + if (1 /* alternative branch needs testing */ || + !jffs2_can_mark_obsolete(c)) { + /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */ + + 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, ALLOC_DELETION); + + 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); + } else { + struct jffs2_full_dirent **prev = &dir_f->dents; + uint32_t nhash = full_name_hash(name, namelen); + + down(&dir_f->sem); + + while ((*prev) && (*prev)->nhash <= nhash) { + if ((*prev)->nhash == nhash && + !memcmp((*prev)->name, name, namelen) && + !(*prev)->name[namelen]) { + struct jffs2_full_dirent *this = *prev; + + D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n", + this->ino, ref_offset(this->raw))); + + *prev = this->next; + jffs2_mark_node_obsolete(c, (this->raw)); + jffs2_free_full_dirent(this); + break; + } + prev = &((*prev)->next); + } + 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); + + if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) { + 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, ALLOC_NORMAL); + + 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; +} diff --git a/ecos/packages/fs/jffs2/current/support/jffs2/etc/group b/ecos/packages/fs/jffs2/current/support/jffs2/etc/group new file mode 100644 index 0000000..0b01a78 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/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: diff --git a/ecos/packages/fs/jffs2/current/support/jffs2/etc/passwd b/ecos/packages/fs/jffs2/current/support/jffs2/etc/passwd new file mode 100644 index 0000000..2805e81 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/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 diff --git a/ecos/packages/fs/jffs2/current/support/jffs2/jffs2.img b/ecos/packages/fs/jffs2/current/support/jffs2/jffs2.img Binary files differnew file mode 100644 index 0000000..964095d --- /dev/null +++ b/ecos/packages/fs/jffs2/current/support/jffs2/jffs2.img diff --git a/ecos/packages/fs/jffs2/current/tests/jffs2_1.c b/ecos/packages/fs/jffs2/current/tests/jffs2_1.c new file mode 100644 index 0000000..6eb4a32 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/tests/jffs2_1.c @@ -0,0 +1,726 @@ +//========================================================================== +// +// jffs2_1.c +// +// Test fileio system +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public 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 <pkgconf/io_flash.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 <stdio.h> +#include <stdlib.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 + +//========================================================================== +// Mount details + +#define stringify2(_x_) #_x_ +#define stringify(_x_) stringify2(_x_) + +#if defined(CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1) +# define JFFS2_TEST_DEV CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1 +#elif defined(CYGFUN_IO_FLASH_BLOCK_FROM_FIS) +# define JFFS2_TEST_DEV "/dev/flash/fis/jffs2test" +#else +// fall back to using a user set area in the first device (only) +# define JFFS2_TEST_DEV "/dev/flash/0/" stringify(CYGNUM_FS_JFFS2_TEST_OFFSET) "," stringify(CYGNUM_FS_JFFS2_TEST_LENGTH) +#endif + +// we could use an mtab but we don't in order to get better diagnostics +// by calling mount() directly. + +#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 %ld %s\n", \ + (unsigned long)_res, _res<0?strerror(errno):""); + +//========================================================================== + +#define IOSIZE 1000 + +#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); +#ifdef CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE + diag_printf(" d_type %2d", entry->d_type); +#endif + 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 %ld]", + sbuf.st_mode,sbuf.st_ino,sbuf.st_nlink, + (unsigned long) sbuf.st_size); + } +#ifdef CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE + if ((entry->d_type & S_IFMT) != (sbuf.st_mode & S_IFMT)) + CYG_TEST_FAIL("File mode's don't match between dirent and stat"); +#endif + } + + 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 ) +{ + unsigned 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 ) +{ + unsigned 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[%ld+%d](%02x) != %02x\n", + (unsigned long)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; + + struct mallinfo info; + + info = mallinfo(); + diag_printf("arenasize %d, freeblocks %d, totalallocated %d, totalfree %d, maxfree %d\n", + info.arena, info.ordblks, info.uordblks, info.fordblks, info.maxfree); + + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + err = mount( JFFS2_TEST_DEV, "/", "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( JFFS2_TEST_DEV, "/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 ); + +#ifdef CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_2 + diag_printf("<INFO>: mounting second JFFS2 filesystem on /mnt\n"); + + err = mount( CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_2, "/mnt", "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"); + + listdir( "/mnt", true, -1, &existingdirents ); + if ( existingdirents < 2 ) + CYG_TEST_FAIL("Not enough dir entries\n"); + + diag_printf("<INFO>: umount /mnt\n"); + err = umount( "/mnt" ); +#endif + + diag_printf("<INFO>: umount /\n"); + err = umount( "/" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + CYG_TEST_PASS_FINISH("jffs2_1"); +} + +// ------------------------------------------------------------------------- +// EOF jffs2_1.c diff --git a/ecos/packages/fs/jffs2/current/tests/jffs2_2.c b/ecos/packages/fs/jffs2/current/tests/jffs2_2.c new file mode 100644 index 0000000..e6f172e --- /dev/null +++ b/ecos/packages/fs/jffs2/current/tests/jffs2_2.c @@ -0,0 +1,261 @@ +//========================================================================== +// +// jffs2_2.c +// +// Test fseek on a filesystem +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): asl +// Contributors: asl +// Date: 2004-03-29 +// Purpose: Test fseek on a filesystem +// Description: This test uses the ramfs to check out the fseek +// operation on a filesystem. +// +// +// +// +// +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/io_flash.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#include <cyg/fileio/fileio.h> +#include <cyg/io/flash.h> + +#include <cyg/infra/testcase.h> +#include <cyg/infra/diag.h> // HAL polled output + +#include <pkgconf/fs_jffs2.h> // Address of JFFS2 + +//========================================================================== +// Mount details + +#define stringify2(_x_) #_x_ +#define stringify(_x_) stringify2(_x_) + +#if defined(CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1) +# define JFFS2_TEST_DEV CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1 +#elif defined(CYGFUN_IO_FLASH_BLOCK_FROM_FIS) +# define JFFS2_TEST_DEV "/dev/flash/fis/jffs2test" +#else +// fall back to using a user set area in the first device (only) +# define JFFS2_TEST_DEV "/dev/flash/0/" stringify(CYGNUM_FS_JFFS2_TEST_OFFSET) "," stringify(CYGNUM_FS_JFFS2_TEST_LENGTH) +#endif + +//========================================================================== + +#define SHOW_RESULT( _fn, _res ) \ +diag_printf("FAIL: " #_fn "() returned %ld %s\n", \ + (unsigned long)_res, _res<0?strerror(errno):""); + +//========================================================================== + +char buf[1024]; +char buf1[1024]; + +//========================================================================== +// main + +int main( int argc, char **argv ) +{ + int err; + FILE *stream; + long pos; + int i; + + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + CYG_TEST_INFO("mount /"); + err = mount( JFFS2_TEST_DEV, "/", "jffs2" ); + + if( err < 0 ) SHOW_RESULT( mount, err ); + + CYG_TEST_INFO("creating /fseek"); + stream = fopen("/fseek","w+"); + if (!stream) { + diag_printf("FAIL: fopen() returned NULL, %s\n", strerror(errno)); + CYG_TEST_FINISH("done"); \ + } + + /* Write a buffer full of cyclic numbers */ + for (i = 0; i < sizeof(buf); i++) { + buf[i] = i % 256; + } + + CYG_TEST_INFO("writing test pattern"); + err=fwrite(buf,sizeof(buf), 1, stream); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + + /* The current position should be the same size as the buffer */ + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != sizeof(buf)) + diag_printf("<FAIL>: ftell is not telling the truth."); + + CYG_TEST_INFO("fseek()ing to beginning and writing"); + + /* Seek back to the beginning of the file */ + err = fseek(stream, 0, SEEK_SET); + if ( err < 0 ) SHOW_RESULT( fseek, err ); + + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != 0) CYG_TEST_FAIL("ftell is not telling the truth"); + + /* Write 4 zeros to the beginning of the file */ + for (i = 0; i < 4; i++) { + buf[i] = 0; + } + + err = fwrite(buf, 4, 1, stream); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + + /* Check the pointer is at 4 */ + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != 4) CYG_TEST_FAIL("ftell is not telling the truth"); + + CYG_TEST_INFO("closing file"); + + /* Close the file, open it up again and read it back */ + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("open file /fseek"); + stream = fopen("/fseek", "r+"); + if (!stream) { + diag_printf("<FAIL>: fopen() returned NULL, %s\n", strerror(errno)); + } + + err = fread(buf1,sizeof(buf1),1, stream); + if (err != 1) SHOW_RESULT( fread, err ); + + CYG_TEST_INFO("Comparing contents"); + if (memcmp(buf, buf1, sizeof(buf1))) { + CYG_TEST_FAIL("File contents inconsistent"); + } + + CYG_TEST_INFO("closing file"); + + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("open file /fseek"); + stream = fopen("/fseek", "r+"); + if (!stream) { + diag_printf("<FAIL>: fopen() returned NULL, %s\n", strerror(errno)); + } + + CYG_TEST_INFO("fseek()ing past the end to create a hole"); + /* Seek 1K after the end of the file */ + err = fseek(stream, sizeof(buf), SEEK_END); + if ( err < 0 ) SHOW_RESULT( fseek, err ); + + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != (2*sizeof(buf))) CYG_TEST_FAIL("ftell is not telling the truth"); + + CYG_TEST_INFO("writing test pattern"); + err=fwrite(buf,sizeof(buf), 1, stream); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != (3*sizeof(buf))) CYG_TEST_FAIL("ftell is not telling the truth"); + + CYG_TEST_INFO("closing file"); + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("open file /fseek"); + stream = fopen("/fseek", "r+"); + if (!stream) { + diag_printf("<FAIL>: fopen() returned NULL, %s\n", strerror(errno)); + } + + err = fread(buf1,sizeof(buf1),1, stream); + if (err != 1) SHOW_RESULT( fread, err ); + + CYG_TEST_INFO("Comparing contents"); + if (memcmp(buf, buf1, sizeof(buf1))) { + CYG_TEST_FAIL("File contents inconsistent"); + } + + err = fread(buf1,sizeof(buf1),1, stream); + if (err != 1) SHOW_RESULT( fread, err ); + + for (i = 0; i< sizeof(buf); i++) { + if (buf1[i] != 0) + CYG_TEST_FAIL("Hole does not contain zeros"); + } + + err = fread(buf1,sizeof(buf1),1, stream); + if (err != 1) SHOW_RESULT( fread, err ); + + if (memcmp(buf, buf1, sizeof(buf1))) { + CYG_TEST_FAIL("File contents inconsistent"); + } + + CYG_TEST_INFO("closing file"); + + /* Close the file */ + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("umount /"); + err = umount( "/" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + CYG_TEST_PASS_FINISH("jffs2_2"); +} diff --git a/ecos/packages/fs/jffs2/current/tests/jffs2_3.c b/ecos/packages/fs/jffs2/current/tests/jffs2_3.c new file mode 100644 index 0000000..7c6d887 --- /dev/null +++ b/ecos/packages/fs/jffs2/current/tests/jffs2_3.c @@ -0,0 +1,198 @@ +//========================================================================== +// +// jffs2_3.c +// +// Test garbage collect on a filesystem +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2005 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): asl +// Contributors: asl +// Date: 2005-01-16 +// Purpose: Test garbage collect on a filesystem +// Description: This test creates and deletes files in order +// to test the garbage collection code. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/io_flash.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#include <cyg/fileio/fileio.h> +#include <cyg/io/flash.h> +#include <cyg/crc/crc.h> + +#include <cyg/infra/testcase.h> +#include <cyg/infra/diag.h> // HAL polled output + +#include <pkgconf/fs_jffs2.h> // Address of JFFS2 + +//========================================================================== + +#define stringify2(_x_) #_x_ +#define stringify(_x_) stringify2(_x_) + +#if defined(CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1) +# define JFFS2_TEST_DEV CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1 +#elif defined(CYGFUN_IO_FLASH_BLOCK_FROM_FIS) +# define JFFS2_TEST_DEV "/dev/flash/fis/jffs2test" +#else +// fall back to using a user set area in the first device (only) +# define JFFS2_TEST_DEV "/dev/flash/0/" stringify(CYGNUM_FS_JFFS2_TEST_OFFSET) "," stringify(CYGNUM_FS_JFFS2_TEST_LENGTH) +#endif + +//========================================================================== + +#define ITERATIONS 1000000 +#define NELEM(_x_) (sizeof(_x_)/sizeof(*(_x_))) + +#define SHOW_RESULT( _fn, _res ) \ +diag_printf("FAIL: " #_fn "() returned %d %s\n", _res, \ + (unsigned long) _res<0?strerror(errno):""); + +//========================================================================== +// file creation, deletion and testing functions + +static void create_file(int i) +{ + cyg_int32 buffer[1020]; + char name[16]; + cyg_uint32 j; + int fd, err; + + sprintf(name,"test%07d",i); + + fd = creat(name, S_IRWXU); + if (fd == -1) SHOW_RESULT( creat, fd ); + + for (j=1; j < NELEM(buffer); j++) { + buffer[j] = rand(); + } + + buffer[0] = 0; + buffer[0] = cyg_posix_crc32((unsigned char *)buffer, sizeof(buffer)); + + err = write(fd, buffer, sizeof(buffer)); + if (err == -1) SHOW_RESULT( write, err ); + + err = close(fd); + if (err == -1) SHOW_RESULT( close, err ); +} + +static void delete_file(int i) +{ + char name[16]; + int err; + + sprintf(name,"test%07d",i); + + err = unlink(name); + if (err == -1) SHOW_RESULT( unlink, err ); +} + +static void check_file(int i) +{ + char name[16]; + int err, fd; + cyg_int32 buffer[1020]; + cyg_uint32 crc; + + sprintf(name,"test%07d",i); + + fd = open(name, O_RDONLY); + if (fd == -1) SHOW_RESULT( open, fd ); + + err = read(fd, buffer, sizeof(buffer)); + if (err == -1) SHOW_RESULT( read, fd ); + + crc = buffer[0]; + buffer[0] = 0; + + if (crc != cyg_posix_crc32((unsigned char *)buffer, sizeof(buffer))) { + CYG_TEST_FAIL("File corrupt"); + } + + err = close(fd); + if (err == -1) SHOW_RESULT( read, fd ); +} + + +//========================================================================== +// main + +int main( int argc, char **argv ) +{ + int err, iteration; + struct mallinfo minfo +; + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + CYG_TEST_INFO("mount /"); + err = mount( JFFS2_TEST_DEV, "/", "jffs2" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + chdir ("/"); + + iteration=0; + create_file(iteration); + while (iteration < ITERATIONS) { + if (!(iteration % 1000)) { + minfo = mallinfo(); + diag_printf("<INFO> Iteration %07d fordblks = %7d\n", + iteration, minfo.fordblks); + } + iteration++; + create_file(iteration); + check_file(iteration-1); + delete_file(iteration-1); + check_file(iteration); + } + + CYG_TEST_INFO("umount /"); + err = umount( "/" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + CYG_TEST_PASS_FINISH("jffs2_3"); +} diff --git a/ecos/packages/fs/ram/current/ChangeLog b/ecos/packages/fs/ram/current/ChangeLog new file mode 100644 index 0000000..f22f384 --- /dev/null +++ b/ecos/packages/fs/ram/current/ChangeLog @@ -0,0 +1,170 @@ +2010-03-29 Oyvind Harboe <oyvind.harboe@zylin.com> + Jonathan Larmour <jifl@eCosCentric.com> + + * src/ramfs.c (add_direntry): Fix directory file name corruption + with the SIMPLE allocation method: do not cache pointers returned + from findbuffer_node(), but do a fresh lookup based on position. + +2010-02-27 Sergei Gavrikov <sergei.gavrikov@gmail.com> + + * src/ramfs.c (find_entry): Fixed wrong type for a local counter + (namelen): s/char/int/. + +2009-04-28 John Dallaway <john@dallaway.org.uk> + + cdl/ramfs.cdl: Use CYGPKG_IO_FILEIO as the parent. + +2008-04-02 Xinghua Yang <yxinghua@sunnorth.com.cn> + Taiyun Wang <taiyun@sunnorth.com.cn> + + * cdl/ramfs.cdl: Use CYGPKG_FS_RAM_RET_DIRENT_DTYPE to control + whether ram sets file type in ramfs_fo_dirread. + * src/ramfs.c: Set file type in ramfs_fo_dirread. + * tests/ramfs1.c: Test the new d_type in dirent when present. + +2006-10-05 Andrew Lunn <andrew.lunn@ascom.ch + Paluch Sebastian <the_sorcerer&op.pl> + + * test/ramfs3 (new): Test for lseek bug found in fileio. + * cdl/ramfs.cdl: Build new test. + +2006-06-25 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/ramfs.c (find_direntry): Don't search off the end of the + directory node into hyperspace. + * src/ramfs.c (ramfs_getinfo): Support for block usage call. + * tests/ramfs1.c (main): Add file system block usage test. + +2006-05-17 Andy Jackson <andy@grapevinetech.co.uk> + + * tests/ramfs1.c (createfile): Fix compile warnings. + +2005-10-01 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/ramfs.c: Implement holes in files when using BLOCK + allocation method. This requires allowing lseek to go past the end + of the file. + + * test/ramfs2.c: Extended the lseek test to now seek past the end + of the file. With the BLOCK allocation method this will create a + whole. With the SIMPLE allocation method is just allocates the + memory and fills it with zero. + + * cdl/ramfs.cdl: Added in interface which both SIMPLE and BLOCK + implement. This allows the inference engine to work out is should + enable BLOCK if SIMPLE is disabled by the user. + +2005-10-01 Dan Jakubiec <dan.jakubiec@systech.com> + + * src/ramfs.c: (ramfs_mount, ramfs_open, ramfs_mkdir) Changed the + permissions for files and directories created on RAMFS file systems + from 000 to 777. This helps ported applications which wrongly + assume there is some security concept and check for the existence + of certain file permissions. + +2005-03-27 Andrew Lunn <andrew.lunn@ascom.ch> + + * tests/ramfs1.c (SHOW_RESULT): Fixed compiler warning about format + not matching type. + +2004-12-13 John Dallaway <jld@ecoscentric.com> + + * tests/fileio1.c: Rename to ramfs1.c. eCos test names should be + unique. + * tests/fseek1.c: Rename to ramfs2.c: + * cdl/ramfs.cdl: Build the renamed tests. + +2004-03-29 Andrew Lunn <andrew.lunn@ascom.ch> + + * test/fseek1.c: Test the fseek/ftell functions + * cdl/ramfs.cdl: Added a fseek1 test to the build + +2004-03-15 Sebastien Couret <sebastien.couret@elios-informatique.fr> + + * src/ramfs.c (block_init): Fixed compiler warning. + +2004-02-20 Vincent Catros <Vincent.Catros@elios-informatique.fr> + + * src/ramfs.c : + (ramfs_find) Policy to skip path separator is no longer + "if '/' then skip" but "while '/' then skip" allowing + multi '/' separators (i.e : /tmp////foo). + (find_entry) Policy to detect end of path is no longer + "if '\0' then end_of_path" + but "while '/' skip it and then if '\0' then end_of_path" + allowing path terminated with any number of '/' + (i.e : chdir(/tmp///)). + +2004-02-25 Jonathan Larmour <jifl@eCosCentric.com> + + * src/ramfs.c (findbuffer_node): If pos larger than even INDIRECT2_MAX + then return ENOSPC, not EINVAL. Thanks to Vincent Catros for spotting + this. + +2004-01-08 Vincent Catros <Vincent.Catros@elios-informatique.fr> + + * src/ramfs.c (ramfs_fo_write): Return ENOSPC when the filesystem + is full. + +2003-02-24 Jonathan Larmour <jifl@eCosCentric.com> + + * cdl/ramfs.cdl: Fix doc link. + +2002-12-06 Andrew Lunn <andrew.lunn@ascom.ch> + + * cdl/ramfs.cdl: Implements the CYGINT_IO_FILEIO_FS interface + +2002-01-25 Jonathan Larmour <jlarmour@redhat.com> + + * tests/fileio1.c (main): Check in listdir that the number of + dirents is correct. + +2001-07-26 Jonathan Larmour <jlarmour@redhat.com> + + * src/ramfs.c (findbuffer_indirect1): Determine correct offset in + indirect block list. + (findbuffer_indirect2): Ditto. + (findbuffer_direct): Compare block index with number of blocks + correctly. + +2000-10-05 Nick Garnett <nickg@cygnus.co.uk> + + * tests/fileio1.c: + Extended to check getcwd() and chdir() functionality more fully. + +2000-08-18 Nick Garnett <nickg@cygnus.co.uk> + + * cdl/ramfs.cdl: + * src/ramfs.c: + * tests/fileio1.c: + Created this example RAM filesystem both as a usable filesystem + and as an example of how to build filesystems for the fileio + infrastructure. + + + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2009 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== + + diff --git a/ecos/packages/fs/ram/current/cdl/ramfs.cdl b/ecos/packages/fs/ram/current/cdl/ramfs.cdl new file mode 100644 index 0000000..76adedf --- /dev/null +++ b/ecos/packages/fs/ram/current/cdl/ramfs.cdl @@ -0,0 +1,216 @@ +# ==================================================================== +# +# ramfs.cdl +# +# RAM Filesystem configuration data +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2009 Free Software Foundation, 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## This exception does not invalidate any other reasons why a work based +## on this file might be covered by the GNU General Public License. +## ------------------------------------------- +## ####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): nickg +# Original data: nickg +# Contributors: +# Date: 2000-08-01 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_FS_RAM { + display "RAM filesystem" + doc ref/fileio.html + include_dir cyg/ramfs + + parent CYGPKG_IO_FILEIO + requires CYGPKG_IO_FILEIO + + requires CYGPKG_ISOINFRA + requires CYGPKG_ERROR + requires CYGINT_ISO_ERRNO + requires CYGINT_ISO_ERRNO_CODES + + implements CYGINT_IO_FILEIO_FS + + compile -library=libextras.a ramfs.c + + requires CYGINT_FS_RAM_ALLOC == 1 + + cdl_interface CYGINT_FS_RAM_ALLOC { + display "Functions to allocate RAM" + description " + This interface is implemented by functions + which allocate RAM to hold the contents of the files" + } + + + + # ---------------------------------------------------------------------- + # Simple allocation mechanism using malloc() + + cdl_component CYGPKG_FS_RAM_SIMPLE { + display "Simple, malloc() based, implementation" + requires { CYGINT_ISO_MALLOC != 0 } + default_value 1 + active_if !CYGPKG_FS_RAM_BLOCKS + + implements CYGINT_FS_RAM_ALLOC + cdl_option CYGNUM_RAMFS_REALLOC_INCREMENT { + display "Size of file data storage increment" + flavor data + default_value 256 + legal_values 64 to 32768 + description "This option controls the size of the increment to a file data + storage block." + } + + } + + # ---------------------------------------------------------------------- + # Block based allocation, using either malloc() or a private block + # pool. + + cdl_component CYGPKG_FS_RAM_BLOCKS { + display "Block-based RAM filesystem allocation" + default_value 0 + active_if !CYGPKG_FS_RAM_SIMPLE + + implements CYGINT_FS_RAM_ALLOC + cdl_option CYGNUM_RAMFS_BLOCK_SIZE { + display "Size of file data storage block" + flavor data + default_value 256 + legal_values 64 to 32768 + description "This option controls the size of a data storage block." + } + + cdl_option CYGNUM_RAMFS_BLOCKS_DIRECT { + display "Directly referenced data storage blocks" + flavor data + default_value 8 + legal_values 0 to 32 + description "This option controls the number of data storage blocks that + are referenced directly from a file or directory node." + } + + cdl_option CYGNUM_RAMFS_BLOCKS_INDIRECT1 { + display "Single level indirect data storage blocks" + flavor data + default_value 1 + legal_values 0 to 32 + description "This option controls the number of single level indirect storage + blocks that are referenced from a file or directory node." + } + + cdl_option CYGNUM_RAMFS_BLOCKS_INDIRECT2 { + display "Two level indirect data storage blocks" + flavor data + default_value 1 + legal_values 0 to 32 + description "This option controls the number of two level indirect storage + blocks that are referenced from a file or directory node." + } + + cdl_component CYGPKG_FS_RAM_BLOCKS_ARRAY { + display "Use block array rather than malloc()" + default_value 0 + description "This option controls whether the blocks are allocated from + an array of blocks rather from the heap using malloc()." + + cdl_option CYGPKG_FS_RAM_BLOCKS_ARRAY_EXTERN { + display "Block array is external" + default_value 0 + description "This option controls whether the block array is + defined by the RAMFS package or whether it is provided + by an external component. The latter option may be + useful when the RAM file system is to be put into a + special memory area." + } + + cdl_option CYGPKG_FS_RAM_BLOCKS_ARRAY_NAME { + display "Name of external block array" + active_if CYGPKG_FS_RAM_BLOCKS_ARRAY_EXTERN + flavor data + default_value "cyg_ramfs_block_array" + description "This option controls what the symbol name of the external + block array will be." + } + + cdl_option CYGNUM_FS_RAM_BLOCKS_ARRAY_SIZE { + display "Size of blocks array" + flavor data + default_value 128 + legal_values 1 to 9999999999 + description "The number of blocks in the array. The total size of + the array will be this value times the block size." + } + } + } + + cdl_option CYGNUM_RAMFS_DIRENT_SIZE { + display "Directory entry size" + flavor data + default_value 32 + legal_values 16 to { CYGNUM_RAMFS_BLOCK_SIZE ? CYGNUM_RAMFS_BLOCK_SIZE : 128 } + description "This option controls the number of two level indirect storage + blocks that are referenced from a file or directory node." + } + + cdl_option CYGPKG_FS_RAM_RET_DIRENT_DTYPE { + display "Support for fileio's struct dirent d_type field" + flavor bool + default_value 0 + active_if CYGPKG_FILEIO_DIRENT_DTYPE + description "This option controls whether the RAM filesystem supports + setting fileio's struct dirent d_type field. + If this option is enabled, d_type will be set. + Otherwise, nothing will be done, d_type's value will + be zero because fileio already sets it." + } + + # ---------------------------------------------------------------- + # Tests + + cdl_option CYGPKG_FS_RAM_TESTS { + display "RAM FS tests" + flavor data + no_define + calculated { "tests/ramfs1 tests/ramfs2 tests/ramfs3" } + description " + This option specifies the set of tests for the RAM FS package." + } + +} + +# End of ramfs.cdl diff --git a/ecos/packages/fs/ram/current/src/ramfs.c b/ecos/packages/fs/ram/current/src/ramfs.c new file mode 100644 index 0000000..eecbacd --- /dev/null +++ b/ecos/packages/fs/ram/current/src/ramfs.c @@ -0,0 +1,2436 @@ +//========================================================================== +// +// ramfs.c +// +// RAM file system +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2000-07-25 +// Purpose: RAM file system +// Description: This is a RAM filesystem for eCos. It attempts to +// provide full POSIX-compatible filesystem behaviour +// while at the same time being efficient in terms of +// time and space used. +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== +// +// General Description +// =================== +// +// This is an implementation of a RAM filesystem for eCos. Its goal is +// to provide a working example of a filesystem that provides most of +// the required POSIX functionality. And obviously it may also be +// useful in its own right. +// +// +// Nodes +// ----- +// +// All files and directories are represented by node objects. Each +// ramfs_node structure contains the following fields: +// +// mode - Node type, file or directory. +// refcnt - Number of references to this node. For files each open counts as +// a reference and for directories a node is referenced when it is made +// current directory, or is opened for reading. +// nlink - Number of links to this node. Each directory entry that references +// this node is a link. +// size - Size of the data in this node in bytes. +// atime - Last time this node was accessed. +// mtime - Last time the data in this node was modified. +// ctime - Last time the status information in this node was changed. +// +// The data storage in a node is controlled by the configuration and +// can take two forms. These will be described later. +// +// Directories +// ----------- +// +// A directory is a node whose data is a list of directory entries. To +// simplify management of these, long directory entries are split into +// a chain of fixed size ramfs_dirent structures. These contain the +// following fields: +// +// node - Pointer to node referenced by this entry. This is present in +// every directory entry fragment +// inuse - Set to 1 if this entry is in use, zero if it is free. +// first - Set to 1 if this is the first fragment of a directory entry. +// last - Set to 1 if this is the last fragment of a directory entry. +// namelen - The size of the whole file name. +// fraglen - The number of bytes of the file name that are stored in this +// fragment. +// next - The offset of the next fragment of this directory entry. +// name - The characters of the fragment of the file name stored in this +// entry. +// +// Small file names are stored in a single fragment. Longer names are +// stored in a chain of fragments. +// +// Data Storage +// ------------ +// +// Two data storage mechanisms may be configured, the SIMPLE and the +// BLOCKS mechanisms. +// +// SIMPLE Data Storage +// ~~~~~~~~~~~~~~~~~~~ +// +// This mechanism simply uses malloc() and free() to allocate the +// memory for both nodes and file data. File data is stored in a +// single malloced vector that is realloced as necessary to acquire +// more space. +// +// The advantage of this approach is that the RAM filesystem only uses +// as much memory as it needs, the rest is available for use by other +// components. It also requires much simpler data structures and code +// in the filesystem to manage. However, if any files get to be a +// significant proportion of the size of the heap, there is the danger +// that fragmentation will prevent any further extension of some +// files, even if there is enough memory in total. It also requires an +// implementation of malloc() to be present. If this needs to be +// present for other components,then this is not a significant +// overhead, but including it just for use by this filesystem +// represents a major addition of code and data structures. +// +// +// BLOCKS Data Storage +// ~~~~~~~~~~~~~~~~~~~ +// +// This mechanism divides the memory used for file storage into fixed +// sized blocks. These blocks may either be allocated using +// malloc()/free(), or may be obtained from a array of blocks reserved +// for the purpose. Configuration allows the block size to be +// selected, as well as the allocation mechanism, and in the case of a +// block array, whether it is defined here or by an external +// component. +// +// Data storage in nodes is arranges in three arrays of pointers to +// blocks. The first array points directly to data blocks, the second +// to blocks which themselves contain pointers to data blocks, and the +// third to blocks which contain pointers to blocks which contain +// pointers to data blocks. In the default configuration These last +// two arrays have only one element each. +// +// The following shows how the data is arranged in a fully populated +// file with a 256 byte block size using the default configuration. +// +// Node +// ~ ~ +// | | +// | | +// +------------+ +// | *------+--------> data block 0 +// +------------+ +// | *------+--------> data block 1 +// +------------+ +// | *------+--------> data block 2 +// +------------+ +// | *------+--------> data block 3 +// +------------+ +// | *------+--------> data block 4 +// +------------+ +// | *------+--------> data block 5 +// +------------+ +// | *------+--------> data block 6 +// +------------+ +// | *------+--------> data block 7 +// +------------+ +// | *------+--------> +------------+ +// +------------+ | *------+--------> data block 8 +// | *------+----+ +------------+ +// +------------+ | | | +// | ~ ~ +// | | | +// | +------------+ +// | | *------+--------> data block 71 +// | +------------+ +// | +// +---->+------------+ +------------+ +// | *------+-------->| *------+---->data block 72 +// +------------+ +------------+ +// | | | | +// ~ ~ ~ ~ +// | | | | +// +------------+ +------------+ +// | *------+---+ | *------+----> data block 135 +// +------------+ | +------------+ +// | +// | +------------+ +// +---->| *------+----> data block 4104 +// +------------+ +// | | +// ~ ~ +// | | +// +------------+ +// | *------+----> data block 4167 +// +------------+ +// +// +// +// The advantages of this approach are that, first, memory usage is +// divided into discreet fixed size blocks which are easier to +// manage. When using malloc() to allocate them, they will fit into +// any free memory of at least the right size. Using the block array +// option removes the need to have a malloc() implementation at all. +// +// The disadvantages of this mechanism are that, first, when using +// malloc() to allocate blocks, the per-block memory allocator +// overhead is paid for each block, rather than per file. This may +// result in less memory overall being available for data +// storage. When using the block array, it is permanently reserved for +// use by the ram filesystem, and is not available for use by other +// components. +// +//========================================================================== + +#include <pkgconf/system.h> +#include <pkgconf/hal.h> +#include <pkgconf/kernel.h> +#include <pkgconf/io_fileio.h> +#include <pkgconf/fs_ram.h> + +#include <cyg/kernel/ktypes.h> // base kernel types +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros + +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <dirent.h> + +#include <stdlib.h> +#include <string.h> + +#include <cyg/fileio/fileio.h> + +#include <cyg/kernel/kapi.h> +#include <cyg/infra/diag.h> + +//========================================================================== +// Sizes derived from configuration + +// ------------------------------------------------------------------------- +// Simple malloc based allocator parameters + +#ifdef CYGPKG_FS_RAM_SIMPLE + +#define RAMFS_FILESIZE_MAX UINT_MAX + +#else + +// ------------------------------------------------------------------------- +// Block allocator parameters + +// The number of nodes per block +#define RAMFS_NODES_PER_BLOCK (CYGNUM_RAMFS_BLOCK_SIZE/sizeof(ramfs_node)) + +// The number of indirect pointers that can be stored in a single data block +#define RAMFS_INDIRECT_PER_BLOCK (CYGNUM_RAMFS_BLOCK_SIZE/sizeof(ramfs_block *)) + +// The number of directory entries that can be stored in a single data block +#define RAMFS_DIRENT_PER_BLOCK (CYGNUM_RAMFS_BLOCK_SIZE/sizeof(ramfs_dirent)) + +// Number of bytes contained in a one level indirect block +#define RAMFS_INDIRECT1_BLOCK_EXTENT (RAMFS_INDIRECT_PER_BLOCK* \ + CYGNUM_RAMFS_BLOCK_SIZE) + +// number of bytes contained in a two level indirect block +#define RAMFS_INDIRECT2_BLOCK_EXTENT (RAMFS_INDIRECT_PER_BLOCK* \ + RAMFS_INDIRECT_PER_BLOCK* \ + CYGNUM_RAMFS_BLOCK_SIZE) + +// The maximum data offset for data directly accessed from the node +#define RAMFS_DIRECT_MAX (CYGNUM_RAMFS_BLOCKS_DIRECT*CYGNUM_RAMFS_BLOCK_SIZE) + +// The maximum data offset for data accessed from the single level indirect blocks +#define RAMFS_INDIRECT1_MAX (RAMFS_DIRECT_MAX+ \ + (CYGNUM_RAMFS_BLOCKS_INDIRECT1* \ + RAMFS_INDIRECT1_BLOCK_EXTENT)) + +// The maximum data offset for data accessed from the two level indirect blocks +#define RAMFS_INDIRECT2_MAX (RAMFS_INDIRECT1_MAX+ \ + (CYGNUM_RAMFS_BLOCKS_INDIRECT2* \ + RAMFS_INDIRECT2_BLOCK_EXTENT)) + +// The maximum size of a file +#define RAMFS_FILESIZE_MAX RAMFS_INDIRECT2_MAX + +#endif + +//========================================================================== +// Forward definitions + +// Filesystem operations +static int ramfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ); +static int ramfs_umount ( cyg_mtab_entry *mte ); +static int ramfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int mode, cyg_file *fte ); +static int ramfs_unlink ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ); +static int ramfs_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ); +static int ramfs_rmdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ); +static int ramfs_rename ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2 ); +static int ramfs_link ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2, int type ); +static int ramfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_file *fte ); +static int ramfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_dir *dir_out ); +static int ramfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + struct stat *buf); +static int ramfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); +static int ramfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); + +// File operations +static int ramfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int ramfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int ramfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); +static int ramfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data); +static int ramfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ); +static int ramfs_fo_close (struct CYG_FILE_TAG *fp); +static int ramfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ); +static int ramfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); +static int ramfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); + +// Directory operations +static int ramfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int ramfs_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); + + +//========================================================================== +// Filesystem table entries + +// ------------------------------------------------------------------------- +// Fstab entry. +// This defines the entry in the filesystem table. +// For simplicity we use _FILESYSTEM synchronization for all accesses since +// we should never block in any filesystem operations. + +FSTAB_ENTRY( ramfs_fste, "ramfs", 0, + CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM, + ramfs_mount, + ramfs_umount, + ramfs_open, + ramfs_unlink, + ramfs_mkdir, + ramfs_rmdir, + ramfs_rename, + ramfs_link, + ramfs_opendir, + ramfs_chdir, + ramfs_stat, + ramfs_getinfo, + ramfs_setinfo); + +// ------------------------------------------------------------------------- +// File operations. +// This set of file operations are used for normal open files. + +static cyg_fileops ramfs_fileops = +{ + ramfs_fo_read, + ramfs_fo_write, + ramfs_fo_lseek, + ramfs_fo_ioctl, + cyg_fileio_seltrue, + ramfs_fo_fsync, + ramfs_fo_close, + ramfs_fo_fstat, + ramfs_fo_getinfo, + ramfs_fo_setinfo +}; + +// ------------------------------------------------------------------------- +// Directory file operations. +// This set of operations are used for open directories. Most entries +// point to error-returning stub functions. Only the read, lseek and +// close entries are functional. + +static cyg_fileops ramfs_dirops = +{ + ramfs_fo_dirread, + (cyg_fileop_write *)cyg_fileio_enosys, + ramfs_fo_dirlseek, + (cyg_fileop_ioctl *)cyg_fileio_enosys, + cyg_fileio_seltrue, + (cyg_fileop_fsync *)cyg_fileio_enosys, + ramfs_fo_close, + (cyg_fileop_fstat *)cyg_fileio_enosys, + (cyg_fileop_getinfo *)cyg_fileio_enosys, + (cyg_fileop_setinfo *)cyg_fileio_enosys +}; + +//========================================================================== +// Data typedefs +// Some forward typedefs for the main data structures. + +struct ramfs_node; +typedef struct ramfs_node ramfs_node; + +struct ramfs_dirent; +typedef struct ramfs_dirent ramfs_dirent; + +#ifndef CYGPKG_FS_RAM_SIMPLE + +typedef cyg_uint8 ramfs_block[CYGNUM_RAMFS_BLOCK_SIZE]; + +#endif + +//========================================================================== +// File and directory node +// This data structure represents a file or directory. + +struct ramfs_node +{ + mode_t mode; // node type + cyg_ucount32 refcnt; // open file/current dir references + nlink_t nlink; // number of links to this node + size_t size; // size of file in bytes + time_t atime; // last access time + time_t mtime; // last modified time + time_t ctime; // last changed status time + +#ifdef CYGPKG_FS_RAM_SIMPLE + + // The data storage in this case consists of a single + // malloced memory block, together with its size. + + size_t datasize; // size of data block + cyg_uint8 *data; // malloced data buffer + +#else + + // The data storage in this case consists of arrays of pointers + // to data blocks. + +#if CYGNUM_RAMFS_BLOCKS_DIRECT > 0 + // Directly accessible blocks from the inode. + ramfs_block *direct[CYGNUM_RAMFS_BLOCKS_DIRECT]; +#endif +#if CYGNUM_RAMFS_BLOCKS_INDIRECT1 > 0 + // Single level indirection + ramfs_block **indirect1[CYGNUM_RAMFS_BLOCKS_INDIRECT1]; +#endif +#if CYGNUM_RAMFS_BLOCKS_INDIRECT2 > 0 + // Two level indirection + ramfs_block ***indirect2[CYGNUM_RAMFS_BLOCKS_INDIRECT2]; +#endif + +#endif + +}; + +//========================================================================== +// Directory entry. +// Fixed sized entry containing a fragment of the name of a file/directory. + +struct ramfs_dirent +{ + ramfs_node *node; // pointer to node + unsigned int inuse:1, // entry in use? + first:1, // first directory entry fragment? + last:1, // last directory entry fragment? + namelen:8, // bytes in whole name + fraglen:8; // bytes in name fragment + off_t next; // offset of next dirent + + // Name fragment, fills rest of entry. + char name[CYGNUM_RAMFS_DIRENT_SIZE- + sizeof(ramfs_node *)- + sizeof( cyg_uint32)- + sizeof(off_t)]; +}; + +//========================================================================== +// Directory search data +// Parameters for a directory search. The fields of this structure are +// updated as we follow a pathname through the directory tree. + +struct ramfs_dirsearch +{ + ramfs_node *dir; // directory to search + const char *path; // path to follow + ramfs_node *node; // Node found + const char *name; // last name fragment used + int namelen; // name fragment length + cyg_bool last; // last name in path? +}; + +typedef struct ramfs_dirsearch ramfs_dirsearch; + +//========================================================================== +// Forward defs + +static int del_direntry( ramfs_node *dir, const char *name, int namelen ); + + +//========================================================================== +// Block array +// This is used for block allocation when malloc is not being used. + +#ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY + +# ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY_EXTERN + +// Array is defined externally with a user-supplied name + +__externC ramfs_block CYGPKG_FS_RAM_BLOCKS_ARRAY_NAME[CYGNUM_FS_RAM_BLOCKS_ARRAY_SIZE]; + +// Translate into a usable common name +#define ramfs_block_array CYGPKG_FS_RAM_BLOCKS_ARRAY_NAME + +# else + +// Array is defined here + +static ramfs_block cyg_ramfs_block_array[CYGNUM_FS_RAM_BLOCKS_ARRAY_SIZE]; + +#define ramfs_block_array cyg_ramfs_block_array + +# endif + +// Pointer to list of free blocks +static ramfs_block *block_free_list = NULL; + +#endif + +//========================================================================== +// Block allocation + +#ifndef CYGPKG_FS_RAM_SIMPLE + +// ------------------------------------------------------------------------- +// block_init() +// Initialize the block allocator by chaining them all together on +// block_free_list. + +#ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY + +static void block_init(void) +{ + static cyg_bool initialized = false; + int i; + + if( !initialized ) + { + for( i = 0; i < CYGNUM_FS_RAM_BLOCKS_ARRAY_SIZE; i++ ) + { + ramfs_block *b = &ramfs_block_array[i]; + *(ramfs_block **)b = block_free_list; + block_free_list = b; + } + initialized = true; + } +} + +#endif + +// ------------------------------------------------------------------------- +// block_alloc() +// Allocate a block for data storage. +// If we have a block array, just pick the first off the free list. +// If we are mallocing, call malloc() to get it. + +static ramfs_block *block_alloc(void) +{ + ramfs_block *b; + +#ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY + + block_init(); // Check blocks are initialized + + // pick first block off free list. + b = block_free_list; + + // and advance list + if( b != NULL ) + block_free_list = *(ramfs_block **)b; + +#else + + b = malloc(CYGNUM_RAMFS_BLOCK_SIZE); + +#endif + + // Clear the block to zero if it was allocated + if( b != NULL ) + memset( b, 0, CYGNUM_RAMFS_BLOCK_SIZE ); + + return b; + +} + +// ------------------------------------------------------------------------- +// block_free() +// Free a block. Depending on the configuration send it back to the +// heap or put it back on free list. + +static void block_free( ramfs_block *b ) +{ +#ifdef CYGPKG_FS_RAM_BLOCKS_ARRAY + + // Put the block back on the free list + + *(ramfs_block **)b = block_free_list; + block_free_list = b; + +#else + + // Call free() to return it to the memory pool + + free( b ); + +#endif +} + +#endif + +//========================================================================== +// Node buffer management +// There are two versions of this, one for the _SIMPLE variant and one for +// the _BLOCKS variant. In both cases the interface to this code is via the +// findbuffer_node() and freebuffer_node() functions. + +#ifdef CYGPKG_FS_RAM_SIMPLE + +//========================================================================== +// SIMPLE buffer management. +// Each node has a data buffer pointer and a size. This buffer is +// realloc()ed as needed. + +// ------------------------------------------------------------------------- +// findbuffer_node() +// return a pointer to the data at the indicated file position, extending +// the buffer if required. + +static int findbuffer_node( ramfs_node *node, // node pointer + off_t pos, // data position to get + cyg_uint8 **buffer, // returned buffer pointer + size_t *size, // returned buffer size + cyg_bool alloc) // extend allocation? +{ + if( alloc && (pos >= node->datasize || node->datasize == 0) ) + { + // If we are allowed to alloc new data, and we are at the end of the + // current data allocation, or there is no data present, allocate or + // extend the data buffer. + + cyg_uint8 *newdata; + + if( node->data == NULL ) + newdata = malloc( CYGNUM_RAMFS_REALLOC_INCREMENT ); + else + newdata = realloc( node->data, pos+CYGNUM_RAMFS_REALLOC_INCREMENT ); + + if( newdata == NULL ) return ENOSPC; + else memset( newdata + node->datasize, 0, + pos + CYGNUM_RAMFS_REALLOC_INCREMENT - node->datasize ); + + node->data = newdata; + node->datasize = pos+CYGNUM_RAMFS_REALLOC_INCREMENT; + } + else if( pos > node->datasize ) + { + // Indicate end of data. + *size = 0; + return ENOERR; + } + + *buffer = node->data+pos; + *size = node->datasize-pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// freebuffer_node() +// Empty out the data storage from the node. + +static int freebuffer_node( ramfs_node *node ) +{ + if( node->data != NULL ) + { + free( node->data ); + } + + node->data = NULL; + node->datasize = 0; + + return ENOERR; +} + +//========================================================================== + +#else + +//========================================================================== +// _BLOCKS storage management. +// Data storage in the node is by means of a set of arrays of pointers to +// blocks. The first array points directly to the data blocks. Subsequent +// arrays point to single and double indirect blocks respectively. + +// ------------------------------------------------------------------------- +// findbuffer_direct() +// Indexes into an array of block pointers and extracts a pointer to the +// data at offset _pos_, allocating new blocks if required. + +static int findbuffer_direct( off_t pos, + ramfs_block **blocks, + int nblocks, + cyg_uint8 **buffer, + size_t *size, + cyg_bool alloc) +{ + int bi = pos / CYGNUM_RAMFS_BLOCK_SIZE; + int bpos = pos % CYGNUM_RAMFS_BLOCK_SIZE; + ramfs_block *b; + + *buffer = NULL; + *size = CYGNUM_RAMFS_BLOCK_SIZE - bpos; + + if( bi >= nblocks ) + return ENOERR; + + b = blocks[bi]; + + if( b == NULL ) + { + // There is no block there. If _alloc_ is true we can fill the + // slot in with a new block. If it is false, we indicate there + // is no block and size indicates where the block would end if + // it existed. + if( alloc ) + { + b = block_alloc(); + if( b == NULL ) + return ENOSPC; + blocks[bi] = b; + } + else return ENOERR; + } + + *buffer = &((*b)[bpos]); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// findbuffer_indirect1() +// Indexes into an array of pointers to blocks containing pointers to +// blocks and extracts a pointer to the data at offset _pos_, +// allocating new blocks if required. + +#if CYGNUM_RAMFS_BLOCKS_INDIRECT1 > 0 + +static int findbuffer_indirect1( off_t pos, + ramfs_block ***blocks, + int nblocks, + cyg_uint8 **buffer, + size_t *size, + cyg_bool alloc) +{ + + int bi = pos / RAMFS_INDIRECT1_BLOCK_EXTENT; + int bpos = pos % RAMFS_INDIRECT1_BLOCK_EXTENT; + int err; + cyg_uint8 *b; + size_t sz; + + // Use findbuffer_direct() to index and allocate + // the first level indirect block. + + err = findbuffer_direct( bi*CYGNUM_RAMFS_BLOCK_SIZE, + (ramfs_block **)blocks, + nblocks, + &b, + &sz, + alloc); + + if( err != ENOERR ) + return err; + + if( sz == 0 ) + { + *size = 0; + return ENOERR; + } + + // Use findbuffer_direct() on the first level indirect + // block to allocate and return the data pointer. + + return findbuffer_direct( bpos, + blocks[bi], + RAMFS_INDIRECT_PER_BLOCK, + buffer, + size, + alloc); +} + +#endif + +// ------------------------------------------------------------------------- +// findbuffer_indirect1() +// Indexes into an array of pointers to blocks containing pointers to +// blocks containing pointers to blocks (!) and extracts a pointer to +// the data at offset _pos_, allocating new blocks if required. + +#if CYGNUM_RAMFS_BLOCKS_INDIRECT2 > 0 + +static int findbuffer_indirect2( off_t pos, + ramfs_block ****blocks, + int nblocks, + cyg_uint8 **buffer, + size_t *size, + cyg_bool alloc) +{ + int bi = pos / RAMFS_INDIRECT2_BLOCK_EXTENT; + int bpos = pos % RAMFS_INDIRECT2_BLOCK_EXTENT; + int err; + cyg_uint8 *b; + size_t sz; + + // Use findbuffer_direct() to index and allocate + // the first level indirect block. + + err = findbuffer_direct( bi*CYGNUM_RAMFS_BLOCK_SIZE, + (ramfs_block **)blocks, + nblocks, + &b, + &sz, + alloc); + + if( err != ENOERR ) + return err; + + if( sz == 0 ) + { + *size = 0; + return ENOERR; + } + + // Use findbuffer_indirect1() on the first level indirect block to + // index and allocate the next level indirect block and the data + // block. + + return findbuffer_indirect1( bpos, + blocks[bi], + RAMFS_INDIRECT_PER_BLOCK, + buffer, + size, + alloc); +} + +#endif + +// ------------------------------------------------------------------------- +// findbuffer_node() +// Depending on the offset and configuration, call the appropriate +// function to get the buffer pointer. + +static int findbuffer_node( ramfs_node *node, + off_t pos, + cyg_uint8 **buffer, + size_t *size, + cyg_bool alloc) +{ +#if CYGNUM_RAMFS_BLOCKS_DIRECT > 0 + if( pos < RAMFS_DIRECT_MAX ) + return findbuffer_direct( pos, + node->direct, + CYGNUM_RAMFS_BLOCKS_DIRECT, + buffer, + size, + alloc); +#endif +#if CYGNUM_RAMFS_BLOCKS_INDIRECT1 > 0 + if( pos < RAMFS_INDIRECT1_MAX ) + return findbuffer_indirect1( pos - RAMFS_DIRECT_MAX, + node->indirect1, + CYGNUM_RAMFS_BLOCKS_INDIRECT1, + buffer, + size, + alloc); +#endif +#if CYGNUM_RAMFS_BLOCKS_INDIRECT2 > 0 + if( pos < RAMFS_INDIRECT2_MAX ) + return findbuffer_indirect2( pos - RAMFS_INDIRECT1_MAX, + node->indirect2, + CYGNUM_RAMFS_BLOCKS_INDIRECT2, + buffer, + size, + alloc); +#endif + + return ENOSPC; +} + +// ------------------------------------------------------------------------- +// freeblock_list() +// Free a list of data blocks. + +static void freeblock_list( ramfs_block *blocks[],int nblocks ) +{ + int i; + for( i = 0; i < nblocks ; i++ ) + { + if( blocks[i] != NULL ) + { + block_free( blocks[i] ); + blocks[i] = NULL; + } + } +} + +// ------------------------------------------------------------------------- +// freebuffer_node() +// Free all the data blocks in the node and clear the pointers. + +static int freebuffer_node( ramfs_node *node ) +{ +#if CYGNUM_RAMFS_BLOCKS_DIRECT > 0 + freeblock_list( node->direct, CYGNUM_RAMFS_BLOCKS_DIRECT ); +#endif + +#if CYGNUM_RAMFS_BLOCKS_INDIRECT1 > 0 + { + int i; + for( i = 0; i < CYGNUM_RAMFS_BLOCKS_INDIRECT1 ; i++ ) + { + if( node->indirect1[i] != NULL ) + { + freeblock_list( (ramfs_block **)node->indirect1[i], RAMFS_INDIRECT_PER_BLOCK ); + block_free( (ramfs_block *)node->indirect1[i] ); + node->indirect1[i] = NULL; + } + } + } +#endif + +#if CYGNUM_RAMFS_BLOCKS_INDIRECT2 > 0 + { + int i; + for( i = 0; i < CYGNUM_RAMFS_BLOCKS_INDIRECT2 ; i++ ) + { + if( node->indirect2[i] != NULL ) + { + ramfs_block ***b = node->indirect2[i]; + int j; + for( j = 0; j < RAMFS_INDIRECT_PER_BLOCK ; j++ ) + { + if( b[j] != NULL ) + { + freeblock_list( (ramfs_block **)b[j], RAMFS_INDIRECT_PER_BLOCK ); + block_free( (ramfs_block *)b[j] ); + b[j] = NULL; + } + } + block_free( (ramfs_block *)node->indirect2[i] ); + node->indirect2[i] = NULL; + } + } + } +#endif + + return ENOERR; +} + +//========================================================================== + +#endif + +//========================================================================== +// Node allocation + +// ------------------------------------------------------------------------- +// alloc_node() +// Allocate a node and initialize it. +// For the _SIMPLE allocation option, we just malloc it. For the +// _BLOCKS option we allocate a block and use that. In theory we could +// pack several nodes into a single block, but we don't at present due +// to sheer lazyness. + +static ramfs_node *alloc_node( mode_t mode ) +{ +#ifdef CYGPKG_FS_RAM_SIMPLE + ramfs_node *node = malloc( sizeof( ramfs_node ) ); + + if( node == NULL ) + return NULL; + +#else + ramfs_block *b = block_alloc(); + ramfs_node *node; + + if( b == NULL ) + return NULL; + + node = (ramfs_node *)b; + +#endif + + memset( node, 0, sizeof(ramfs_node) ); + + node->mode = mode; + node->refcnt = 0; + node->nlink = 0; + node->size = 0; + node->atime = + node->mtime = + node->ctime = cyg_timestamp(); + +#ifdef CYGPKG_FS_RAM_SIMPLE + node->datasize = 0; + node->data = NULL; +#else + + // The node is already all zero + +#endif + return node; +} + +// ------------------------------------------------------------------------- +// free_node() +// Release a node either back to the free pool or back into the block +// pool. + +static void free_node( ramfs_node *node ) +{ +#ifdef CYGPKG_FS_RAM_SIMPLE + + free( node ); + +#else + + block_free( (ramfs_block *)node ); + +#endif + +} + + +//========================================================================== +// Ref count and nlink management + +// ------------------------------------------------------------------------- +// dec_refcnt() +// Decrment the reference count on a node. If this makes the ref count +// zero, and the number of links is either zero for a file or one for +// a node, then this node is detached from the directory tree and can +// be freed. + +static int dec_refcnt( ramfs_node *node ) +{ + int err = ENOERR; + node->refcnt--; + + if( node->refcnt == 0 && + ((S_ISREG(node->mode) && node->nlink == 0 ) || + (S_ISDIR(node->mode) && node->nlink == 1) ) + ) + { + // This node it now totally detached from the directory tree, + // so delete it. + + if( S_ISDIR(node->mode) ) + { + del_direntry( node, ".", 1 ); + del_direntry( node, "..", 2 ); + } + + err = freebuffer_node( node ); + + if( err == ENOERR ) + free_node( node ); + } + + return err; +} + +// ------------------------------------------------------------------------- +// dec_nlink() +// Decrement a node's link count. Since this has to do all the same +// work as dec_refcnt() we implement this using that function by +// essentially transferring the count to refcnt and then decrement +// that. + +static int dec_nlink( ramfs_node *node ) +{ + node->refcnt++; + + node->nlink--; + + return dec_refcnt( node ); +} + +//========================================================================== +// Directory operations + +// ------------------------------------------------------------------------- +// add_direntry() +// Add an entry to a directory. This is added as a chain of entry +// fragments until the name is exhausted. + +static int add_direntry( ramfs_node *dir, // dir to add to + const char *name, // name to add + int namelen, // length of name + ramfs_node *node // node to reference + ) +{ + off_t pos = 0; +#ifdef CYGPKG_FS_RAM_SIMPLE + off_t prev_pos = 0; +#endif + ramfs_dirent *d = NULL, *dp = NULL; + cyg_bool isfirst = true; + + // Loop inserting fragments of the name into the directory until we + // have found a home for them all. + + while( namelen > 0 ) + { + int fraglen = namelen; + + if( fraglen > sizeof(d->name) ) + fraglen = sizeof(d->name); + + // Find a free fragment + for(;;) + { + cyg_uint8 *buf; + size_t size; + int err = findbuffer_node( dir, pos, &buf, &size, true ); + if( err != ENOERR ) return err; + + d = (ramfs_dirent *)buf; + + if( size < sizeof(ramfs_dirent) || d->inuse ) + { + pos += sizeof(ramfs_dirent); + continue; + } + + break; + } + +#ifdef CYGPKG_FS_RAM_SIMPLE + // Tricky! Here we have to look up the previous segment as + // reallocating could have moved it. + if( !isfirst ) { + cyg_uint8 *buf; + size_t size; + int err; + err = findbuffer_node( dir, prev_pos, &buf, &size, false ); + if( err != ENOERR ) return err; + + dp = (ramfs_dirent *) buf; + } +#endif + + // d now points to a free dirent structure + + d->node = node; + d->inuse = 1; + d->first = isfirst; + d->namelen = namelen; + d->fraglen = fraglen; + if( dp ) dp->next = pos; + + memcpy( d->name, name, fraglen ); + + name += fraglen; + namelen -= fraglen; +#ifdef CYGPKG_FS_RAM_SIMPLE + prev_pos = pos; +#endif + pos += sizeof(ramfs_dirent); + dp = d; + isfirst = false; + + } + + + d->last = 1; // Mark last fragment + + // Update directory times + dir->mtime = + dir->ctime = cyg_timestamp(); + + // Extend dir size if necessary + if( pos > dir->size ) + dir->size = pos; + + // Count the new link + node->nlink++; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// find_direntry() +// Find a directory entry for the name and return a pointer to the first +// entry fragment. + +static ramfs_dirent *find_direntry( ramfs_node *dir, const char *name, int namelen ) +{ + ramfs_dirent *first = NULL; + off_t pos = 0; + int err; + + // Loop over all the entries until a match is found or we run out + // of data. + while( pos < dir->size ) + { + const char *frag = name; + ramfs_dirent *d; + cyg_uint8 *buf; + size_t size; + + // look for a first name fragment + for(;;) + { + err = findbuffer_node( dir, pos, &buf, &size, false ); + if( err != ENOERR || size == 0) + return NULL; + + d = (ramfs_dirent *)buf; + + if( size < sizeof(ramfs_dirent) || !d->inuse || !d->first ) + { + pos += sizeof(ramfs_dirent); + if ( pos < dir->size ) + continue; + // End if directory, didn't find it. + return NULL; + } + + break; + } + + // Here we have got a first fragment of a name, check it + // against the name we are looking for. First check that they + // are the same length. + + if( d->namelen == namelen ) + { + // We have a potential candidate here... + + first = d; // Save it for later + + // Now check that all the name fragments match + for(;;) + { + int fraglen = namelen-(frag-name); + + if( fraglen > d->fraglen ) + fraglen = d->fraglen; + + // compare strings, if different, look for another + if( memcmp( frag, d->name, fraglen ) != 0 ) { + break; + } + frag += fraglen; + + // If we are at the last fragment, then the whole name string + // has matched and we have a successful search. + + if( d->last ) + return first; + + // Otherwise move on to next entry in chain + err = findbuffer_node( dir, d->next, &buf, &size, false ); + if( err != ENOERR ) + return NULL; + + d = (ramfs_dirent *)buf; + + } + } + + pos += sizeof(ramfs_dirent); + } + + return NULL; +} + +// ------------------------------------------------------------------------- +// del_direntry() +// Delete a named directory entry. Find it and then follow the chain +// deleting the fragments as we go. + +static int del_direntry( ramfs_node *dir, const char *name, int namelen ) +{ + ramfs_dirent *d = find_direntry( dir, name, namelen ); + + if( d == NULL ) + return ENOENT; + + for(;;) + { + int err; + cyg_uint8 *buf; + size_t size; + + d->inuse = 0; + if( d->last ) break; + + err = findbuffer_node( dir, d->next, &buf, &size, false ); + if( err != ENOERR ) + return ENOENT; + + d = (ramfs_dirent *)buf; + } + + dec_nlink( d->node ); + + return ENOERR; +} + +//========================================================================== +// Directory search + +// ------------------------------------------------------------------------- +// init_dirsearch() +// Initialize a dirsearch object to start a search + +static void init_dirsearch( ramfs_dirsearch *ds, + ramfs_node *dir, + const char *name) +{ + ds->dir = dir; + ds->path = name; + ds->node = dir; + ds->name = name; + ds->namelen = 0; + ds->last = false; +} + +// ------------------------------------------------------------------------- +// find_entry() +// Search a single directory for the next name in a path and update the +// dirsearch object appropriately. + +static int find_entry( ramfs_dirsearch *ds ) +{ + ramfs_node *dir = ds->dir; + const char *name = ds->path; + const char *n = name; + int namelen = 0; + ramfs_dirent *d; + + // check that we really have a directory + if( !S_ISDIR(dir->mode) ) + return ENOTDIR; + + // Isolate the next element of the path name. + while( *n != '\0' && *n != '/' ) + n++, namelen++; + + // Check if this is the last path element. + while( *n == '/') n++; + if( *n == '\0' ) + ds->last = true; + + // update name in dirsearch object + ds->name = name; + ds->namelen = namelen; + + // Here we have the name and its length set up. + // Search the directory for a matching entry + + d = find_direntry( dir, name, namelen ); + + if( d == NULL ) + return ENOENT; + + // pass back the node we have found + ds->node = d->node; + + return ENOERR; + +} + +// ------------------------------------------------------------------------- +// ramfs_find() +// Main interface to directory search code. This is used in all file +// level operations to locate the object named by the pathname. + +static int ramfs_find( ramfs_dirsearch *d ) +{ + int err; + + // Short circuit empty paths + if( *(d->path) == '\0' ) + return ENOERR; + + // iterate down directory tree until we find the object + // we want. + for(;;) + { + err = find_entry( d ); + + if( err != ENOERR ) + return err; + + if( d->last ) + return ENOERR; + + // Update dirsearch object to search next directory. + d->dir = d->node; + d->path += d->namelen; + while( *(d->path) == '/' ) d->path++; // skip dirname separators + } +} + +//========================================================================== +// Pathconf support +// This function provides support for pathconf() and fpathconf(). + +static int ramfs_pathconf( ramfs_node *node, struct cyg_pathconf_info *info ) +{ + int err = ENOERR; + + switch( info->name ) + { + case _PC_LINK_MAX: + info->value = LINK_MAX; + break; + + case _PC_MAX_CANON: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_MAX_INPUT: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_NAME_MAX: + info->value = NAME_MAX; + break; + + case _PC_PATH_MAX: + info->value = PATH_MAX; + break; + + case _PC_PIPE_BUF: + info->value = -1; // not supported + err = EINVAL; + break; + + + case _PC_ASYNC_IO: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_CHOWN_RESTRICTED: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_NO_TRUNC: + info->value = 0; + break; + + case _PC_PRIO_IO: + info->value = 0; + break; + + case _PC_SYNC_IO: + info->value = 0; + break; + + case _PC_VDISABLE: + info->value = -1; // not supported + err = EINVAL; + break; + + default: + err = EINVAL; + break; + } + + return err; +} + +//========================================================================== +// Filesystem operations + +// ------------------------------------------------------------------------- +// ramfs_mount() +// Process a mount request. This mainly creates a root for the +// filesystem. + +static int ramfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ) +{ + ramfs_node *root; + int err; + + // Allocate a node to be the root of this filesystem and initialize it. + + root = alloc_node(__stat_mode_DIR|S_IRWXU|S_IRWXG|S_IRWXO); + + if( root == NULL ) + return ENOSPC; + + // Add . and .. entries back to self. + + err = add_direntry( root, ".", 1, root ); + if( err == ENOERR ) + err = add_direntry( root, "..", 2, root ); + + if( err != ENOERR ) + { + free_node( root ); + return err; + } + + mte->root = (cyg_dir)root; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// ramfs_umount() +// Unmount the filesystem. This will currently only succeed if the +// filesystem is empty. + +static int ramfs_umount ( cyg_mtab_entry *mte ) +{ + ramfs_node *root = (ramfs_node *)mte->root; + + // Check for open/inuse root + if( root->refcnt != 0 ) + return EBUSY; + + // Check that root directory is clear of extra links. + if( root->nlink != 2 ) + return EBUSY; + + // Just return it to free pool + free_node( root ); + + // Clear root pointer + mte->root = CYG_DIR_NULL; + + // That's all folks. + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// ramfs_open() +// Open a file for reading or writing. + +static int ramfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int mode, cyg_file *file ) +{ + + ramfs_dirsearch ds; + ramfs_node *node = NULL; + int err; + + init_dirsearch( &ds, (ramfs_node *)dir, name ); + + err = ramfs_find( &ds ); + + if( err == ENOENT ) + { + if( ds.last && (mode & O_CREAT) ) + { + // No node there, if the O_CREAT bit is set then we must + // create a new one. The dir and name fields of the dirsearch + // object will have been updated so we know where to put it. + + node = alloc_node( __stat_mode_REG|S_IRWXU|S_IRWXG|S_IRWXO); + + if( node == NULL ) + return ENOSPC; + + err = add_direntry( ds.dir, ds.name, ds.namelen, node ); + + if( err != ENOERR ) + { + free_node( node ); + return err; + } + + err = ENOERR; + } + } + else if( err == ENOERR ) + { + // The node exists. If the O_CREAT and O_EXCL bits are set, we + // must fail the open. + + if( (mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) ) + err = EEXIST; + else node = ds.node; + } + + if( err == ENOERR && (mode & O_TRUNC ) ) + { + // If the O_TRUNC bit is set we must clean out the file data. + + err = freebuffer_node( node ); + node->size = 0; + + // Update file times + node->ctime = + node->mtime = cyg_timestamp(); + } + + if( err != ENOERR ) return err; + + // Check that we actually have a file here + if( S_ISDIR(node->mode) ) return EISDIR; + + node->refcnt++; // Count successful open + + // Initialize the file object + + file->f_flag |= mode & CYG_FILE_MODE_MASK; + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &ramfs_fileops; + file->f_offset = (mode&O_APPEND) ? node->size : 0; + file->f_data = (CYG_ADDRWORD)node; + file->f_xops = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// ramfs_unlink() +// Remove a file link from its directory. + +static int ramfs_unlink ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ) +{ + ramfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (ramfs_node *)dir, name ); + + err = ramfs_find( &ds ); + + if( err != ENOERR ) return err; + + // Cannot unlink directories, use rmdir() instead + if( S_ISDIR(ds.node->mode) ) + return EPERM; + + // Delete it from its directory + err = del_direntry( ds.dir, ds.name, ds.namelen ); + + return err; +} + +// ------------------------------------------------------------------------- +// ramfs_mkdir() +// Create a new directory. + +static int ramfs_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ) +{ + ramfs_dirsearch ds; + ramfs_node *node = NULL; + int err; + + init_dirsearch( &ds, (ramfs_node *)dir, name ); + + err = ramfs_find( &ds ); + + if( err == ENOENT ) + { + if( ds.last ) + { + // The entry does not exist, and it is the last element in + // the pathname, so we can create it here. + int doterr, dotdoterr, direrr; + + node = alloc_node( __stat_mode_DIR | S_IRWXU|S_IRWXG|S_IRWXO); + + if( node == NULL ) + return ENOSPC; + + // Add "." and ".." entries. + doterr = add_direntry( node, ".", 1, node ); + dotdoterr = add_direntry( node, "..", 2, ds.dir ); + + // And add to parent directory. + direrr = add_direntry( ds.dir, ds.name, ds.namelen, node ); + + // check for any errors in that... + if( doterr+dotdoterr+direrr != ENOERR ) + { + // For each of the add_direntry() calls that succeeded, + // we must now undo it. + + if( doterr == ENOERR ) + del_direntry( node, ".", 1 ); + else err = doterr; + + if( dotdoterr == ENOERR ) + del_direntry( node, "..", 2 ); + else err = dotdoterr; + + if( direrr == ENOERR ) + del_direntry( ds.dir, ds.name, ds.namelen ); + else err = direrr; + + // Free the data and the node itself. + freebuffer_node( node ); + free_node( node ); + } + else err = ENOERR; + } + // If this was not the last element, then and intermediate + // directory does not exist. + } + else + { + // If there we no error, something already exists with that + // name, so we cannot create another one. + + if( err == ENOERR ) + err = EEXIST; + } + + return err; +} + +// ------------------------------------------------------------------------- +// ramfs_rmdir() +// Remove a directory. + +static int ramfs_rmdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ) +{ + ramfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (ramfs_node *)dir, name ); + + err = ramfs_find( &ds ); + + if( err != ENOERR ) return err; + + // Check that this is actually a directory. + if( !S_ISDIR(ds.node->mode) ) + return EPERM; + + // Delete the entry. This will adjust the link values + // accordingly and if the directory is now unreferenced, + // will cause it to be deleted. + + err = del_direntry( ds.dir, ds.name, ds.namelen ); + + return err; +} + +// ------------------------------------------------------------------------- +// ramfs_rename() +// Rename a file/dir. + +static int ramfs_rename ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2 ) +{ + ramfs_dirsearch ds1, ds2; + int err; + + init_dirsearch( &ds1, (ramfs_node *)dir1, name1 ); + + err = ramfs_find( &ds1 ); + + if( err != ENOERR ) return err; + + init_dirsearch( &ds2, (ramfs_node *)dir2, name2 ); + + err = ramfs_find( &ds2 ); + + // Allow through renames to non-existent objects. + if( ds2.last && err == ENOENT ) + ds2.node = NULL, err = ENOERR; + + if( err != ENOERR ) return err; + + // Null rename, just return + if( ds1.node == ds2.node ) + return ENOERR; + + // First deal with any entry that is at the destination + if( ds2.node ) + { + // Check that we are renaming like-for-like + + if( !S_ISDIR(ds1.node->mode) && S_ISDIR(ds2.node->mode) ) + return EISDIR; + + if( S_ISDIR(ds1.node->mode) && !S_ISDIR(ds2.node->mode) ) + return ENOTDIR; + + // Now delete the destination directory entry + + err = del_direntry( ds2.dir, ds2.name, ds2.namelen ); + + if( err != ENOERR ) return err; + + } + + // Now we know that there is no clashing node at the destination, + // make a new direntry at the destination and delete the old entry + // at the source. + + err = add_direntry( ds2.dir, ds2.name, ds2.namelen, ds1.node ); + + if( err == ENOERR ) + err = del_direntry( ds1.dir, ds1.name, ds1.namelen ); + + // Update directory times + if( err == ENOERR ) + ds1.dir->ctime = + ds1.dir->mtime = + ds2.dir->ctime = + ds2.dir->mtime = cyg_timestamp(); + + return err; +} + +// ------------------------------------------------------------------------- +// ramfs_link() +// Make a new directory entry for a file. + +static int ramfs_link ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2, int type ) +{ + ramfs_dirsearch ds1, ds2; + int err; + + // Only do hard links for now in this filesystem + if( type != CYG_FSLINK_HARD ) + return EINVAL; + + init_dirsearch( &ds1, (ramfs_node *)dir1, name1 ); + + err = ramfs_find( &ds1 ); + + if( err != ENOERR ) return err; + + init_dirsearch( &ds2, (ramfs_node *)dir2, name2 ); + + err = ramfs_find( &ds2 ); + + // Don't allow links to existing objects + if( err == ENOERR ) return EEXIST; + + // Allow through links to non-existing terminal objects + if( ds2.last && err == ENOENT ) + ds2.node = NULL, err = ENOERR; + + if( err != ENOERR ) return err; + + // Now we know that there is no existing node at the destination, + // make a new direntry at the destination. + + err = add_direntry( ds2.dir, ds2.name, ds2.namelen, ds1.node ); + + if( err == ENOERR ) + ds1.node->ctime = + ds2.dir->ctime = + ds2.dir->mtime = cyg_timestamp(); + + return err; +} + +// ------------------------------------------------------------------------- +// ramfs_opendir() +// Open a directory for reading. + +static int ramfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_file *file ) +{ + ramfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (ramfs_node *)dir, name ); + + err = ramfs_find( &ds ); + + if( err != ENOERR ) return err; + + // check it is really a directory. + if( !S_ISDIR(ds.node->mode) ) return ENOTDIR; + + ds.node->refcnt++; // Count successful open + + // Initialize the file object, setting the f_ops field to a + // special set of file ops. + + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &ramfs_dirops; + file->f_offset = 0; + file->f_data = (CYG_ADDRWORD)ds.node; + file->f_xops = 0; + + return ENOERR; + +} + +// ------------------------------------------------------------------------- +// ramfs_chdir() +// Change directory support. + +static int ramfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_dir *dir_out ) +{ + if( dir_out != NULL ) + { + // This is a request to get a new directory pointer in + // *dir_out. + + ramfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (ramfs_node *)dir, name ); + + err = ramfs_find( &ds ); + + if( err != ENOERR ) return err; + + // check it is a directory + if( !S_ISDIR(ds.node->mode) ) + return ENOTDIR; + + // Increment ref count to keep this directory in existent + // while it is the current cdir. + ds.node->refcnt++; + + // Pass it out + *dir_out = (cyg_dir)ds.node; + } + else + { + // If no output dir is required, this means that the mte and + // dir arguments are the current cdir setting and we should + // forget this fact. + + ramfs_node *node = (ramfs_node *)dir; + + // Just decrement directory reference count. + dec_refcnt( node ); + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// ramfs_stat() +// Get struct stat info for named object. + +static int ramfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + struct stat *buf) +{ + ramfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (ramfs_node *)dir, name ); + + err = ramfs_find( &ds ); + + if( err != ENOERR ) return err; + + // Fill in the status + buf->st_mode = ds.node->mode; + buf->st_ino = (ino_t)ds.node; + buf->st_dev = 0; + buf->st_nlink = ds.node->nlink; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = ds.node->size; + buf->st_atime = ds.node->atime; + buf->st_mtime = ds.node->mtime; + buf->st_ctime = ds.node->ctime; + + return err; +} + +// ------------------------------------------------------------------------- +// ramfs_getinfo() +// Getinfo. Currently only support pathconf() and filesystem usage. + +static int ramfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ) +{ + ramfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (ramfs_node *)dir, name ); + + err = ramfs_find( &ds ); + + if( err != ENOERR ) return err; + + switch( key ) + { + case FS_INFO_CONF: + err = ramfs_pathconf( ds.node, (struct cyg_pathconf_info *)buf ); + break; +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) && defined(CYGPKG_FS_RAM_BLOCKS_ARRAY) + // When using malloc for storage this does not make much + // sense, so only implement this when using pre-allocated + // blocks + case FS_INFO_BLOCK_USAGE: { + struct cyg_fs_block_usage *usage = (struct cyg_fs_block_usage *) buf; + ramfs_block *b; + + usage->total_blocks = CYGNUM_FS_RAM_BLOCKS_ARRAY_SIZE; + usage->free_blocks = 0; + // Iterate over the free list to count its size + b = block_free_list; + while(b) { + usage->free_blocks++; + b=*(ramfs_block **)b; + } + usage->block_size = CYGNUM_RAMFS_BLOCK_SIZE; + return ENOERR; + } +#endif + default: + err = EINVAL; + } + return err; +} + +// ------------------------------------------------------------------------- +// ramfs_setinfo() +// Setinfo. Nothing to support here at present. + +static int ramfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ) +{ + // No setinfo keys supported at present + + return EINVAL; +} + + +//========================================================================== +// File operations + +// ------------------------------------------------------------------------- +// ramfs_fo_read() +// Read data from the file. + +static int ramfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + ramfs_node *node = (ramfs_node *)fp->f_data; + int i; + off_t pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + + // Loop over the io vectors until there are none left + for( i = 0; i < uio->uio_iovcnt; i++ ) + { + cyg_iovec *iov = &uio->uio_iov[i]; + char *buf = (char *)iov->iov_base; + off_t len = iov->iov_len; + + // Loop over each vector filling it with data from the file. + while( len > 0 && pos < node->size ) + { + cyg_uint8 *fbuf; + size_t bsize; + off_t l = len; + int err; + + // Get a pointer to the data at offset _pos_. + err = findbuffer_node( node, pos, &fbuf, &bsize, false ); + + if( err != ENOERR ) + return err; + + // adjust size to end of file if necessary + if( l > node->size-pos ) + l = node->size-pos; + + // adjust size to the amount of contiguous data we can see + // at present. + if( l > bsize ) + l = bsize; + + if (fbuf) { + // copy data out + memcpy( buf, fbuf, l ); + } else { // hole, so return zeros here. + memset( buf, 0, l ); + } + + // Update working vars + len -= l; + buf += l; + pos += l; + resid -= l; + } + } + + // We successfully read some data, update the node's access time + // and update the file offset and transfer residue. + + node->atime = cyg_timestamp(); + + uio->uio_resid = resid; + fp->f_offset = pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// ramfs_fo_write() +// Write data to file. + +static int ramfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + ramfs_node *node = (ramfs_node *)fp->f_data; + off_t pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + int err = ENOERR; + int i; + + // If the APPEND mode bit was supplied, force all writes to + // the end of the file. + if( fp->f_flag & CYG_FAPPEND ) + pos = fp->f_offset = node->size; + + // Now loop over the iovecs until they are all done, or + // we get an error. + for( i = 0; i < uio->uio_iovcnt; i++ ) + { + cyg_iovec *iov = &uio->uio_iov[i]; + char *buf = (char *)iov->iov_base; + off_t len = iov->iov_len; + + // loop over the vector writing it to the file until it has + // all been done. + while( len > 0 ) + { + cyg_uint8 *fbuf; + size_t bsize; + off_t l = len; + + err = findbuffer_node( node, pos, &fbuf, &bsize, true ); + + // Stop writing if there is no more space in the file and + // indicate end of data. + if( err == ENOSPC ) + break; + + if( err != ENOERR ) + return err; + + // adjust size to this block + if( l > bsize ) + l = bsize; + + // copy data in + memcpy( fbuf, buf, l ); + + // Update working vars + len -= l; + buf += l; + pos += l; + resid -= l; + } + } + + // We wrote some data successfully, update the modified and access + // times of the node, increase its size appropriately, and update + // the file offset and transfer residue. + node->mtime = + node->ctime = cyg_timestamp(); + if( pos > node->size ) + node->size = pos; + + uio->uio_resid = resid; + fp->f_offset = pos; + + return err; +} + +// ------------------------------------------------------------------------- +// ramfs_fo_lseek() +// Seek to a new file position. + +static int ramfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *apos, int whence ) +{ + ramfs_node *node = (ramfs_node *)fp->f_data; + off_t pos = *apos; + + switch( whence ) + { + case SEEK_SET: + // Pos is already where we want to be. + break; + + case SEEK_CUR: + // Add pos to current offset. + pos += fp->f_offset; + break; + + case SEEK_END: + // Add pos to file size. + pos += node->size; + break; + + default: + return EINVAL; + } + + // All OK, set fp offset and return new position. + *apos = fp->f_offset = pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// ramfs_fo_ioctl() +// Handle ioctls. Currently none are defined. + +static int ramfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data) +{ + // No Ioctls currenly defined. + + return EINVAL; +} + +// ------------------------------------------------------------------------- +// ramfs_fo_fsync(). +// Force the file out to data storage. + +static int ramfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ) +{ + // Data is always permanently where it belongs, nothing to do + // here. + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// ramfs_fo_close() +// Close a file. We just decrement the refcnt and let it go away if +// that is all that is keeping it here. + +static int ramfs_fo_close (struct CYG_FILE_TAG *fp) +{ + ramfs_node *node = (ramfs_node *)fp->f_data; + + dec_refcnt( node ); + + fp->f_data = 0; // zero data pointer + + return ENOERR; +} + +// ------------------------------------------------------------------------- +//ramfs_fo_fstat() +// Get file status. + +static int ramfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ) +{ + ramfs_node *node = (ramfs_node *)fp->f_data; + + // Fill in the status + buf->st_mode = node->mode; + buf->st_ino = (ino_t)node; + buf->st_dev = 0; + buf->st_nlink = node->nlink; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = node->size; + buf->st_atime = node->atime; + buf->st_mtime = node->mtime; + buf->st_ctime = node->ctime; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// ramfs_fo_getinfo() +// Get info. Currently only supports fpathconf(). + +static int ramfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) +{ + ramfs_node *node = (ramfs_node *)fp->f_data; + int err; + + switch( key ) + { + case FS_INFO_CONF: + err = ramfs_pathconf( node, (struct cyg_pathconf_info *)buf ); + break; + + default: + err = EINVAL; + } + return err; +} + +// ------------------------------------------------------------------------- +// ramfs_fo_setinfo() +// Set info. Nothing supported here. + +static int ramfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) +{ + // No setinfo key supported at present + + return ENOERR; +} + + +//========================================================================== +// Directory operations + +// ------------------------------------------------------------------------- +// ramfs_fo_dirread() +// Read a single directory entry from a file. + +static int ramfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + ramfs_node *dir = (ramfs_node *)fp->f_data; + off_t pos = fp->f_offset; + int err = ENOERR; + struct dirent *ent = (struct dirent *)uio->uio_iov[0].iov_base; + char *nbuf = ent->d_name; + int nlen = sizeof(ent->d_name)-1; + off_t len = uio->uio_iov[0].iov_len; + ramfs_dirent *d = NULL; + cyg_uint8 *buf; + size_t size; + + if( len < sizeof(struct dirent) ) + return EINVAL; + + // look for a first name fragment + + while( pos < dir->size ) + { + err = findbuffer_node( dir, pos, &buf, &size, false ); + if( err != ENOERR || size == 0) + break; + + d = (ramfs_dirent *)buf; + + if( size < sizeof(ramfs_dirent) || !d->inuse || !d->first ) + { + pos += sizeof(ramfs_dirent); + continue; + } + + break; + } + + // Check we have not exceeded the size of the directory. + if( pos == dir->size ) + return err; + + // Here we have the first fragment of a directory entry. + + for(;;) + { + int fraglen = d->fraglen; + + // adjust to allow for remaining space in dirent struct + if( fraglen > nlen ) + fraglen = nlen; + + memcpy( nbuf, d->name, fraglen); + nbuf += fraglen; + nlen -= fraglen; +#ifdef CYGPKG_FS_RAM_RET_DIRENT_DTYPE + ent->d_type = d->node->mode; +#endif + + // if we hit the last entry, we have a successful transfer + if( d->last || nlen == 0) + break; + + // Otherwise move on to next entry in chain + err = findbuffer_node( dir, d->next, &buf, &size, false ); + if( err != ENOERR ) + return err; + + d = (ramfs_dirent *)buf; + } + + // A successful read. Terminate the entry name with a NUL, set the + // residue and set the file offset to restart at the next + // directory entry. + + *nbuf = '\0'; + uio->uio_resid -= sizeof(struct dirent); + fp->f_offset = pos+sizeof(ramfs_dirent); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// ramfs_fo_dirlseek() +// Seek directory to start. + +static int ramfs_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ) +{ + // Only allow SEEK_SET to zero + + if( whence != SEEK_SET || *pos != 0) + return EINVAL; + + *pos = fp->f_offset = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// EOF ramfs.c diff --git a/ecos/packages/fs/ram/current/tests/ramfs1.c b/ecos/packages/fs/ram/current/tests/ramfs1.c new file mode 100644 index 0000000..37910f3 --- /dev/null +++ b/ecos/packages/fs/ram/current/tests/ramfs1.c @@ -0,0 +1,683 @@ +//========================================================================== +// +// ramfs1.c +// +// Test fileio system +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public 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 <pkgconf/fs_ram.h> + +#include <cyg/kernel/ktypes.h> // base kernel types +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> +#include <dirent.h> + +#include <stdio.h> +#include <cyg/fileio/fileio.h> + +#include <cyg/infra/testcase.h> +#include <cyg/infra/diag.h> // HAL polled output + +//========================================================================== + +#if 0 +MTAB_ENTRY( ramfs_mte1, + "/", + "ramfs", + "", + 0); +#endif + +//========================================================================== + +#define SHOW_RESULT( _fn, _res ) \ +diag_printf("<FAIL>: " #_fn "() returned %ld %s\n", (long)_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); +#ifdef CYGPKG_FS_RAM_RET_DIRENT_DTYPE + diag_printf(" d_type %2x", entry->d_type); +#endif + 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 %ld]", + sbuf.st_mode,sbuf.st_ino,sbuf.st_nlink, + (unsigned long) sbuf.st_size); + } +#ifdef CYGPKG_FS_RAM_RET_DIRENT_DTYPE + if ((entry->d_type & S_IFMT) != (sbuf.st_mode & S_IFMT)) + CYG_TEST_FAIL("File mode's don't match between dirent and stat"); +#endif + } + + 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 %zd\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[%ld+%d](%02x) != %02x\n", + (unsigned long)pos,i,buf[i],i%256); + CYG_TEST_FAIL("Data read not equal to data written\n"); + } + + pos += done; + } + + err = close( fd ); + if( err < 0 ) SHOW_RESULT( close, err ); +} + +//========================================================================== + +static void copyfile( char *name2, char *name1 ) +{ + + int err; + char buf[IOSIZE]; + int fd1, fd2; + ssize_t done, wrote; + + diag_printf("<INFO>: copy file %s -> %s\n",name2,name1); + + err = access( name1, F_OK ); + if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err ); + + err = access( name2, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + fd1 = open( name1, O_WRONLY|O_CREAT ); + if( fd1 < 0 ) SHOW_RESULT( open, fd1 ); + + fd2 = open( name2, O_RDONLY ); + if( fd2 < 0 ) SHOW_RESULT( open, fd2 ); + + for(;;) + { + done = read( fd2, buf, IOSIZE ); + if( done < 0 ) SHOW_RESULT( read, done ); + + if( done == 0 ) break; + + wrote = write( fd1, buf, done ); + if( wrote != done ) SHOW_RESULT( write, wrote ); + + if( wrote != done ) break; + } + + err = close( fd1 ); + if( err < 0 ) SHOW_RESULT( close, err ); + + err = close( fd2 ); + if( err < 0 ) SHOW_RESULT( close, err ); + +} + +//========================================================================== + +static void comparefiles( char *name2, char *name1 ) +{ + int err; + char buf1[IOSIZE]; + char buf2[IOSIZE]; + int fd1, fd2; + ssize_t done1, done2; + int i; + + diag_printf("<INFO>: compare files %s == %s\n",name2,name1); + + err = access( name1, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + err = access( name1, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + fd1 = open( name1, O_RDONLY ); + if( fd1 < 0 ) SHOW_RESULT( open, fd1 ); + + fd2 = open( name2, O_RDONLY ); + if( fd2 < 0 ) SHOW_RESULT( open, fd2 ); + + for(;;) + { + done1 = read( fd1, buf1, IOSIZE ); + if( done1 < 0 ) SHOW_RESULT( read, done1 ); + + done2 = read( fd2, buf2, IOSIZE ); + if( done2 < 0 ) SHOW_RESULT( read, done2 ); + + if( done1 != done2 ) + diag_printf("Files different sizes\n"); + + if( done1 == 0 ) break; + + for( i = 0; i < done1; i++ ) + if( buf1[i] != buf2[i] ) + { + diag_printf("buf1[%d](%02x) != buf1[%d](%02x)\n",i,buf1[i],i,buf2[i]); + CYG_TEST_FAIL("Data in files not equal\n"); + } + } + + err = close( fd1 ); + if( err < 0 ) SHOW_RESULT( close, err ); + + err = close( fd2 ); + if( err < 0 ) SHOW_RESULT( close, err ); + +} + +//========================================================================== + +void checkcwd( const char *cwd ) +{ + static char cwdbuf[PATH_MAX]; + char *ret; + + ret = getcwd( cwdbuf, sizeof(cwdbuf)); + if( ret == NULL ) SHOW_RESULT( getcwd, ret ); + + if( strcmp( cwdbuf, cwd ) != 0 ) + { + diag_printf( "cwdbuf %s cwd %s\n",cwdbuf, cwd ); + CYG_TEST_FAIL( "Current directory mismatch"); + } +} + +//========================================================================== +// main + +int main( int argc, char **argv ) +{ + int err; + int existingdirents=-1; +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) && defined(CYGPKG_FS_RAM_BLOCKS_ARRAY) + struct cyg_fs_block_usage usage; +#endif + + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + err = mount( "", "/", "ramfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + err = chdir( "/" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/" ); + + listdir( "/", true, -1, &existingdirents ); + if ( existingdirents < 2 ) + CYG_TEST_FAIL("Not enough dir entries\n"); + // -------------------------------------------------------------- +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) && defined(CYGPKG_FS_RAM_BLOCKS_ARRAY) + err = cyg_fs_getinfo("/", FS_INFO_BLOCK_USAGE, &usage, sizeof(usage)); + if( err < 0 ) SHOW_RESULT( cyg_fs_getinfo, err ); + diag_printf("<INFO>: total size: %6lld blocks, %10lld bytes\n", + usage.total_blocks, usage.total_blocks * usage.block_size); + diag_printf("<INFO>: free size: %6lld blocks, %10lld bytes\n", + usage.free_blocks, usage.free_blocks * usage.block_size); + diag_printf("<INFO>: block size: %6u bytes\n", usage.block_size); +#endif + // -------------------------------------------------------------- + + createfile( "/foo", 202 ); + checkfile( "foo" ); + copyfile( "foo", "fee"); + checkfile( "fee" ); + comparefiles( "foo", "/fee" ); + diag_printf("<INFO>: mkdir bar\n"); + err = mkdir( "/bar", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + listdir( "/" , true, existingdirents+3, NULL ); + + copyfile( "fee", "/bar/fum" ); + checkfile( "bar/fum" ); + comparefiles( "/fee", "bar/fum" ); + + diag_printf("<INFO>: cd bar\n"); + err = chdir( "bar" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/bar" ); + + diag_printf("<INFO>: rename /foo bundy\n"); + err = rename( "/foo", "bundy" ); + if( err < 0 ) SHOW_RESULT( rename, err ); + + listdir( "/", true, existingdirents+2, NULL ); + listdir( "" , true, 4, NULL ); + + checkfile( "/bar/bundy" ); + comparefiles("/fee", "bundy" ); + + // -------------------------------------------------------------- + + createfile( LONGNAME1, 123 ); + checkfile( LONGNAME1 ); + copyfile( LONGNAME1, LONGNAME2 ); + + listdir( "", false, 6, NULL ); + + diag_printf("<INFO>: unlink " LONGNAME1 "\n"); + err = unlink( LONGNAME1 ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: unlink " LONGNAME2 "\n"); + err = unlink( LONGNAME2 ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + + // -------------------------------------------------------------- + + diag_printf("<INFO>: unlink fee\n"); + err = unlink( "/fee" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: unlink fum\n"); + err = unlink( "fum" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: unlink /bar/bundy\n"); + err = unlink( "/bar/bundy" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: cd /\n"); + err = chdir( "/" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/" ); + + diag_printf("<INFO>: rmdir /bar\n"); + err = rmdir( "/bar" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + listdir( "/", false, existingdirents, NULL ); + + // -------------------------------------------------------------- + + diag_printf("<INFO>: mount /ram \n"); + err = mount( "", "/ram", "ramfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + createfile( "/ram/tinky", 456 ); + copyfile( "/ram/tinky", "/ram/laalaa" ); + checkfile( "/ram/tinky"); + checkfile( "/ram/laalaa"); + comparefiles( "/ram/tinky", "/ram/laalaa" ); + + diag_printf("<INFO>: cd /ram\n"); + err = chdir( "/ram" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/ram" ); + + diag_printf("<INFO>: mkdir noonoo\n"); + err = mkdir( "noonoo", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + listdir( "." , true, existingdirents+3, NULL); + + diag_printf("<INFO>: cd noonoo\n"); + err = chdir( "noonoo" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/ram/noonoo" ); + + createfile( "tinky", 678 ); + checkfile( "tinky" ); + + createfile( "dipsy", 3456 ); + checkfile( "dipsy" ); + copyfile( "dipsy", "po" ); + checkfile( "po" ); + comparefiles( "dipsy", "po" ); + + listdir( ".", true, 5, NULL ); + listdir( "", true, 5, NULL ); + listdir( "..", true, existingdirents+3, NULL ); + + // -------------------------------------------------------------- +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) && defined(CYGPKG_FS_RAM_BLOCKS_ARRAY) + err = cyg_fs_getinfo("/", FS_INFO_BLOCK_USAGE, &usage, sizeof(usage)); + if( err < 0 ) SHOW_RESULT( cyg_fs_getinfo, err ); + diag_printf("<INFO>: total size: %6lld blocks, %10lld bytes\n", + usage.total_blocks, usage.total_blocks * usage.block_size); + diag_printf("<INFO>: free size: %6lld blocks, %10lld bytes\n", + usage.free_blocks, usage.free_blocks * usage.block_size); + diag_printf("<INFO>: block size: %6u bytes\n", usage.block_size); +#endif + // -------------------------------------------------------------- + + diag_printf("<INFO>: unlink tinky\n"); + err = unlink( "tinky" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: unlink dipsy\n"); + err = unlink( "dipsy" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: unlink po\n"); + err = unlink( "po" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: cd ..\n"); + err = chdir( ".." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/ram" ); + + diag_printf("<INFO>: rmdir noonoo\n"); + err = rmdir( "noonoo" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + // -------------------------------------------------------------- + + err = mkdir( "x", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + err = mkdir( "x/y", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + err = mkdir( "x/y/z", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + err = mkdir( "x/y/z/w", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + diag_printf("<INFO>: cd /ram/x/y/z/w\n"); + err = chdir( "/ram/x/y/z/w" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/ram/x/y/z/w" ); + + diag_printf("<INFO>: cd ..\n"); + err = chdir( ".." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/ram/x/y/z" ); + + diag_printf("<INFO>: cd .\n"); + err = chdir( "." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/ram/x/y/z" ); + + diag_printf("<INFO>: cd ../../y\n"); + err = chdir( "../../y" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/ram/x/y" ); + + diag_printf("<INFO>: cd ../..\n"); + err = chdir( "../.." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/ram" ); + + diag_printf("<INFO>: rmdir x/y/z/w\n"); + err = rmdir( "x/y/z/w" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + diag_printf("<INFO>: rmdir x/y/z\n"); + err = rmdir( "x/y/z" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + diag_printf("<INFO>: rmdir x/y\n"); + err = rmdir( "x/y" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + diag_printf("<INFO>: rmdir x\n"); + err = rmdir( "x" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + // -------------------------------------------------------------- + + diag_printf("<INFO>: unlink tinky\n"); + err = unlink( "tinky" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: unlink laalaa\n"); + err = unlink( "laalaa" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf("<INFO>: cd /\n"); + err = chdir( "/" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/" ); + + diag_printf("<INFO>: umount /ram\n"); + err = umount( "/ram" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + CYG_TEST_PASS_FINISH("ramfs1"); +} + +// ------------------------------------------------------------------------- +// EOF ramfs1.c diff --git a/ecos/packages/fs/ram/current/tests/ramfs2.c b/ecos/packages/fs/ram/current/tests/ramfs2.c new file mode 100644 index 0000000..ca0065b --- /dev/null +++ b/ecos/packages/fs/ram/current/tests/ramfs2.c @@ -0,0 +1,238 @@ +//========================================================================== +// +// ramfs2.c +// +// Test fseek on a filesystem +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): asl +// Contributors: asl +// Date: 2004-03-29 +// Purpose: Test fseek on a filesystem +// Description: This test uses the ramfs to check out the fseek +// operation on a filesystem. +// +// +// +// +// +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#include <cyg/fileio/fileio.h> + +#include <cyg/infra/testcase.h> +#include <cyg/infra/diag.h> // HAL polled output +//========================================================================== + +#define SHOW_RESULT( _fn, _res ) \ +diag_printf("FAIL: " #_fn "() returned %ld %s\n", (long)_res, _res<0?strerror(errno):""); + +//========================================================================== + +char buf[1024]; +char buf1[1024]; + +//========================================================================== +// main + +int main( int argc, char **argv ) +{ + int err; + FILE *stream; + long pos; + int i; + + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + CYG_TEST_INFO("mount /"); + err = mount( "", "/", "ramfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + CYG_TEST_INFO("creating /fseek"); + stream = fopen("/fseek","w+"); + if (!stream) { + diag_printf("FAIL: fopen() returned NULL, %s\n", strerror(errno)); + CYG_TEST_FINISH("done"); \ + } + + /* Write a buffer full of cyclic numbers */ + for (i = 0; i < sizeof(buf); i++) { + buf[i] = i % 256; + } + + CYG_TEST_INFO("writing test pattern"); + err=fwrite(buf,sizeof(buf), 1, stream); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + + /* The current position should be the same size as the buffer */ + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != sizeof(buf)) + diag_printf("<FAIL>: ftell is not telling the truth."); + + CYG_TEST_INFO("fseek()ing to beginning and writing"); + + /* Seek back to the beginning of the file */ + err = fseek(stream, 0, SEEK_SET); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != 0) CYG_TEST_FAIL("ftell is not telling the truth"); + + /* Write 4 zeros to the beginning of the file */ + for (i = 0; i < 4; i++) { + buf[i] = 0; + } + + err = fwrite(buf, 4, 1, stream); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + + /* Check the pointer is at 4 */ + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != 4) CYG_TEST_FAIL("ftell is not telling the truth"); + + CYG_TEST_INFO("closing file"); + + /* Close the file, open it up again and read it back */ + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("open file /fseek"); + stream = fopen("/fseek", "r+"); + if (!stream) { + diag_printf("<FAIL>: fopen() returned NULL, %s\n", strerror(errno)); + } + + err = fread(buf1,sizeof(buf1),1, stream); + if (err != 1) SHOW_RESULT( fread, err ); + + CYG_TEST_INFO("Comparing contents"); + if (memcmp(buf, buf1, sizeof(buf1))) { + CYG_TEST_FAIL("File contents inconsistent"); + } + + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("open file /fseek"); + stream = fopen("/fseek", "r+"); + if (!stream) { + diag_printf("<FAIL>: fopen() returned NULL, %s\n", strerror(errno)); + } + + CYG_TEST_INFO("fseek()ing past the end to create a hole"); + /* Seek 1K after the end of the file */ + err = fseek(stream, sizeof(buf), SEEK_END); + if ( err < 0 ) SHOW_RESULT( fseek, err ); + + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != (2*sizeof(buf))) CYG_TEST_FAIL("ftell is not telling the truth"); + + CYG_TEST_INFO("writing test pattern"); + err=fwrite(buf,sizeof(buf), 1, stream); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + + pos = ftell(stream); + + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != (3*sizeof(buf))) CYG_TEST_FAIL("ftell is not telling the truth"); + + CYG_TEST_INFO("closing file"); + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("open file /fseek"); + stream = fopen("/fseek", "r+"); + if (!stream) { + diag_printf("<FAIL>: fopen() returned NULL, %s\n", strerror(errno)); + } + + err = fread(buf1,sizeof(buf1),1, stream); + if (err != 1) SHOW_RESULT( fread, err ); + + CYG_TEST_INFO("Comparing contents"); + if (memcmp(buf, buf1, sizeof(buf1))) { + CYG_TEST_FAIL("File contents inconsistent"); + } + + err = fread(buf1,sizeof(buf1),1, stream); + if (err != 1) SHOW_RESULT( fread, err ); + + for (i = 0; i< sizeof(buf); i++) { + if (buf1[i] != 0) + CYG_TEST_FAIL("Hole does not contain zeros"); + } + + err = fread(buf1,sizeof(buf1),1, stream); + if (err != 1) SHOW_RESULT( fread, err ); + + if (memcmp(buf, buf1, sizeof(buf1))) { + CYG_TEST_FAIL("File contents inconsistent"); + } + + CYG_TEST_INFO("closing file"); + + /* Close the file */ + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("umount /"); + err = umount( "/" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + CYG_TEST_PASS_FINISH("ramfs2"); + + +} diff --git a/ecos/packages/fs/ram/current/tests/ramfs3.c b/ecos/packages/fs/ram/current/tests/ramfs3.c new file mode 100644 index 0000000..c65c579 --- /dev/null +++ b/ecos/packages/fs/ram/current/tests/ramfs3.c @@ -0,0 +1,183 @@ +//========================================================================== +// +// ramfs3.c +// +// Test fileio system, especially lseek calls. +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg, asl, Paluch Sebastian +// Contributors: nickg +// Date: 2006-10-05 +// Purpose: Test fileio system +// Description: This test uses the testfs to check out the initialization +// and basic operation of the fileio system +// +// +// +// +// +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#include <cyg/fileio/fileio.h> + +#include <cyg/infra/testcase.h> +#include <cyg/infra/diag.h> // HAL polled output +//========================================================================== + +#define SHOW_RESULT( _fn, _res ) \ +diag_printf("FAIL: " #_fn "() returned %ld %s\n", \ + (unsigned long)_res, _res<0?strerror(errno):""); + +//========================================================================== + +cyg_uint8 buf[256]; +cyg_uint8 buf1[256]; + +//========================================================================== +// main + +int main( int argc, char **argv ) +{ + int err; + FILE *stream; + long pos; + unsigned int i; + char header[3]; + + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + CYG_TEST_INFO("mount /"); + err = mount( "", "/", "ramfs" ); + + if( err < 0 ) SHOW_RESULT( mount, err ); + + CYG_TEST_INFO("creating /fseek"); + stream = fopen("/fseek","w+"); + if (!stream) { + SHOW_RESULT( fopen, NULL); + CYG_TEST_FAIL_FINISH("done");\ + } + + for (i = 0; i < sizeof(buf); i++) { + buf[i] = i % 256; + } + + CYG_TEST_INFO("writing test pattern"); + err=fwrite(buf,sizeof(buf), 1, stream); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + + pos = ftell(stream); + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != sizeof(buf)) + diag_printf("<FAIL>: ftell is not telling the truth."); + + CYG_TEST_INFO("fseek()ing to 85"); + err = fseek(stream, 85, SEEK_SET); + if ( err < 0 ) SHOW_RESULT( fseek, err ); + + pos = ftell(stream); + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != 85) CYG_TEST_FAIL("ftell is not telling the truth"); + + err = fread(header,3,1,stream); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + if ((header[0] != 85) || + (header[1] != 86) || + (header[2] != 87)) + CYG_TEST_FAIL("Read returned false data"); + + pos = ftell(stream); + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != 88) CYG_TEST_FAIL("ftell is not telling the truth"); + + for (i = 88; i < 161; i++) { + buf[i] = 0x42; + } + + CYG_TEST_INFO("writing"); + err = fwrite(buf+88, 73, 1, stream); + if ( err < 0 ) SHOW_RESULT( fwrite, err ); + + pos = ftell(stream); + if (pos < 0) SHOW_RESULT( ftell, pos ); + if (pos != 161) CYG_TEST_FAIL("ftell is not telling the truth"); + + CYG_TEST_INFO("closing file"); + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("open file /fseek"); + stream = fopen("/fseek", "r+"); + if (!stream) { + SHOW_RESULT( fopen, NULL); + CYG_TEST_FAIL_FINISH("done");\ + } + + CYG_TEST_INFO("Seeking to beginning of file"); + err = fseek(stream, 0, SEEK_SET); + if ( err < 0 ) SHOW_RESULT( fseek, err ); + + CYG_TEST_INFO("Reading buf1"); + err = fread(buf1,sizeof(buf1),1, stream); + if (err != 1) SHOW_RESULT( fread, err ); + + CYG_TEST_INFO("Comparing contents"); + if (memcmp(buf, buf1, sizeof(buf1))) + CYG_TEST_FAIL("File contents inconsistent"); + + CYG_TEST_INFO("closing file"); + err = fclose(stream); + if (err != 0) SHOW_RESULT( fclose, err ); + + CYG_TEST_INFO("umount /"); + err = umount( "/" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + CYG_TEST_PASS_FINISH("ramfs3"); +} diff --git a/ecos/packages/fs/rom/current/ChangeLog b/ecos/packages/fs/rom/current/ChangeLog new file mode 100644 index 0000000..dcaefd7 --- /dev/null +++ b/ecos/packages/fs/rom/current/ChangeLog @@ -0,0 +1,217 @@ +2010-11-29 John Dallaway <john@dallaway.org.uk> + + * cdl/romfs.cdl: Add CYGFUN_FS_ROM_FLASH_BLOCK_DEVICE_LOOKUP option. + * support/mk_romfs.c: Fix Win32 build. + +2010-11-13 Alexander Aganichev <aaganichev@gmail.com> + + * src/romfs.c: Added ability to mount a flash block device by name. + +2010-02-28 Øyvind Harboe <oyvind.harboe@zylin.com> + + * src/romfs.c: file name comparison was broken for two files + with the same stem. A directory/file called "foo" would not + be found if there was a file "foo*" before "foo". + +2009-10-09 John Dallaway <john@dallaway.org.uk> + + * cdl/romfs.cdl: Eliminate workarounds for file path handling + issue in obsolete Cygwin tclsh. Build the mk_romfs tool with higher + priority than the custom rules which use it. + +2009-04-28 John Dallaway <john@dallaway.org.uk> + + * cdl/romfs.cdl: Use CYGPKG_IO_FILEIO as the parent. + +2009-02-07 John Dallaway <john@dallaway.org.uk> + + * cdl/romfs.cdl: Pass file2c.tcl to tclsh directly. + +2009-01-08 John Dallaway <john@dallaway.org.uk> + + * support/file2c.tcl: Specify script interpreter via /usr/bin/env. + +2008-08-14 Klaas Gadeyne <klaas.gadeyne@fmtc.be> +2008-08-14 Jonathan Larmour <jifl@eCosCentric.com> + + * doc/mk_romfs.txt (Usage): Remove documentation about obsolete + CYGNUM_FS_ROM_BASE_ADDRESS option, and replace with better + usage description. + +2006-11-13 Xinghua Yang <yxinghua@sunnorth.com.cn> + Taiyun Wang <taiyun@sunnorth.com.cn> + + * cdl/romfs.cdl: Use CYGPKG_FS_ROM_RET_DIRENT_DTYPE to control + whether fatfs sets file type in romfs_fo_dirread. + * src/romfs.c: Set file type in romfs_fo_dirread. + * tests/romfs1.c: Test the new d_type in dirent when present. + +2006-11-13 Oyvind Harboe <oyvind.harboe@zylin.com> + + * support/file2c.tcl : align romdisk data to 4 bytes. With a bit + of bad luck, it would not be long-word aligned. + +2006-08-04 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/romfs.c (romfs_getinfo): Support for block usage call. + * tests/romfs1.c (main): Add file system block usage test. + +2006-02-15 Andrew Lunn <andrew.lunn@ascom.ch> + Peter Korsgaard <jacmet@sunsite.dk> + + * support/mk_romfs.c: Use stdint.h defined types so the code is + 64 bit clean. Fix compiler warnings. + +2005-07-08 Andrew Lunn <andrew.lunn@ascom.ch> + + * cdl/romfs.cdl: Allow mk_romfs to be build even when the tests + are disabled. It is generally useful and other tests programs may + want it. + +2004-12-13 John Dallaway <jld@ecoscentric.com> + + * tests/fileio1.c: Rename to romfs1.c. eCos test names should be + unique. + * cdl/romfs.cdl: Build the romfs1 test. + +2004-10-04 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/romfs.c (romfs_mount): Avoid a compiler warning about punned + types. + +2004-08-08 Bart Veer <bartv@ecoscentric.com> + + * cdl/romfs.cdl: generate both little-endian and big-endian image + files. + + * tests/fileio1.c: include either a little-endian or a big-endian + image. Totally fail the test early on if the file system cannot be + mounted. + +2004-06-14 John Dallaway <jld@ecoscentric.com> + + * cdl/romfs.cdl: Specify the test executable file name for + compatibility with the eCos Configuration Tool. + +2004-02-20 Vincent Catros <Vincent.Catros@elios-informatique.fr> + + * src/fs-ecos.c : + (jffs2_find) Policy to skip path separator is no longer + "if '/' then skip" but "while '/' then skip" allowing + multi '/' separators (i.e : /tmp////foo). + (find_entry) Policy to detect end of path is no longer + "if '\0' then end_of_path" + but "while '/' skip it and then if '\0' then end_of_path" + allowing path terminated with any number of '/' + (i.e : chdir(/tmp///)). + +2003-12-11 Sandeep Kumar <sandeep@codito.com> + + * src/romfs.c (romfs_mount) : function wrongly returns ENOENT even + if fste->data isn't NULL. + +2003-09-25 Oyvind Harboe <oyvind.harboe@zylin.com> + + * src/romfs.c (romfs_mount): If a mount fails, make sure we leave + all state information in such a way we can try again. + +2003-07-10 Andrew Lunn <andrew.lunn@ascom.ch> + + * support/mk_romfs.c: S_I[RWX]{USR|GRP|OTH} etc changed to match + the changes in sys/stat.h + +2003-02-24 Jonathan Larmour <jifl@eCosCentric.com> + + * cdl/romfs.cdl: Fix doc link. + +2003-01-30 Andrew Lunn <andrew.lunn@ascom.ch> + + * cdl/romfs.cdl: Implements the CYGINT_IO_FILEIO_FS interface. + +2003-01-29 John Dallaway <jld@ecoscentric.com> + + * support/file2c.tcl: Accommodate latest Cygwin Tcl shell + (tclsh83.exe) + +2002-04-15 Bart Veer <bartv@redhat.com> + + * support/file2c.tcl: + Do not use an alignment attribute, since it is not honoured on + all targets. + + * src/romfs.c: + Remove alignment restrictions, since they are not actually needed + yet and alignment is hard to guarantee on all targets. + +2002-01-21 Jonathan Larmour <jlarmour@redhat.com> + + * support/mk_romfs.c: Open image file in binary mode (for cygwin). + Spotted by Warren Jasper. + +2001-11-23 Jonathan Larmour <jlarmour@redhat.com> + + * cdl/romfs.cdl (CYGTST_ROMFS_BUILD_TESTS): Try gcc and cc if $HOST_CC + doesn't exist or has a problem. + +2001-11-22 Jesper Skov <jskov@redhat.com> + + * cdl/romfs.cdl: Use HOST_CC instead of 'cc'. + +2001-10-17 Drew Moseley <dmoseley@redhat.com> +2001-10-17 Jonathan Larmour <jlarmour@redhat.com> + + * support/mk_romfs.c: Open input files in binary mode (for cygwin). + * cdl/romfs.cdl: Work around cygwin path problems by copying files + into build tree. + +2001-07-20 Jonathan Larmour <jlarmour@redhat.com> + + * tests/fileio1.c (main): Get this to actually pass! Remove + kernel dependency. + * cdl/fileio.cdl: Get CDL dependencies better. Don't use + fixed base address. Make test building an option. Build mk_romfs + and use it to construct a test romfs. + * support/mk_romfs.c: fix trivial typo + * tests/testromfs: Directory hierarchy added for constructing test + ROMFS. + +2001-07-13 Richard Panton (richard.panton@3glab.com) + + * support/mk_romfs.c: Convert between host FS file modes and eCos + ones. + +2000-10-25 Richard Panton (richard.panton@3glab.com) + + * cdl/romfs.cdl: + * src/romfs.c: + * support/mk_romfs.c: + * tests/fileio1.c: + A sample ROM filesystem implementation + + + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2009, 2010 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== + + diff --git a/ecos/packages/fs/rom/current/cdl/romfs.cdl b/ecos/packages/fs/rom/current/cdl/romfs.cdl new file mode 100644 index 0000000..8aa13fe --- /dev/null +++ b/ecos/packages/fs/rom/current/cdl/romfs.cdl @@ -0,0 +1,155 @@ +# ==================================================================== +# +# romfs.cdl +# +# ROM Filesystem configuration data +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2009, 2010 Free Software Foundation, 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## This exception does not invalidate any other reasons why a work based +## on this file might be covered by the GNU General Public License. +## ------------------------------------------- +## ####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): nickg +# Original data: nickg +# Contributors: richard.panton@3glab.com, jld +# Date: 2000-08-01 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_FS_ROM { + display "ROM filesystem" + doc ref/fileio.html + include_dir cyg/romfs + + parent CYGPKG_IO_FILEIO + requires CYGPKG_IO_FILEIO + + requires CYGPKG_ISOINFRA + requires CYGINT_ISO_ERRNO + requires CYGINT_ISO_ERRNO_CODES + + implements CYGINT_IO_FILEIO_FS + + compile -library=libextras.a romfs.c + + cdl_option CYGBLD_FS_ROMFS_MK_ROMFS { + display "Build the tool used to build filesystems" + flavor bool + default_value 1 + + make -priority 98 { + <PREFIX>/bin/file2c.tcl: <PACKAGE>/support/file2c.tcl + @mkdir -p "$(dir $@)" + @cp $< $@ + } + + # FIXME: host compiler/flags should be provided by config system + make -priority 98 { + <PREFIX>/bin/mk_romfs: <PACKAGE>/support/mk_romfs.c + @mkdir -p "$(dir $@)" + @$(HOST_CC) -g -O2 -o $@ $< || cc -g -O2 -o $@ $< || gcc -g -O2 -o $@ $< + } + + description " + When enabled this option will build a host tool which can be + used to create a rom filesystem image." + } + + cdl_option CYGPKG_FS_ROM_RET_DIRENT_DTYPE { + display "Support for fileio's struct dirent d_type field" + flavor bool + default_value 0 + active_if CYGPKG_FILEIO_DIRENT_DTYPE + + description " + This option controls whether the ROM filesystem supports + setting fileio's struct dirent d_type field. + If this option is enabled, d_type will be set. Otherwise, + nothing will be done, d_type's value will be zero because + fileio already sets it." + } + + cdl_option CYGFUN_FS_ROM_FLASH_BLOCK_DEVICE_LOOKUP { + display "Lookup flash block device names" + flavor bool + requires CYGPKG_IO_FLASH + requires CYGPKG_IO_FLASH_BLOCK_DEVICE + default_value 0 + description "Enables the location of a ROM filesystem to + be specified using a flash block device name such as + \"/dev/flash/fis/romfs\" in the call to mount()." + } + + # ---------------------------------------------------------------- + # Tests + + cdl_component CYGTST_ROMFS_BUILD_TESTS { + display "Build ROM filesystem tests" + flavor bool + no_define + default_value 0 + requires CYGINT_LIBC_STARTUP_CONTEXT + requires CYGINT_ISO_STDIO_FORMATTED_IO + requires CYGINT_ISO_STRERROR + requires CYGBLD_FS_ROMFS_MK_ROMFS + description " + This option enables the building of the ROM filesystem tests." + + make -priority 100 { + <PREFIX>/include/cyg/romfs/testromfs_le.h : <PACKAGE>/tests/testromfs <PREFIX>/bin/mk_romfs <PREFIX>/bin/file2c.tcl + $(PREFIX)/bin/mk_romfs $< testromfs_le.bin + @mkdir -p "$(dir $@)" + tclsh $(PREFIX)/bin/file2c.tcl testromfs_le.bin $@ + } + + make -priority 100 { + <PREFIX>/include/cyg/romfs/testromfs_be.h : <PACKAGE>/tests/testromfs <PREFIX>/bin/mk_romfs <PREFIX>/bin/file2c.tcl + $(PREFIX)/bin/mk_romfs -b $< testromfs_be.bin + @mkdir -p "$(dir $@)" + tclsh $(PREFIX)/bin/file2c.tcl testromfs_be.bin $@ + } + + cdl_option CYGPKG_FS_ROM_TESTS { + display "ROM filesystem tests" + flavor data + no_define + calculated { "tests/romfs1" } + description " + This option specifies the set of tests for the ROM filesystem package." + } + } +} + +# End of romfs.cdl diff --git a/ecos/packages/fs/rom/current/doc/mk_romfs.txt b/ecos/packages/fs/rom/current/doc/mk_romfs.txt new file mode 100644 index 0000000..f322513 --- /dev/null +++ b/ecos/packages/fs/rom/current/doc/mk_romfs.txt @@ -0,0 +1,111 @@ +MK_ROMFS - Make a ROMFS image +============================= + +This program creates a ROMFS image that can be read by eCos. + +mk_romfs - Create an eCos ROMFS disk image from the files + contained under a specified directory + +Usage: ../support/mk_romfs [options] <fs_root> <fs_file> + fs_root is the directory containing the files to package into the ROMFS image + fs_file is the name of the ROMFS image file to create + Options include: + -v / -q increase / decrease verbosity + -n do everything EXCEPT creating the output file + -b write a big-endian image (default is little endian) + -l collapse hard links to a single node + +----------- +How to use. +----------- + +For example, suppose you wish to access the following directories and files: + / + /etc passwd, group + /dev + /mnt + /tmp (for a RAMFS) + /var (for a RAMFS) + +1. Create the required directories and files under a suitable root, eg. under + ~/rom: + $ mkdir ~/rom + $ cd ~/rom + $ mkdir etc dev mnt tmp var + $ cp /etc/passwd /etc/group etc/ + ( remembering to edit these files....;-) + +2. Make the romfs image in a suitable place, eg /tftpboot for direct upload to + the RedBoot monitor. + $ mk_romfs -v . /tftpboot/romfs.img + mk_romfs: Verbosity 2 little endian + Phase 1 - Build file list + Phase 2 - Calculate space allocation + Phase 2a - * Directories + Phase 2b - * Regular files + Phase 2c - * Executable files + Phase 3 - Construct ROMFS image file (3 kb) + Phase 3a - * Node table + Phase 3b - * Data blocks + /tftpboot/romfs.img completed + +3. Connect to your target RedBoot monitor, and load the romfs image. You will + need to determine a suitable place in RAM to load the image into. + $ telnet xxx.xxx.xxx.xxx 1000 + Trying xxx.xxx.xxx.xxx... + Connected to xxx.xxx.xxx.xxx. + Escape character is '^]'. + RedBoot> load romfs.img -r -v -b 0x1000000 + Raw file loaded 0x01000000-0x0100093e + RedBoot> + +4. Determine where to load the romfs image in the ROM. See what's there... + RedBoot> fis list + Name FLASH addr Mem addr Length Entry point + RedBoot 0x50000000 0x50000000 0x020000 0x00000000 + RedBoot[backup] 0x50020000 0x50020000 0x020000 0x00000000 + RedBoot config 0x503C0000 0x503C0000 0x020000 0x00000000 + FIS directory 0x503E0000 0x503E0000 0x020000 0x00000000 + RedBoot> fis free + 0x50040000 .. 0x503C0000 + RedBoot> + We can see that a suitable place would be 0x50040000. + Alternatively, you can let RedBoot determine the address itself... + +5. Copy the image from RAM to ROM... + RedBoot> fis create -b 0x1000000 -l 0x940 RomFs + ... Erase from 0x50040000-0x50040940: . + ... Program from 0x01000000-0x01000940 at 0x50040000: . + ... Erase from 0x503e0000-0x50400000: . + ... Program from 0x01fd0000-0x01ff0000 at 0x503e0000: . + RedBoot> fis list + Name FLASH addr Mem addr Length Entry point + RedBoot 0x50000000 0x50000000 0x020000 0x00000000 + RedBoot[backup] 0x50020000 0x50020000 0x020000 0x00000000 + RedBoot config 0x503C0000 0x503C0000 0x020000 0x00000000 + FIS directory 0x503E0000 0x503E0000 0x020000 0x00000000 + RomFs 0x50040000 0x01000000 0x000940 0x00000000 + RedBoot> + +6. MAKE A NOTE OF THE ADDRESS IN FLASH THAT THE IMAGE IS LOADED AT. + + This address can then be used in your program in order to mount the + filesystem. + + For example, to automatically mount the new romfs created above at + /rom, you can use the following macro in your application: + + MTAB_ENTRY( romfs_mte1, + "/rom", + "romfs", + "", + (CYG_ADDRWORD) 0x50040000 ); + + See the File I/O package documentation in the eCos Reference Manual + ("Writing a New Filesystem") for more information about the MTAB_ENTRY + macro. + + Alternatively, the filesystem can be dynamically mounted with a call + to mount() such as the following: + + err = mount( "0x50040000", "/rom", "romfs" ); diff --git a/ecos/packages/fs/rom/current/doc/romfs.txt b/ecos/packages/fs/rom/current/doc/romfs.txt new file mode 100644 index 0000000..7b34a13 --- /dev/null +++ b/ecos/packages/fs/rom/current/doc/romfs.txt @@ -0,0 +1,12 @@ +ROMFS - A rom filesystem plug-in module for eCos +================================================ + +You can use this module if you have flash ROM available to install the +filesystem into. Instructions are given for creating and installing a +rom filesystem into a RedBoot monitor. + +See mk_romfs for instructions for making the filesystem. + +See romfs.c for comments about the filesystem structure. + + diff --git a/ecos/packages/fs/rom/current/src/romfs.c b/ecos/packages/fs/rom/current/src/romfs.c new file mode 100644 index 0000000..b748200 --- /dev/null +++ b/ecos/packages/fs/rom/current/src/romfs.c @@ -0,0 +1,1138 @@ +//========================================================================== +// +// romfs.c +// +// ROM file system +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg, richard.panton@3glab.com +// Date: 2000-07-25 +// Purpose: ROM file system +// Description: This is a ROM filesystem for eCos. It attempts to +// provide full POSIX-compatible filesystem behaviour +// while at the same time being efficient in terms of +// time and space used. +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== +// +// General Description +// =================== +// +// This is an implementation of a ROM filesystem for eCos. Its goal is +// to provide a working example of a filesystem that provides most of +// the required POSIX functionality. And obviously it may also be +// useful in its own right. +// +// +// Header +// ------ +// +// There is a single header that describes the overall format of the ROMFS +// disk. The address of this header is used as the base for all offsets used +// in the node and directory structures. It contains the following fields: +// +// label - A human readable label for various purposes +// fssize - The size in bytes of the entire ROMFS disk +// nodes - A count of the nodes in the disk +// +// Immediately following thisin memory is the node table, consisting of +// 'nodes' repetitions of the node object. +// +// Nodes +// ----- +// +// All files and directories are represented by node objects. Each +// romfs_node structure contains the following fields: +// +// mode - Node type, file or directory. +// nlink - Number of links to this node. Each directory entry that references +// this node is a link. +// size - Size of the data in this node in bytes. +// ctime - Creation time of the file (NOT the ROMFS) +// data - Offset of the first data byte for this node from the header +// +// Directories +// ----------- +// +// A directory is a node whose data is a list of directory entries. +// These contain the +// following fields: +// +// node - Index of the node in the romfs_disk table that is referenced by +// this entry. This is present in every directory entry fragment. +// next - Offset of the next name entry. +// name - The filename associated with this link to the node. +// +// Data Storage +// ------------ +// +// Each file has its data stored in a single contiguous memory block +// referenced by the offset in the node. +// +//========================================================================== + +#include <pkgconf/system.h> +#include <pkgconf/hal.h> +#include <pkgconf/kernel.h> +#include <pkgconf/io_fileio.h> +#include <pkgconf/fs_rom.h> + +#include <cyg/kernel/ktypes.h> // base kernel types +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros + +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <dirent.h> + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <cyg/fileio/fileio.h> + +#include <cyg/kernel/kapi.h> +#include <cyg/infra/diag.h> + +#if defined(CYGFUN_FS_ROM_FLASH_BLOCK_DEVICE_LOOKUP) +#include <cyg/io/io.h> +#include <cyg/io/config_keys.h> +#include <cyg/io/flash.h> +#endif + +//========================================================================== +// Eventually we want to eXecute In Place from the ROM in a protected +// environment, so we'll need executables to be aligned to a boundary +// suitable for MMU protection. A suitable boundary would be the 4k +// boundary in all the CPU architectures I am currently aware of. + +// Forward definitions + +// Filesystem operations +static int romfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ); +static int romfs_umount ( cyg_mtab_entry *mte ); +static int romfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int mode, cyg_file *fte ); +static int romfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_file *fte ); +static int romfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_dir *dir_out ); +static int romfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + struct stat *buf); +static int romfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); +static int romfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); + +// File operations +static int romfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int romfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); +static int romfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data); +static int romfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ); +static int romfs_fo_close (struct CYG_FILE_TAG *fp); +static int romfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ); +static int romfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); +static int romfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); + +// Directory operations +static int romfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int romfs_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); + + +//========================================================================== +// Filesystem table entries + +// ------------------------------------------------------------------------- +// Fstab entry. +// This defines the entry in the filesystem table. +// For simplicity we use _FILESYSTEM synchronization for all accesses since +// we should never block in any filesystem operations. + +FSTAB_ENTRY( romfs_fste, "romfs", 0, + CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM, + romfs_mount, + romfs_umount, + romfs_open, + (cyg_fsop_unlink *)cyg_fileio_erofs, + (cyg_fsop_mkdir *)cyg_fileio_erofs, + (cyg_fsop_rmdir *)cyg_fileio_erofs, + (cyg_fsop_rename *)cyg_fileio_erofs, + (cyg_fsop_link *)cyg_fileio_erofs, + romfs_opendir, + romfs_chdir, + romfs_stat, + romfs_getinfo, + romfs_setinfo); + +// ------------------------------------------------------------------------- +// mtab entry. +// This defines a single ROMFS loaded into ROM at the configured address +// +// MTAB_ENTRY( rom_mte, // structure name +// "/rom", // mount point +// "romfs", // FIlesystem type +// "", // hardware device +// (CYG_ADDRWORD) CYGNUM_FS_ROM_BASE_ADDRESS // Address in ROM +// ); + + +// ------------------------------------------------------------------------- +// File operations. +// This set of file operations are used for normal open files. + +static cyg_fileops romfs_fileops = +{ + romfs_fo_read, + (cyg_fileop_write *)cyg_fileio_erofs, + romfs_fo_lseek, + romfs_fo_ioctl, + cyg_fileio_seltrue, + romfs_fo_fsync, + romfs_fo_close, + romfs_fo_fstat, + romfs_fo_getinfo, + romfs_fo_setinfo +}; + +// ------------------------------------------------------------------------- +// Directory file operations. +// This set of operations are used for open directories. Most entries +// point to error-returning stub functions. Only the read, lseek and +// close entries are functional. + +static cyg_fileops romfs_dirops = +{ + romfs_fo_dirread, + (cyg_fileop_write *)cyg_fileio_enosys, + romfs_fo_dirlseek, + (cyg_fileop_ioctl *)cyg_fileio_enosys, + cyg_fileio_seltrue, + (cyg_fileop_fsync *)cyg_fileio_enosys, + romfs_fo_close, + (cyg_fileop_fstat *)cyg_fileio_enosys, + (cyg_fileop_getinfo *)cyg_fileio_enosys, + (cyg_fileop_setinfo *)cyg_fileio_enosys +}; + +//========================================================================== +// Data typedefs +// Some forward typedefs for the main data structures. + +struct romfs_disk; +typedef struct romfs_disk romfs_disk; + +struct romfs_node; +typedef struct romfs_node romfs_node; + +struct romfs_dirent; +typedef struct romfs_dirent romfs_dirent; + +//========================================================================== +// File and directory node +// This data structure represents a file or directory. + +struct romfs_node +{ + cyg_uint32 mode; // 0-3 node type + cyg_ucount32 nlink; // 4-7 number of links to this node + cyg_uint16 uid; // 8-9 Owner id + cyg_uint16 gid; // 10-11 Group id + cyg_uint32 size; // 12-15 size of file in bytes + cyg_uint32 ctime; // 16-19 creation status time + cyg_uint32 offset; // 20-23 offset of data from start of ROMFS + cyg_uint32 pad[2]; // 24-31 padding to align to 32byte boundary +}; + +//========================================================================== +// Directory entry. +// Variable sized entry containing the name and node of a directory entry + +struct romfs_dirent +{ + cyg_ucount32 node; // Index of node in romfs_disk structure + cyg_uint32 next; // Offset from start of directory of + // a) the next entry, or + // b) the end of the directory data + char name[0]; // The name, NUL terminated +}; + +//========================================================================== +// ROMFS header +// This data structure contains overall information on the ROMFS + +struct romfs_disk +{ + cyg_uint32 magic; // 0-3 Marks a valid ROMFS entry + cyg_ucount32 nodecount; // 4-7 Count of nodes in this filesystem + cyg_ucount32 disksize; // 8-11 Count of bytes in this filesystem + cyg_uint32 dev_id; // 12-15 ID of disk (put into stat.st_dev) + char name[16]; // 16-31 Name - pads to 32 bytes + romfs_node node[0]; +}; + +#define ROMFS_MAGIC 0x526f6d2e // The magic signature word for a romfs +#define ROMFS_CIGAM 0x2e6d6f52 // The byte sex is wrong if you see this + +//========================================================================== +// Directory search data +// Parameters for a directory search. The fields of this structure are +// updated as we follow a pathname through the directory tree. + +struct romfs_dirsearch +{ + romfs_disk *disk; // disk structure + romfs_node *dir; // directory to search + const char *path; // path to follow + romfs_node *node; // Node found + const char *name; // last name used + int namelen; // name fragment length + cyg_bool last; // last name in path? +}; + +typedef struct romfs_dirsearch romfs_dirsearch; + +//========================================================================== +// This seems to be the only string function referenced. Define as static +// here to avoid having to load the string library + +static bool match( const char *a, const char *b, int len ) +{ + for ( ; len > 0 && *a && *b && *a == *b ; a++, b++, len-- ) + ; + return ( len == 0 && *a == 0 ); +} + + +//========================================================================== +// SIMPLE buffer management. +// Each node has a data buffer pointer and a size. + +// ------------------------------------------------------------------------- +// findbuffer_node() +// return a pointer to the data at the indicated file position. + +static int findbuffer_node( romfs_disk *disk, // header pointer + romfs_node *node, // node pointer + off_t pos, // data position to get + cyg_uint8 **buffer, // returned buffer pointer + size_t *size) // returned buffer size +{ + if ( pos >= node->size || node->size == 0 ) + { + // Indicate end of data. + *size = 0; + } else { + + // Calculate the buffer position + *buffer = (cyg_uint8*)disk + node->offset + pos; + *size = node->size-pos; + } + + return ENOERR; +} + +//========================================================================== +// Directory operations + + +// ------------------------------------------------------------------------- +// find_direntry() +// Find a directory entry for the name and return a pointer to the first +// entry fragment. + +static romfs_dirent *find_direntry( romfs_disk *disk, romfs_node *dir, const char *name, int namelen ) +{ + off_t pos = 0; + int err; + + // Loop over all the entries until a match is found or we run out + // of data. + while( pos < dir->size ) + { + romfs_dirent *d; + cyg_uint8 *buf; + size_t size; + + err = findbuffer_node( disk, dir, pos, &buf, &size ); + if( err != ENOERR || size == 0) + return NULL; + + d = (romfs_dirent *)buf; + + // Is this the directory entry we're looking for? + if ( match( d->name, name, namelen ) ) + return d; + + // Otherwise move on to next entry in chain + pos = d->next; + } + + return NULL; +} + +//========================================================================== +// Directory search + +// ------------------------------------------------------------------------- +// init_dirsearch() +// Initialize a dirsearch object to start a search + +static void init_dirsearch( romfs_dirsearch *ds, + romfs_disk *disk, + romfs_node *dir, + const char *name) +{ + ds->disk = disk; + ds->dir = dir; + ds->path = name; + ds->node = dir; + ds->name = name; + ds->namelen = 0; + ds->last = false; +} + +// ------------------------------------------------------------------------- +// find_entry() +// Search a single directory for the next name in a path and update the +// dirsearch object appropriately. + +static int find_entry( romfs_dirsearch *ds ) +{ + romfs_node *dir = ds->dir; + const char *name = ds->path; + const char *n = name; + int namelen = 0; + romfs_dirent *d; + + // check that we really have a directory + if( !S_ISDIR(dir->mode) ) + return ENOTDIR; + + // Isolate the next element of the path name. + while( *n != '\0' && *n != '/' ) + n++, namelen++; + + // Check if this is the last path element. + while( *n == '/') n++; + if( *n == '\0' ) + ds->last = true; + + // update name in dirsearch object + ds->name = name; + ds->namelen = namelen; + + // Here we have the name and its length set up. + // Search the directory for a matching entry + + d = find_direntry( ds->disk, dir, name, namelen ); + + if( d == NULL ) + return ENOENT; + + // pass back the node we have found + ds->node = &ds->disk->node[d->node]; + + return ENOERR; + +} + +// ------------------------------------------------------------------------- +// romfs_find() +// Main interface to directory search code. This is used in all file +// level operations to locate the object named by the pathname. + +static int romfs_find( romfs_dirsearch *d ) +{ + int err; + + // Short circuit empty paths + if( *(d->path) == '\0' ) + return ENOERR; + + // iterate down directory tree until we find the object + // we want. + for(;;) + { + err = find_entry( d ); + + if( err != ENOERR ) + return err; + + if( d->last ) + return ENOERR; + + // Update dirsearch object to search next directory. + d->dir = d->node; + d->path += d->namelen; + while( *(d->path) == '/' ) d->path++; // skip dirname separators + } +} + +//========================================================================== +// Pathconf support +// This function provides support for pathconf() and fpathconf(). + +static int romfs_pathconf( romfs_node *node, struct cyg_pathconf_info *info ) +{ + int err = ENOERR; + + switch( info->name ) + { + case _PC_LINK_MAX: + info->value = LINK_MAX; + break; + + case _PC_MAX_CANON: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_MAX_INPUT: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_NAME_MAX: + info->value = NAME_MAX; + break; + + case _PC_PATH_MAX: + info->value = PATH_MAX; + break; + + case _PC_PIPE_BUF: + info->value = -1; // not supported + err = EINVAL; + break; + + + case _PC_ASYNC_IO: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_CHOWN_RESTRICTED: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_NO_TRUNC: + info->value = 0; + break; + + case _PC_PRIO_IO: + info->value = 0; + break; + + case _PC_SYNC_IO: + info->value = 0; + break; + + case _PC_VDISABLE: + info->value = -1; // not supported + err = EINVAL; + break; + + default: + err = EINVAL; + break; + } + + return err; +} + +//========================================================================== +// Filesystem operations + +// ------------------------------------------------------------------------- +// romfs_mount() +// Process a mount request. This mainly finds root for the +// filesystem. + +static int romfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ) +{ + romfs_disk *disk=NULL; + + if ( !mte->data ) { + // If the image address was not in the MTE data word, + if ( mte->devname && mte->devname[0] ) { + // If a device name specified, lookup flash block device. +#if defined(CYGFUN_FS_ROM_FLASH_BLOCK_DEVICE_LOOKUP) + if ( mte->devname[0] == '/' ) { + Cyg_ErrNo err; + cyg_io_handle_t t; + cyg_io_flash_getconfig_devaddr_t d; + int len; + err = cyg_io_lookup(mte->devname, &t); + if (err != ENOERR) { + return -err; + } + len = sizeof(d); + err = cyg_io_get_config(t, CYG_IO_GET_CONFIG_FLASH_DEVADDR, &d, &len); + if (err != ENOERR) { + return -err; + } + disk = (romfs_disk *) d.dev_addr; + } else +#endif + { + char *addr; + // And there's something in the 'hardware device' field, + // then read the address from there. + sscanf( mte->devname, "%p", &addr ); + disk = (romfs_disk *) addr; + } + } + } else { + disk = (romfs_disk *)mte->data; + } + + if ( !disk ) { + // If still no address, try the FSTAB entry data word + disk = (romfs_disk *)fste->data; + } + + if ( !disk ) { + // If still no address, give up... + return ENOENT; + } + + + + // Check the ROMFS magic number to ensure that there's really an fs. + if ( disk->magic == ROMFS_CIGAM ) { + // The disk image has the wrong byte sex!!! + return EIO; + } else if ( disk->magic != ROMFS_MAGIC || disk->nodecount == 0 ) { + // No image found + return ENOENT; + } + + mte->root = (cyg_dir)&disk->node[0]; + + mte->data = (CYG_ADDRWORD)disk; + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_umount() +// Unmount the filesystem. This will currently always succeed. + +static int romfs_umount ( cyg_mtab_entry *mte ) +{ + // Clear root pointer + mte->root = CYG_DIR_NULL; + + // That's all folks. + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_open() +// Open a file for reading + +static int romfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int mode, cyg_file *file ) +{ + + romfs_dirsearch ds; + romfs_node *node = NULL; + int err; + + init_dirsearch( &ds, (romfs_disk *)mte->data, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err == ENOENT ) + { + return ENOENT; + } + else if( err == ENOERR ) + { + // The node exists. If the O_CREAT and O_EXCL bits are set, we + // must fail the open. + + if( (mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) ) + err = EEXIST; + else node = ds.node; + } + + if( err == ENOERR && (mode & O_TRUNC ) ) + { + // If the O_TRUNC bit is set we must fail the open + + err = EPERM; + } + + if( err != ENOERR ) return err; + + // Check that we actually have a file here + if( S_ISDIR(node->mode) ) return EISDIR; + + // Initialize the file object + + file->f_flag |= mode & CYG_FILE_MODE_MASK; + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &romfs_fileops; + file->f_offset = 0; + file->f_data = (CYG_ADDRWORD)node; + file->f_xops = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_opendir() +// Open a directory for reading. + +static int romfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_file *file ) +{ + romfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (romfs_disk *)mte->data, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err != ENOERR ) return err; + + // check it is really a directory. + if( !S_ISDIR(ds.node->mode) ) return ENOTDIR; + + // Initialize the file object, setting the f_ops field to a + // special set of file ops. + + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &romfs_dirops; + file->f_offset = 0; + file->f_data = (CYG_ADDRWORD)ds.node; + file->f_xops = 0; + + return ENOERR; + +} + +// ------------------------------------------------------------------------- +// romfs_chdir() +// Change directory support. + +static int romfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_dir *dir_out ) +{ + if( dir_out != NULL ) + { + // This is a request to get a new directory pointer in + // *dir_out. + + romfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (romfs_disk *)mte->data, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err != ENOERR ) return err; + + // check it is a directory + if( !S_ISDIR(ds.node->mode) ) + return ENOTDIR; + + // Pass it out + *dir_out = (cyg_dir)ds.node; + } + // If no output dir is required, this means that the mte and + // dir arguments are the current cdir setting and we should + // forget this fact. Do nothing in ROMFS. + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_stat() +// Get struct stat info for named object. + +static int romfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + struct stat *buf) +{ + romfs_dirsearch ds; + int err; + romfs_disk *disk = (romfs_disk *)mte->data; + + init_dirsearch( &ds, disk, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err != ENOERR ) return err; + + // Fill in the status + buf->st_mode = ds.node->mode; + buf->st_ino = (ino_t)(ds.node - &disk->node[0]); + buf->st_dev = (dev_t)disk->dev_id; + buf->st_nlink = ds.node->nlink; + buf->st_uid = ds.node->uid; + buf->st_gid = ds.node->gid; + buf->st_size = ds.node->size; + buf->st_atime = ds.node->ctime; + buf->st_mtime = ds.node->ctime; + buf->st_ctime = ds.node->ctime; + + return err; +} + +// ------------------------------------------------------------------------- +// romfs_getinfo() +// Getinfo. Currently only support pathconf() and file system block usage + +static int romfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ) +{ + romfs_dirsearch ds; + int err; + + init_dirsearch( &ds, (romfs_disk *)mte->data, (romfs_node *)dir, name ); + + err = romfs_find( &ds ); + + if( err != ENOERR ) return err; + + switch( key ) + { + case FS_INFO_CONF: + err = romfs_pathconf( ds.node, (struct cyg_pathconf_info *)buf ); + break; +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) + case FS_INFO_BLOCK_USAGE: { + struct cyg_fs_block_usage *usage = (struct cyg_fs_block_usage *) buf; + struct romfs_disk *disk = (struct romfs_disk*) mte->data; + usage->total_blocks = disk->disksize; + usage->free_blocks = 0; + usage->block_size = 1; + return ENOERR; + } +#endif + default: + err = EINVAL; + } + return err; +} + +// ------------------------------------------------------------------------- +// romfs_setinfo() +// Setinfo. Nothing to support here at present. + +static int romfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ) +{ + // No setinfo keys supported at present + + return EINVAL; +} + + +//========================================================================== +// File operations + +// ------------------------------------------------------------------------- +// romfs_fo_read() +// Read data from the file. + +static int romfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + romfs_node *node = (romfs_node *)fp->f_data; + int i; + off_t pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + + // Loop over the io vectors until there are none left + for( i = 0; i < uio->uio_iovcnt; i++ ) + { + cyg_iovec *iov = &uio->uio_iov[i]; + char *buf = (char *)iov->iov_base; + off_t len = iov->iov_len; + + // Loop over each vector filling it with data from the file. + while( len > 0 && pos < node->size ) + { + cyg_uint8 *fbuf; + size_t bsize; + off_t l = len; + int err; + + // Get a pointer to the data at offset _pos_. + err = findbuffer_node( (romfs_disk *)fp->f_mte->data, node, pos, &fbuf, &bsize ); + + if( err != ENOERR ) + return err; + + // adjust size to end of file if necessary + if( l > node->size-pos ) + l = node->size-pos; + + // adjust size to the amount of contiguous data we can see + // at present. + if( l > bsize ) + l = bsize; + + // copy data out + memcpy( buf, fbuf, l ); + + // Update working vars + len -= l; + buf += l; + pos += l; + resid -= l; + } + } + + // We successfully read some data + // Update the file offset and transfer residue. + + uio->uio_resid = resid; + fp->f_offset = pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_fo_lseek() +// Seek to a new file position. + +static int romfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *apos, int whence ) +{ + romfs_node *node = (romfs_node *)fp->f_data; + off_t pos = *apos; + + switch( whence ) + { + case SEEK_SET: + // Pos is already where we want to be. + break; + + case SEEK_CUR: + // Add pos to current offset. + pos += fp->f_offset; + break; + + case SEEK_END: + // Add pos to file size. + pos += node->size; + break; + + default: + return EINVAL; + } + + // Check that pos is still within current file size, or at the + // very end. + if( pos < 0 || pos > node->size ) + return EINVAL; + + // All OK, set fp offset and return new position. + *apos = fp->f_offset = pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_fo_ioctl() +// Handle ioctls. Currently none are defined. + +static int romfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data) +{ + // No Ioctls currenly defined. + + return EINVAL; +} + +// ------------------------------------------------------------------------- +// romfs_fo_fsync(). +// Force the file out to data storage. + +static int romfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ) +{ + // Data is always permanently where it belongs, nothing to do + // here. + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_fo_close() +// Close a file. We just clear out the data pointer. + +static int romfs_fo_close (struct CYG_FILE_TAG *fp) +{ + fp->f_data = 0; // zero data pointer + + return ENOERR; +} + +// ------------------------------------------------------------------------- +//romfs_fo_fstat() +// Get file status. + +static int romfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ) +{ + romfs_node *node = (romfs_node *)fp->f_data; + romfs_disk *disk = (romfs_disk*)(fp->f_mte->data); + + // Fill in the status + buf->st_mode = node->mode; + buf->st_ino = (ino_t)(node - &disk->node[0]); + buf->st_dev = disk->dev_id; + buf->st_nlink = node->nlink; + buf->st_uid = node->uid; + buf->st_gid = node->gid; + buf->st_size = node->size; + buf->st_atime = node->ctime; + buf->st_mtime = node->ctime; + buf->st_ctime = node->ctime; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_fo_getinfo() +// Get info. Currently only supports fpathconf(). + +static int romfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) +{ + romfs_node *node = (romfs_node *)fp->f_data; + int err; + + switch( key ) + { + case FS_INFO_CONF: + err = romfs_pathconf( node, (struct cyg_pathconf_info *)buf ); + break; + + default: + err = EINVAL; + } + return err; +} + +// ------------------------------------------------------------------------- +// romfs_fo_setinfo() +// Set info. Nothing supported here. + +static int romfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) +{ + // No setinfo key supported at present + + return ENOERR; +} + + +//========================================================================== +// Directory operations + +// ------------------------------------------------------------------------- +// romfs_fo_dirread() +// Read a single directory entry from a file. + +static int romfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + romfs_node *dir = (romfs_node *)fp->f_data; + off_t pos = fp->f_offset; + int err = ENOERR; + struct dirent *ent = (struct dirent *)uio->uio_iov[0].iov_base; + char *nbuf = ent->d_name; + int nlen = sizeof(ent->d_name)-1; + off_t len = uio->uio_iov[0].iov_len; + romfs_dirent *d = NULL; + cyg_uint8 *buf; + size_t size; + int i; + + if( len < sizeof(struct dirent) ) + return EINVAL; + + // Get the next entry + err = findbuffer_node( (romfs_disk *)fp->f_mte->data, dir, pos, &buf, &size ); + if( err != ENOERR || size == 0 || pos >= dir->size ) + return err; + + d = (romfs_dirent *)buf; + + for ( i = 0 ; i < nlen && d->name[i] ; i++, nbuf++ ) + *nbuf = d->name[i]; +#ifdef CYGPKG_FS_ROM_RET_DIRENT_DTYPE + ent->d_type = (((romfs_disk *)fp->f_mte->data)->node[d->node]).mode; +#endif + + // A successful read. Terminate the entry name with a NUL, set the + // residue and set the file offset to restart at the next + // directory entry. + + *nbuf = '\0'; + uio->uio_resid -= sizeof(struct dirent); + fp->f_offset = d->next; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// romfs_fo_dirlseek() +// Seek directory to start. + +static int romfs_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ) +{ + // Only allow SEEK_SET to zero + + if( whence != SEEK_SET || *pos != 0) + return EINVAL; + + *pos = fp->f_offset = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// EOF romfs.c diff --git a/ecos/packages/fs/rom/current/support/Makefile b/ecos/packages/fs/rom/current/support/Makefile new file mode 100644 index 0000000..985e8fc --- /dev/null +++ b/ecos/packages/fs/rom/current/support/Makefile @@ -0,0 +1,38 @@ +#========================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## This exception does not invalidate any other reasons why a work based +## on this file might be covered by the GNU General Public License. +## ------------------------------------------- +## ####ECOSGPLCOPYRIGHTEND#### +#========================================================================== + +CC = gcc +CFLAGS = -ggdb -Wall + +mk_romfs: diff --git a/ecos/packages/fs/rom/current/support/file2c.tcl b/ecos/packages/fs/rom/current/support/file2c.tcl new file mode 100755 index 0000000..1313c6d --- /dev/null +++ b/ecos/packages/fs/rom/current/support/file2c.tcl @@ -0,0 +1,118 @@ +#!/usr/bin/env tclsh + +#=============================================================================== +# +# file2c.tcl +# +# Convert a file into a header that can be #included from C. +# +#=============================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## This exception does not invalidate any other reasons why a work based +## on this file might be covered by the GNU General Public License. +## ------------------------------------------- +## ####ECOSGPLCOPYRIGHTEND#### +#=============================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): jlarmour,bartv +# Contact(s): +# Date: 2001-07-20 +# Purpose: +# Description: +# Usage: file2c.tcl <file to encode> <output C header file> +# +#####DESCRIPTIONEND#### +#=============================================================================== + + + +if { $argc != 2 } { + puts "Usage: file2c.tcl <file to encode> <output C header file>" + exit 1 +} +set infile [lindex $argv 0] +set outfile [lindex $argv 1] + +set status [ catch { + set infilefd [open $infile "r"] + fconfigure $infilefd -translation binary + set data [read $infilefd] + close $infilefd +} message] + +if { $status != 0 } { + error "Unable to read file $infile: $message" +} + +set result "" + +set status [ catch { + set outfilefd [ open $outfile "w" ] +} message ] + +if { $status != 0 } { + error "Unable to create file $outfile: $message" +} + +append result "/* This is a generated file. Do not edit. */\n\n" +append result "static CYGBLD_ATTRIB_ALIGN(4) const unsigned char filedata\[\] = {\n" + +set datalength [ string length $data ] + +set aligned_datalength [expr $datalength - ($datalength % 8)] + +for { set i 0 } {$i < $aligned_datalength} {incr i 8} { + binary scan $data "@[set i]H16" var0 + append result [format " 0x%2s, 0x%2s, 0x%2s, 0x%2s, 0x%2s, 0x%2s, 0x%2s, 0x%2s,\n" \ + [string range $var0 0 1] \ + [string range $var0 2 3] \ + [string range $var0 4 5] \ + [string range $var0 6 7] \ + [string range $var0 8 9] \ + [string range $var0 10 11] \ + [string range $var0 12 13] \ + [string range $var0 14 15]] +} + +if { $aligned_datalength != $datalength } { + append result " " + for { set i $aligned_datalength } {$i < $datalength} {incr i} { + binary scan $data "@[set i]H2" var0 + append result [format "0x%2s, " $var0] + } +} + +# Remove either comma+newline or comma+space from the end +set result [string range $result 0 [expr [string length $result] - 3]] + +append result "\n};" + +puts $outfilefd $result +close $outfilefd diff --git a/ecos/packages/fs/rom/current/support/mk_romfs.c b/ecos/packages/fs/rom/current/support/mk_romfs.c new file mode 100644 index 0000000..6bde153 --- /dev/null +++ b/ecos/packages/fs/rom/current/support/mk_romfs.c @@ -0,0 +1,744 @@ +//========================================================================== +// +// mk_romfs.c +// +// Create ROM file system image +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2010 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): richard.panton@3glab.com +// Contributors: richard.panton@3glab.com +// Date: 2000-07-25 +// Purpose: ROM file system +// Description: This program creates a ROM file system image, suitable +// for use with the sample ROM file system implemented by +// this package. +// * CAUTION! * This is host code and can only be built +// in a host, e.g. Linux, environment. +// +//####DESCRIPTIONEND#### +//========================================================================== + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> + +//========================================================================== +// +// CONFIGURABLE ITEMS HERE +// +//========================================================================== + +// define LONG to be a four byte unsigned integer on the host +#define LONG uint32_t + +// define SHORT to be a two byte unsigned integer on the host +#define SHORT uint16_t + +// All data files should be aligned to this sized boundary (minimum probably 32) +#define DATA_ALIGN 32 + +// The data stored in a directory should be aligned to this size boundary +#define DIRECTORY_ALIGN 32 + +// All executable files should be aligned to this sized boundary (minimum probably 32) +#define EXEC_ALIGN 32 + +// Undefine this if the host filesystem does not support lstat() +#define HAS_LSTAT + +// Win32 definitions +#ifdef _WIN32 +#undef HAS_LSTAT +#define S_ISLNK(m) (0) +typedef unsigned int uid_t; +typedef unsigned int gid_t; +#endif + +//========================================================================== + +// Return (n) aligned to the next (b) byte boundary +#define ALIGN_TO( n, b ) (( (n) + (b)-1 ) & ~((b)-1)) + +// Set the stat call to use +#ifdef HAS_LSTAT +#define get_status( p, b ) lstat( (p), (b) ) +#else +#define get_status( p, b ) stat( (p), (b) ) +#endif + +// This is how we identify a directory from its mode +#define IS_DIRECTORY( m ) (S_ISDIR(m)) + +// This is how we identify a data file from its mode +#define IS_DATAFILE( m ) (S_ISREG(m) && ((m)&S_IXUSR) == 0 ) + +// This is how we identify an executable from its mode +#define IS_EXECUTABLE( m ) (S_ISREG(m) && ((m)&S_IXUSR) != 0 ) + +// This is how we identify a symbolic link from its mode +#define IS_SYMLINK( m ) (S_ISLNK(m)) + +#define ROMFS_MAGIC 0x526f6d2e + +//========================================================================= +// EXIT CODES +#define EXIT_SUCCESS 0 +#define EXIT_ARGS 1 +#define EXIT_MALLOC 2 +#define EXIT_FILESYS 3 +#define EXIT_WRITE 4 +#define EXIT_SEEK 5 +#define EXIT_COMPILE 6 +#define EXIT_BUG 7 + + + +// These are the structures we will build into the ROMFS image. +// The sizes of these structures should be fixed for all architectures +typedef struct romfs_dirent { + LONG node; // 4 + LONG next; // 8 + char name[0]; // 8 + strlen(name) + 1 +} romfs_dirent; // Aligns to next 32 byte boundary + +typedef struct romfs_node { + LONG mode; // 4 + LONG nlink; // 8 + SHORT uid; // 10 + SHORT gid; // 12 + LONG size; // 16 + LONG ctime; // 20 + LONG data_offset; // 24 + char pad[8]; // 32 +} romfs_node; // Next node begins here + +typedef struct romfs_disk { + LONG magic; // 4 + LONG nodecount; // 8 + LONG disksize; // 12 + LONG dev_id; // 16 + char name[16]; // 32 +} romfs_disk; // Nodes start here + +// This is the holding structure for a node +typedef struct node { + const char *path; // Filename (inc. path) of a link to this node + size_t size; // Size of file/directory/link + mode_t st_mode; // Type and permissions + uid_t uid; // Owner id + gid_t gid; // Group id + time_t ctime; // File creation time + int nodenum; // Nodenumber of this node in the ROMFS image + dev_t device; // Device (for hardlink check) + ino_t inode; // Inode (for hardlink check) + int nlink; // [DIRECTORIES] Number of sub-directories [FILES] hard links + romfs_dirent *entry; // [DIRECTORIES] Points to an array of directory entries + int entry_size; // Size to be allocated to file (includes alignment bytes) + unsigned long offset; // Offset within ROMFS image of data + struct node *sibling; // Points to the next entry in this directory + struct node *child; // [DIRECTORIES] Points to any subdirectories + struct node *next_in_rom; // Next in ROMFS write order + struct node *next_multilink;// Next node that is multilinked +} node; + +static int nodes = 0; +static char *prog; +static int verbose = 1; +static int dowrite = 1; +static int bigendian = 0; +static int hardlinks = 0; +static unsigned long coffset = 0; +static node * first = NULL; +static node ** last_p = &first; +static node * first_multilink = NULL; +static node ** last_multilink_p = &first_multilink; +static int fd = -1; + +#define VERB_NONE 0 +#define VERB_MINIMUM 1 +#define VERB_SUB 2 +#define VERB_MAX 3 +#define VERB_EXCESSIVE 4 + +// Use gcc format argument checking on this function, which cannot return +static void fatal_error( int exitcode, const char *fmt, ... ) \ + __attribute__ (( noreturn,format (printf, 2, 3) )); + +// Use gcc format argument checking on this function +static void verb_printf( int level, const char *fmt, ... ) \ + __attribute__ ((format (printf, 2, 3) )); + +static void fatal_error( int exitcode, const char *fmt, ... ) { + va_list v; + + va_start( v, fmt ); + vfprintf( stderr, fmt, v ); + + exit(exitcode); +} + +static void verb_printf( int level, const char *fmt, ... ){ + if ( level <= verbose ) { + va_list v; + va_start( v,fmt ); + vprintf(fmt, v); + } +} + +static void *mymalloc( size_t size ) { + void *p = malloc(size); + if ( !p ) { + fatal_error( EXIT_MALLOC, "Out of memory allocating %d bytes\n", size ); + } + return p; +} + +static void myrealloc( void **o, size_t newsize ) { + if ( *o == NULL ) + *o = mymalloc( newsize ); + else if ( !(*o = realloc( *o, newsize )) ) { + fatal_error( EXIT_MALLOC, "Out of memory re-allocating %d bytes\n", newsize ); + } +} + +static void outputlong( unsigned char *b, LONG w ) { + if ( bigendian ) { + b[0] = (w>>24) & 0xff; + b[1] = (w>>16) & 0xff; + b[2] = (w>> 8) & 0xff; + b[3] = (w ) & 0xff; + } else { + b[3] = (w>>24) & 0xff; + b[2] = (w>>16) & 0xff; + b[1] = (w>> 8) & 0xff; + b[0] = (w ) & 0xff; + } +} + +static void outputshort( unsigned char *b, SHORT w ) { + if ( bigendian ) { + b[0] = (w>> 8) & 0xff; + b[1] = (w ) & 0xff; + } else { + b[1] = (w>> 8) & 0xff; + b[0] = (w ) & 0xff; + } +} + +static unsigned long ConvertMode( unsigned long posix_mode ) { + unsigned long result = 0; + if ( S_ISDIR( posix_mode ) ) result |= 1<<0; + if ( S_ISCHR( posix_mode ) ) result |= 1<<1; + if ( S_ISBLK( posix_mode ) ) result |= 1<<2; + if ( S_ISREG( posix_mode ) ) result |= 1<<3; + if ( S_ISFIFO(posix_mode ) ) result |= 1<<4; + // We cannot create MQ, SEM, or SHM entries here + if ( posix_mode & S_IRUSR ) result |= 1<<16; + if ( posix_mode & S_IWUSR ) result |= 1<<17; + if ( posix_mode & S_IXUSR ) result |= 1<<18; +#ifndef _WIN32 + if ( posix_mode & S_IRGRP ) result |= 1<<19; + if ( posix_mode & S_IWGRP ) result |= 1<<20; + if ( posix_mode & S_IXGRP ) result |= 1<<21; + if ( posix_mode & S_IROTH ) result |= 1<<22; + if ( posix_mode & S_IWOTH ) result |= 1<<23; + if ( posix_mode & S_IXOTH ) result |= 1<<24; + if ( posix_mode & S_ISUID ) result |= 1<<25; + if ( posix_mode & S_ISGID ) result |= 1<<26; +#endif + return result; +} + +static const char *AddDirEntry( const char *name, node *parent_node, int node_num ) { + int this_size = ((strlen(name) + 4 + 4 + 1) + 31) & ~31; + int start = parent_node->size; + romfs_dirent *g; + myrealloc( (void**)&parent_node->entry, (parent_node->size += this_size) ); + g = (romfs_dirent *)((unsigned char *)parent_node->entry + start); + memset( (void*)g, '\0', this_size ); + outputlong( (unsigned char*)&g->node, node_num); + outputlong( (unsigned char*)&g->next, parent_node->size); + strcpy(g->name,name); + verb_printf( VERB_MAX, "\t%s --> node %d\n", name, node_num ); + return (const char *)g->name; +} + +extern int errno; + +static node * FindLink( dev_t d, ino_t i ) { + // See if the node has been previously included by checking the device/inode + // combinations of all known multi-linked nodes + node *np = first_multilink; + + for ( ; np ; np = np->next_multilink ) { + if ( np->device == d && np->inode == i ) + return np; + } + return NULL; +} + +static node * GetNodeInfo( const char *path, const char *name, int *hlink ) { + char newpath[1024]; + node *node, *lnode; + struct stat stbuff; + + sprintf(newpath,"%s/%s",path,name); + if ( (get_status(newpath,&stbuff)) < 0 ) { + fatal_error(EXIT_FILESYS, "stat(%s) failed: %s\n", newpath, strerror(errno)); + } + if ( !(stbuff.st_mode & S_IRUSR) ) { + fatal_error(EXIT_FILESYS, "\"%s\" is not readable\n", newpath ); + } + if ( hardlinks && S_ISREG( stbuff.st_mode ) && stbuff.st_nlink > 1 ) { + + // See if this node has already been loaded + lnode = FindLink( stbuff.st_dev, stbuff.st_ino ); + + if ( lnode ) { + lnode->nlink++; + *hlink = 1; + return lnode; // Return the found link instead + } + + // Create a new node + node = mymalloc( sizeof(struct node) ); + + // Incorporate the new link into the 'multi-linked' node list + *last_multilink_p = node; + last_multilink_p = &node->next_multilink; + } else { + // Create a new node + node = mymalloc( sizeof(struct node) ); + } + node->path = strdup( newpath ); + // We re-calculate the size for directories + node->size = IS_DIRECTORY( stbuff.st_mode ) ? 0 : stbuff.st_size; + node->st_mode = stbuff.st_mode; + node->uid = stbuff.st_uid; + node->gid = stbuff.st_gid; + node->ctime = stbuff.st_ctime; + node->nodenum = nodes++; + node->device = stbuff.st_dev; + node->inode = stbuff.st_ino; + // We always re-calculate the number of links + node->nlink = IS_DIRECTORY( stbuff.st_mode ) ? 2 : 1; + node->entry = NULL; + node->entry_size = 0; + node->offset = 0; + node->sibling = NULL; + node->child = NULL; + node->next_in_rom = NULL; + node->next_multilink = NULL; + *hlink = 0; + return node; +} + +static void ScanDirectory(node *mynode, int p_node) { + + DIR *dh; + struct dirent *e; + node **last_p = &mynode->child; + node *th; + int was_hardlinked; + + if ( (dh = opendir( mynode->path )) == NULL ) { + perror(mynode->path); + return; + } + + verb_printf(VERB_EXCESSIVE, "Construct directory '%s'(%d):\n", + mynode->path, mynode->nodenum ); + + // Add . & .. here because they MUST be present in the image + AddDirEntry( ".", mynode, mynode->nodenum ); + AddDirEntry( "..", mynode, p_node ); + + while ( (e = readdir( dh )) ) { + // Ignore . & .. here because they MAY NOT be in the host filesystem + if ( strcmp(e->d_name,".") && strcmp(e->d_name,"..") ) { + + + th = GetNodeInfo( mynode->path, e->d_name, &was_hardlinked ); + AddDirEntry( e->d_name, mynode, th->nodenum ); + + if ( !was_hardlinked ) { + verb_printf( VERB_EXCESSIVE, "\t\tNew node %d for entry '%s'\n", th->nodenum, e->d_name); + *last_p = th; + last_p = &th->sibling; + } else { + verb_printf( VERB_EXCESSIVE, "\t\tRe-used node %d for entry '%s'\n", th->nodenum, e->d_name); + } + } + } + closedir( dh ); + verb_printf(VERB_EXCESSIVE,"Completed '%s'. Checking for child directories...\n", mynode->path); + + for ( th = mynode->child ; th ; th = th->sibling ) { + if ( IS_DIRECTORY( th->st_mode ) ) { + mynode->nlink++; + ScanDirectory( th, mynode->nodenum ); + } + } +} + +static void AllocateSpaceToDirectories( node *first ) { + node *np; + + for ( np = first ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) ) { + // The first node is a directory. Add its data + np->offset = coffset; + np->entry_size = ALIGN_TO( np->size, DIRECTORY_ALIGN ); + coffset += np->entry_size; + + verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n", + np->nodenum, np->offset, np->entry_size ); + + // Link this node into the write order chain. + // For node 0 (the root), this will overwrite the first pointer with itself + *last_p = np; + last_p = &np->next_in_rom; + } + } + + // Now add any child directories + for ( np = first ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) && np->child ) + AllocateSpaceToDirectories( np->child ); + } +} + +static void AllocateSpaceToDataFiles( node *first ) { + node *np; + + // There are two loops below. It CAN be done in just one, but this re-orders + // the file positions in relation to their inode numbers. To keep it simple + // to check, allocation takes place in the first loop, recursion in the second + + // Search for child data files + for ( np = first->child ; np ; np = np->sibling ) { + if ( IS_DATAFILE( np->st_mode ) || IS_SYMLINK( np->st_mode ) ) { + np->offset = coffset; + np->entry_size = ALIGN_TO( np->size, DATA_ALIGN ); + coffset += np->entry_size; + + // Link in to the rom write order list + *last_p = np; + last_p = &np->next_in_rom; + + verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n", + np->nodenum, np->offset, np->entry_size ); + } + } + + // Recurse into sub-directories + for ( np = first->child ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) ) { + AllocateSpaceToDataFiles( np ); + } + } +} + +static void AllocateSpaceToExecutables( node *first ) { + node *np; + + // The first node is a directory. Don't bother with that... + + // Search for child executables + for ( np = first->child ; np ; np = np->sibling ) { + if ( IS_EXECUTABLE( np->st_mode ) ) { + np->offset = coffset; + np->entry_size = ALIGN_TO( np->size, EXEC_ALIGN ); + coffset += np->entry_size; + + // Link in to the rom write order list + *last_p = np; + last_p = &np->next_in_rom; + + verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n", + np->nodenum, np->offset, np->entry_size ); + } + } + + // Recurse into sub-directories + for ( np = first->child ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) ) { + AllocateSpaceToExecutables( np ); + } + } +} + +static void WriteNode( int fd, node *np ) { + romfs_node anode; + char padhere[9]; + outputlong( (unsigned char*) &anode.mode, ConvertMode( np->st_mode ) ); + outputlong( (unsigned char*) &anode.nlink, np->nlink ); + outputshort((unsigned char*) &anode.uid, np->uid ); + outputshort((unsigned char*) &anode.gid, np->gid ); + outputlong( (unsigned char*) &anode.size, np->size ); + outputlong( (unsigned char*) &anode.ctime, np->ctime ); + outputlong( (unsigned char*) &anode.data_offset, np->offset ); + sprintf( padhere, "<%6d>", np->nodenum ); + memcpy( anode.pad, padhere, 8 ); + if ( dowrite && write( fd, (void*)&anode, sizeof(anode) ) != sizeof(anode) ) + fatal_error(EXIT_WRITE, "Error writing node %d (%s): %s\n", np->nodenum, np->path, strerror(errno) ); +} + +static int WriteNodeAndSiblings( int fd, int nodenum, node *first ) { + node *np; + + for ( np = first ; np ; np = np->sibling ) { + if ( np->nodenum != nodenum++ ) { + fatal_error(EXIT_BUG, "BUG: Out of sequence node number; got %d, expected %d\n", np->nodenum, nodenum-1); + } + WriteNode( fd, np ); + } + + for ( np = first ; np ; np = np->sibling ) { + if ( IS_DIRECTORY( np->st_mode ) && np->child ) { + nodenum = WriteNodeAndSiblings( fd, nodenum, np->child ); + } + } + return nodenum; +} + +static void WriteNodeTable( int fd ) { + romfs_disk header; + int wnodes; + + outputlong( (unsigned char*) &header.magic, ROMFS_MAGIC ); + outputlong( (unsigned char*) &header.nodecount, nodes ); + outputlong( (unsigned char*) &header.disksize, coffset ); + outputlong( (unsigned char*) &header.dev_id, 0x01020304 ); + strcpy( header.name, "ROMFS v1.0" ); + if ( dowrite && write( fd, (void*)&header, sizeof(header) ) != sizeof(header) ) + fatal_error(EXIT_WRITE, "Error writing ROMFS header: %s\n", strerror(errno) ); + + if ( (wnodes = WriteNodeAndSiblings( fd, 0, first )) != nodes ) { + fatal_error(EXIT_BUG, "BUG: Lost/gained some nodes; wrote %d, expected %d\n", wnodes, nodes ); + } +} + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static void WriteData( int fd, node *np ) { + char newpath[1024]; + int ffd; + unsigned long todo; + + if ( IS_SYMLINK( np->st_mode ) ) { + if ( (ffd = readlink( np->path, newpath, sizeof(newpath) )) < 0 ) + fatal_error(EXIT_FILESYS, "Error reading symlink \"%s\": %s\n", np->path, strerror(errno) ); + + if ( !dowrite ) return; + + if ( lseek( fd, np->offset, SEEK_SET ) != np->offset ) + fatal_error(EXIT_SEEK, "Error seeking to offset 0x%lX: %s\n", np->offset, strerror(errno) ); + + if ( write( fd, newpath, ffd ) != ffd ) + fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) ); + + return; + } + + if ( (ffd=open(np->path, O_RDONLY | O_BINARY )) < 0 ) + fatal_error(EXIT_FILESYS, "Error opening \"%s\": %s\n", np->path, strerror(errno) ); + + if ( dowrite && lseek( fd, np->offset, SEEK_SET ) != np->offset ) + fatal_error(EXIT_SEEK, "Error seeking to offset 0x%lX: %s\n", np->offset, strerror(errno) ); + + todo = np->size; + while ( todo >= 1024 ) { + if ( read( ffd, newpath, 1024 ) != 1024 ) + fatal_error(EXIT_FILESYS, "Error reading file \"%s\" at offset 0x%lX: %s\n", np->path, np->size - todo, strerror(errno) ); + if ( dowrite && write( fd, newpath, 1024 ) != 1024 ) + fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) ); + todo -= 1024; + } + + if ( todo ) { + if ( read( ffd, newpath, todo ) != todo ) + fatal_error(EXIT_FILESYS, "Error reading file \"%s\" at offset 0x%lX: %s\n", np->path, np->size - todo, strerror(errno) ); + if ( dowrite && write( fd, newpath, todo ) != todo ) + fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) ); + } + + close(ffd); + +} + +static void WriteDataBlocks( int fd, node *first ) { + for ( ; first ; first = first->next_in_rom ) { + if ( dowrite && lseek( fd, first->offset, SEEK_SET ) != first->offset ) + fatal_error(EXIT_SEEK, "Error seeking to offset 0x%lX: %s\n", first->offset, strerror(errno) ); + if ( IS_DIRECTORY( first->st_mode ) ) { + if ( dowrite && write( fd, first->entry, first->size ) != first->size ) + fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) ); + } else { + WriteData( fd, first ); + } + } +} + +static void usage(void) { + fprintf(stderr,"\n%s - Create an eCos ROMFS disk image from the files\n",prog); + fprintf(stderr,"%*s contained under a specified directory\n\n", strlen(prog), ""); + fprintf(stderr,"Usage: %s [options] <fs_root> <fs_file>\n", prog); + fprintf(stderr," fs_root is the directory containing the files to package into the ROMFS image\n"); + fprintf(stderr," fs_file is the name of the ROMFS image file to create\n"); + fprintf(stderr," Options include:\n"); + fprintf(stderr," -v / -q increase / decrease verbosity\n"); + fprintf(stderr," -n do everything EXCEPT creating the output file\n"); + fprintf(stderr," -b write a big-endian image (default is little endian)\n"); + fprintf(stderr," -l collapse hard links to a single node\n"); + fprintf(stderr,"\n"); + exit(EXIT_ARGS); +} + +int main(int ac, char *av[]) { + int dummy; + + prog = av[0]; + + // Check structure sizes + if (sizeof(romfs_node) != 32) { + fatal_error(EXIT_COMPILE , "Size of romfs_node is %d, NOT 32\n", sizeof(romfs_node) ); + } else if (sizeof(romfs_dirent) != 8) { + fatal_error(EXIT_COMPILE , "Size of romfs_dirent is %d, NOT 8\n", sizeof(romfs_dirent) ); + } else if (sizeof(romfs_disk) != 32) { + fatal_error(EXIT_COMPILE , "Size of romfs_disk is %d, NOT 32\n", sizeof(romfs_disk) ); + } + + // Parse option arguments + while ( ac > 1 && av[1][0] == '-' ) { + char *o = &av[1][1]; + for ( ; *o ; o++ ) { + switch ( *o ) { + case 'q' : + verbose--; + break; + case 'v' : + verbose++; + break; + case 'n' : + dowrite = 0; + break; + case 'b' : + bigendian = 1; + break; + case 'l' : + hardlinks = 1; + break; + default : + fprintf(stderr,"%s: Invalid flag -%c\n", prog, *o ); + usage(); + } + } + av++; ac--; + } + + // Check remaining arguments + if ( ac != 3 ) usage(); + + + verb_printf( VERB_MINIMUM, "%s: Verbosity %d %s%s endian\n", + prog, verbose, + dowrite ? "" : "no write, ", + bigendian ? "big" : "little" ); + + // Phase 1. Recursively scan the root directory for files and directories. + verb_printf(VERB_MINIMUM, "Phase 1 - Build file list\n"); + + first = GetNodeInfo( av[1], ".", &dummy ); // Initialize the root node entry. + ScanDirectory( first, 0 ); + + // Phase 2. Work out space allocations for filesystem + verb_printf(VERB_MINIMUM, "Phase 2 - Calculate space allocation\n"); + coffset = sizeof(romfs_disk) + nodes * sizeof(romfs_node); + verb_printf(VERB_MAX,"\t\tnode table : 0x000000 (+0x%05lX) %d nodes\n", coffset, nodes ); + + // Phase 2a. Work out space allocations for the directories of the filesystem + verb_printf(VERB_SUB,"Phase 2a - * Directories\n"); + coffset = ALIGN_TO( coffset, DIRECTORY_ALIGN ); + AllocateSpaceToDirectories( first ); + + // Phase 2b. Work out space allocations for the data files of the filesystem + verb_printf(VERB_SUB,"Phase 2b - * Regular files\n"); + coffset = ALIGN_TO( coffset, DATA_ALIGN ); + AllocateSpaceToDataFiles( first ); + + // Phase 2c. Work out space allocations for the executable files of the filesystem + verb_printf(VERB_SUB,"Phase 2c - * Executable files\n"); + coffset = ALIGN_TO( coffset, EXEC_ALIGN ); + AllocateSpaceToExecutables( first ); + + // Round off the image size... + coffset = ALIGN_TO( coffset, EXEC_ALIGN ); + + // Phase 3. Write out the image file + verb_printf(VERB_MINIMUM, "Phase 3 - Construct ROMFS image file (%ld kb)\n", ALIGN_TO( coffset, 1024 )/1024); + + if ( dowrite ) { + if ( (fd = open( av[2], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666 )) < 0 ) { + fatal_error(EXIT_WRITE,"Failed to open output file '%s', errno=%d\n", av[2], errno ); + } + } else { + verb_printf(VERB_NONE," (No image is being written)\n"); + } + + verb_printf(VERB_SUB,"Phase 3a - * Node table\n"); + WriteNodeTable( fd ); + + verb_printf(VERB_SUB,"Phase 3b - * Data blocks\n"); + WriteDataBlocks( fd, first ); + + if ( fd >= 0 ) close(fd); + + verb_printf(VERB_MINIMUM, "%s completed\n", av[2] ); + + return 0; +} diff --git a/ecos/packages/fs/rom/current/tests/romfs1.c b/ecos/packages/fs/rom/current/tests/romfs1.c new file mode 100644 index 0000000..dc994fb --- /dev/null +++ b/ecos/packages/fs/rom/current/tests/romfs1.c @@ -0,0 +1,408 @@ +//========================================================================== +// +// romfs1.c +// +// Test fileio system +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg, richard.panton@3glab.com, jlarmour +// Date: 2000-05-25 +// Purpose: Test fileio system +// Description: This test uses the testfs to check out the initialization +// and basic operation of the fileio system +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/hal.h> +#include <pkgconf/io_fileio.h> +#include <pkgconf/isoinfra.h> +#include <pkgconf/system.h> +#include <pkgconf/fs_rom.h> + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> +#include <dirent.h> +#include <stdio.h> + +#include <cyg/fileio/fileio.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/testcase.h> +#include <cyg/infra/diag.h> // HAL polled output + +// Test ROMFS data. Two example data files are generated so that +// the test will work on both big-endian and little-endian targets. +#if (CYG_BYTEORDER == CYG_LSBFIRST) +# include <cyg/romfs/testromfs_le.h> +#else +# include <cyg/romfs/testromfs_be.h> +#endif + +//========================================================================== + +MTAB_ENTRY( romfs_mte1, + "/", + "romfs", + "", + (CYG_ADDRWORD) &filedata[0] ); + + +//========================================================================== + +#define SHOW_RESULT( _fn, _res ) \ + diag_printf("<FAIL>: " #_fn "() returned %d %s\n", (int)_res, _res<0?strerror(errno):""); + +#define CHKFAIL_TYPE( _fn, _res, _type ) { \ +if ( _res != -1 ) \ + diag_printf("<FAIL>: " #_fn "() returned %d (expected -1)\n", (int)_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 CYGINT_ISO_STRING_STRFUNCS + +char *strcat( char *s1, const char *s2 ) +{ + char *s = s1; + while( *s1 ) s1++; + while( (*s1++ = *s2++) != 0); + return s; +} + +#endif + +//========================================================================== + +static void listdir( char *name, int statp ) +{ + int err; + DIR *dirp; + + diag_printf("<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); +#ifdef CYGPKG_FS_ROM_RET_DIRENT_DTYPE + diag_printf(" d_type %2x", entry->d_type); +#endif + 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 %ld]", + sbuf.st_mode,sbuf.st_ino,sbuf.st_nlink,sbuf.st_size); + } +#ifdef CYGPKG_FS_ROM_RET_DIRENT_DTYPE + if ((entry->d_type & S_IFMT) != (sbuf.st_mode & S_IFMT)) + CYG_TEST_FAIL("File mode's don't match between dirent and stat"); +#endif + } + + diag_printf("\n"); + } + + err = closedir( dirp ); + if( err < 0 ) SHOW_RESULT( stat, err ); +} + +//========================================================================== + +#ifdef CYGPKG_FS_RAM +static void copyfile( char *name2, char *name1 ) +{ + + int err; + char buf[IOSIZE]; + int fd1, fd2; + ssize_t done, wrote; + + diag_printf("<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 ); + +} +#endif + +//========================================================================== + +static void comparefiles( char *name2, char *name1 ) +{ + int err; + char buf1[IOSIZE]; + char buf2[IOSIZE]; + int fd1, fd2; + ssize_t done1, done2; + int i; + + diag_printf("<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]; +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) + struct cyg_fs_block_usage usage; +#endif + + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + diag_printf("<INFO>: ROMFS root follows\n"); + listdir( "/", true ); + + diag_printf("<INFO>: cd /etc\n" ); + err = chdir( "/etc" ); + if ( err < 0 ) { + SHOW_RESULT( chdir, err ); + CYG_TEST_FAIL_FINISH("romfs1"); + } + + diag_printf("<INFO>: ROMFS list of '' follows\n"); + listdir( "", true ); + + diag_printf("<INFO>: ROMFS list of /etc follows\n"); + listdir( "/etc", true ); + + diag_printf("<INFO>: ROMFS list of . follows\n"); + listdir( ".", true ); + +#ifdef CYGPKG_FS_RAM + err = mount( "", "/var", "ramfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + copyfile( "/etc/passwd", "/var/passwd_copy" ); + + comparefiles( "/etc/passwd", "/var/passwd_copy" ); +#endif + + diag_printf("<INFO>: ROMFS list of / follows\n"); +#ifdef CYGPKG_FS_RAM + diag_printf("<INFO>: Note that /var now gives stat() info for RAMFS\n"); +#endif + listdir( "/", true ); + + diag_printf("<INFO>: Mount ROMFS again onto /mnt\n"); + sprintf( address, "%p", (void*)&filedata[0] ); + err = mount( address, "/mnt", "romfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + comparefiles( "/etc/passwd", "/mnt/etc/passwd" ); + + + err = mkdir( "/foo", 0 ); + CHKFAIL_TYPE( mkdir, err, EROFS ); + + err = rename( "/var", "/tmp" ); // RAMFS is mounted here +#ifdef CYGPKG_FS_RAM + CHKFAIL_TYPE( rename, err, EXDEV ); +#else + CHKFAIL_TYPE( rename, err, EROFS ); +#endif + + err = rename( "/var/passwd_copy", "/mnt/etc/passwd_copy" ); + CHKFAIL_TYPE( rename, err, EXDEV ); + + err = rename( "/etc", "/tmp" ); + CHKFAIL_TYPE( rename, err, EROFS ); + + diag_printf("<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*)(&filedata[0] + 0x100)); + err = mount( address, "/tmp", "romfs" ); + CHKFAIL_TYPE( mount, err, ENOENT ); + + err = umount( "/mnt" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + err = umount( "/var" ); +#ifdef CYGPKG_FS_RAM + if( err < 0 ) SHOW_RESULT( umount, err ); +#else + CHKFAIL_TYPE( umount, err, EINVAL ); +#endif + +#if defined(CYGSEM_FILEIO_BLOCK_USAGE) + err = cyg_fs_getinfo("/", FS_INFO_BLOCK_USAGE, &usage, sizeof(usage)); + if( err < 0 ) SHOW_RESULT( cyg_fs_getinfo, err ); + diag_printf("<INFO>: total size: %6lld blocks, %10lld bytes\n", + usage.total_blocks, usage.total_blocks * usage.block_size); + diag_printf("<INFO>: free size: %6lld blocks, %10lld bytes\n", + usage.free_blocks, usage.free_blocks * usage.block_size); + diag_printf("<INFO>: block size: %6u bytes\n", usage.block_size); +#endif + // -------------------------------------------------------------- + + err = umount( "/" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + + CYG_TEST_PASS_FINISH("romfs1"); +} + +// ------------------------------------------------------------------------- +// EOF romfs1.c diff --git a/ecos/packages/fs/rom/current/tests/testromfs/etc/hosts b/ecos/packages/fs/rom/current/tests/testromfs/etc/hosts new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ecos/packages/fs/rom/current/tests/testromfs/etc/hosts diff --git a/ecos/packages/fs/rom/current/tests/testromfs/etc/inetd b/ecos/packages/fs/rom/current/tests/testromfs/etc/inetd new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ecos/packages/fs/rom/current/tests/testromfs/etc/inetd diff --git a/ecos/packages/fs/rom/current/tests/testromfs/etc/passwd b/ecos/packages/fs/rom/current/tests/testromfs/etc/passwd new file mode 100644 index 0000000..85aa954 --- /dev/null +++ b/ecos/packages/fs/rom/current/tests/testromfs/etc/passwd @@ -0,0 +1,32 @@ +root:x:0:0:root:/root:/bin/bash +daemon:x:1:1:daemon:/usr/sbin:/bin/sh +bin:x:2:2:bin:/bin:/bin/sh +sys:x:3:3:sys:/dev:/bin/sh +sync:x:4:100:sync:/bin:/bin/sync +games:x:5:100:games:/usr/games:/bin/sh +man:x:6:100:man:/var/cache/man:/bin/sh +lp:x:7:7:lp:/var/spool/lpd:/bin/sh +mail:x:8:8:mail:/var/spool/mail:/bin/sh +news:x:9:9:news:/var/spool/news:/bin/sh +uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh +proxy:x:13:13:proxy:/bin:/bin/sh +majordom:x:30:31:Majordomo:/usr/lib/majordomo:/bin/sh +postgres:x:31:32:postgres:/var/lib/postgres:/bin/sh +www-data:x:33:33:www-data:/var/www:/bin/sh +backup:x:34:34:backup:/var/backups:/bin/sh +msql:x:36:36:Mini SQL Database Manager:/var/lib/msql:/bin/sh +operator:x:37:37:Operator:/var:/bin/sh +list:x:38:38:SmartList:/var/list:/bin/sh +irc:x:39:39:ircd:/var:/bin/sh +gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats/gnats-db:/bin/sh +alias:x:70:65534:qmail alias:/var/qmail/alias:/bin/sh +qmaild:x:71:65534:qmail daemon:/var/qmail:/bin/sh +qmails:x:72:70:qmail send:/var/qmail:/bin/sh +qmailr:x:73:70:qmail remote:/var/qmail:/bin/sh +qmailq:x:74:70:qmail queue:/var/qmail:/bin/sh +qmaill:x:75:65534:qmail log:/var/qmail:/bin/sh +qmailp:x:76:65534:qmail pw:/var/qmail:/bin/sh +ftp:x:60000:65534::/lhome/ftp:/bin/false +nobody:x:65534:65534:nobody:/home:/bin/sh +identd:x:60001:65534::/var/run/identd:/bin/false +telnetd:x:60002:60002::/usr/lib/telnetd:/bin/false diff --git a/ecos/packages/fs/rom/current/tests/testromfs/mnt/thing b/ecos/packages/fs/rom/current/tests/testromfs/mnt/thing new file mode 100644 index 0000000..45b983b --- /dev/null +++ b/ecos/packages/fs/rom/current/tests/testromfs/mnt/thing @@ -0,0 +1 @@ +hi diff --git a/ecos/packages/fs/rom/current/tests/testromfs/var/foobar b/ecos/packages/fs/rom/current/tests/testromfs/var/foobar new file mode 100644 index 0000000..9cbcf6f --- /dev/null +++ b/ecos/packages/fs/rom/current/tests/testromfs/var/foobar @@ -0,0 +1 @@ +flibble |