summaryrefslogtreecommitdiff
path: root/ecos/packages/fs
diff options
context:
space:
mode:
authorMichael Gielda <mgielda@antmicro.com>2014-04-03 14:53:04 +0200
committerMichael Gielda <mgielda@antmicro.com>2014-04-03 14:53:04 +0200
commitae1e4e08a1005a0c487f03ba189d7536e7fdcba6 (patch)
treef1c296f8a966a9a39876b0e98e16d9c5da1776dd /ecos/packages/fs
parentf157da5337118d3c5cd464266796de4262ac9dbd (diff)
Added the OS files
Diffstat (limited to 'ecos/packages/fs')
-rw-r--r--ecos/packages/fs/fat/current/ChangeLog230
-rw-r--r--ecos/packages/fs/fat/current/cdl/fatfs.cdl159
-rw-r--r--ecos/packages/fs/fat/current/doc/README.txt116
-rw-r--r--ecos/packages/fs/fat/current/doc/fat16.ecm53
-rw-r--r--ecos/packages/fs/fat/current/doc/fatfd.ecm41
-rwxr-xr-xecos/packages/fs/fat/current/doc/mkdisk018
-rw-r--r--ecos/packages/fs/fat/current/include/fatfs.h83
-rw-r--r--ecos/packages/fs/fat/current/src/fatfs.c1587
-rw-r--r--ecos/packages/fs/fat/current/src/fatfs.h307
-rw-r--r--ecos/packages/fs/fat/current/src/fatfs_ncache.c898
-rw-r--r--ecos/packages/fs/fat/current/src/fatfs_supp.c2589
-rw-r--r--ecos/packages/fs/fat/current/src/fatfs_tcache.c285
-rw-r--r--ecos/packages/fs/fat/current/tests/fatfs1.c773
-rw-r--r--ecos/packages/fs/jffs2/current/ChangeLog545
-rw-r--r--ecos/packages/fs/jffs2/current/cdl/jffs2.cdl318
-rw-r--r--ecos/packages/fs/jffs2/current/doc/README.Locking155
-rw-r--r--ecos/packages/fs/jffs2/current/doc/TODO35
-rw-r--r--ecos/packages/fs/jffs2/current/doc/TODO.eCos25
-rw-r--r--ecos/packages/fs/jffs2/current/doc/readme.txt32
-rw-r--r--ecos/packages/fs/jffs2/current/include/linux/jffs2.h155
-rw-r--r--ecos/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h50
-rw-r--r--ecos/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h119
-rw-r--r--ecos/packages/fs/jffs2/current/src/LICENCE35
-rw-r--r--ecos/packages/fs/jffs2/current/src/build.c371
-rw-r--r--ecos/packages/fs/jffs2/current/src/compr.c457
-rw-r--r--ecos/packages/fs/jffs2/current/src/compr.h107
-rw-r--r--ecos/packages/fs/jffs2/current/src/compr_rtime.c132
-rw-r--r--ecos/packages/fs/jffs2/current/src/compr_rubin.c379
-rw-r--r--ecos/packages/fs/jffs2/current/src/compr_rubin.h21
-rw-r--r--ecos/packages/fs/jffs2/current/src/compr_zlib.c222
-rw-r--r--ecos/packages/fs/jffs2/current/src/debug.c710
-rw-r--r--ecos/packages/fs/jffs2/current/src/debug.h276
-rw-r--r--ecos/packages/fs/jffs2/current/src/dir-ecos.c371
-rw-r--r--ecos/packages/fs/jffs2/current/src/erase.c459
-rw-r--r--ecos/packages/fs/jffs2/current/src/flashio.c165
-rw-r--r--ecos/packages/fs/jffs2/current/src/fs-ecos.c2191
-rw-r--r--ecos/packages/fs/jffs2/current/src/gc.c1271
-rw-r--r--ecos/packages/fs/jffs2/current/src/gcthread.c124
-rw-r--r--ecos/packages/fs/jffs2/current/src/histo.h3
-rw-r--r--ecos/packages/fs/jffs2/current/src/histo_mips.h2
-rw-r--r--ecos/packages/fs/jffs2/current/src/malloc-ecos.c163
-rw-r--r--ecos/packages/fs/jffs2/current/src/nodelist.c534
-rw-r--r--ecos/packages/fs/jffs2/current/src/nodelist.h394
-rw-r--r--ecos/packages/fs/jffs2/current/src/nodemgmt.c680
-rw-r--r--ecos/packages/fs/jffs2/current/src/os-ecos.h226
-rw-r--r--ecos/packages/fs/jffs2/current/src/pushpull.h72
-rw-r--r--ecos/packages/fs/jffs2/current/src/read.c215
-rw-r--r--ecos/packages/fs/jffs2/current/src/readinode.c895
-rw-r--r--ecos/packages/fs/jffs2/current/src/scan.c927
-rw-r--r--ecos/packages/fs/jffs2/current/src/write.c702
-rw-r--r--ecos/packages/fs/jffs2/current/support/jffs2/etc/group31
-rw-r--r--ecos/packages/fs/jffs2/current/support/jffs2/etc/passwd19
-rw-r--r--ecos/packages/fs/jffs2/current/support/jffs2/jffs2.imgbin0 -> 2376 bytes
-rw-r--r--ecos/packages/fs/jffs2/current/tests/jffs2_1.c726
-rw-r--r--ecos/packages/fs/jffs2/current/tests/jffs2_2.c261
-rw-r--r--ecos/packages/fs/jffs2/current/tests/jffs2_3.c198
-rw-r--r--ecos/packages/fs/ram/current/ChangeLog170
-rw-r--r--ecos/packages/fs/ram/current/cdl/ramfs.cdl216
-rw-r--r--ecos/packages/fs/ram/current/src/ramfs.c2436
-rw-r--r--ecos/packages/fs/ram/current/tests/ramfs1.c683
-rw-r--r--ecos/packages/fs/ram/current/tests/ramfs2.c238
-rw-r--r--ecos/packages/fs/ram/current/tests/ramfs3.c183
-rw-r--r--ecos/packages/fs/rom/current/ChangeLog217
-rw-r--r--ecos/packages/fs/rom/current/cdl/romfs.cdl155
-rw-r--r--ecos/packages/fs/rom/current/doc/mk_romfs.txt111
-rw-r--r--ecos/packages/fs/rom/current/doc/romfs.txt12
-rw-r--r--ecos/packages/fs/rom/current/src/romfs.c1138
-rw-r--r--ecos/packages/fs/rom/current/support/Makefile38
-rwxr-xr-xecos/packages/fs/rom/current/support/file2c.tcl118
-rw-r--r--ecos/packages/fs/rom/current/support/mk_romfs.c744
-rw-r--r--ecos/packages/fs/rom/current/tests/romfs1.c408
-rw-r--r--ecos/packages/fs/rom/current/tests/testromfs/etc/hosts0
-rw-r--r--ecos/packages/fs/rom/current/tests/testromfs/etc/inetd0
-rw-r--r--ecos/packages/fs/rom/current/tests/testromfs/etc/passwd32
-rw-r--r--ecos/packages/fs/rom/current/tests/testromfs/mnt/thing1
-rw-r--r--ecos/packages/fs/rom/current/tests/testromfs/var/foobar1
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 *) &marker;
+ 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
new file mode 100644
index 0000000..964095d
--- /dev/null
+++ b/ecos/packages/fs/jffs2/current/support/jffs2/jffs2.img
Binary files differ
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