changeset 213:ef7d7da61c56

FFS code integration: remaining C files imported, but not yet integrated
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Mon, 06 Jan 2014 04:20:29 +0000
parents 3ebe6409e8bc
children bdfdea886bea
files gsm-fw/services/ffs/ffs.c gsm-fw/services/ffs/ffs_env.c gsm-fw/services/ffs/ffstrace.c gsm-fw/services/ffs/fsck.c gsm-fw/services/ffs/reclaim.c
diffstat 5 files changed, 4083 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/services/ffs/ffs.c	Mon Jan 06 04:20:29 2014 +0000
@@ -0,0 +1,1314 @@
+/******************************************************************************
+ * Flash File System (ffs)
+ * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
+ *
+ * ffs public API functions
+ *
+ * $Id: ffs.c 1.69.1.24.1.40 Thu, 08 Jan 2004 15:05:23 +0100 tsj $
+ *
+f ******************************************************************************/
+
+#ifndef TARGET
+#include "ffs.cfg"
+#endif
+
+#if ((TARGET == 1) || (RIV_ENV== 1))
+#include "ffs/board/task.h"
+#endif
+
+#if (TARGET == 0) 
+#include <stdlib.h>
+#endif
+
+#include <string.h>
+#include <limits.h>
+
+#include "ffs/ffs.h"
+#include "ffs/board/core.h"
+#include "ffs/board/ffstrace.h"
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+
+extern struct fs_s fs; // defined in core.c
+
+// These dummy defines and struct are only use to simulate FFS on the
+// PC. The ones that is used in target are located in task.h
+#if (TARGET == 0)
+struct ffs_blocking_s {int x; };
+#define FFS_BLOCKING_CALL_BEGIN()
+    int result; \
+    struct ffs_blocking_s fb; 
+#define FFS_BLOCKING_CALL_END()    
+#endif
+
+/******************************************************************************
+ * Create, Read and Write
+ ******************************************************************************/
+
+req_id_t ffs_file_write_b(const char *pathname, void *src, int size, 
+                        ffs_options_t option, T_RV_RETURN *cp, 
+                        struct ffs_blocking_s *fb)
+{
+    iref_t i, dir;
+    char *name;
+    effs_t error;
+    int chunk_size, size_remaining, bytes_free;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_file_write('%s', 0x%x, %d, %d) ?\n",
+          pathname, (int) src, size, option));
+
+    ttw(ttr(TTrApi, "ffs_file_write('%s', 0x%x, %d, %d) ?" NL,
+            pathname, (int) src, size, option)); 
+
+    // TASKBEGIN effs_t FILE_WRITE(path=pathname, src=src, size=size, value16=option) iref_t i, dir; char *name; effs_t error; int chunk_size, size_remaining, bytes_free;
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    if (size < 0)
+        return EFFS_INVALID;
+    
+    ffs_query(Q_BYTES_FREE, &bytes_free);
+    if (bytes_free < size)
+        return EFFS_NOSPACE;
+
+    chunk_size = (size > fs.chunk_size_max ? fs.chunk_size_max : size);
+
+    if ((i = object_lookup(pathname, &name, &dir)) < 0) {
+        // Object not found, continue like fcreate()
+        if (i != EFFS_NOTFOUND)
+            return i;
+
+        if (!is_open_option(option, FFS_O_CREATE))
+            return EFFS_NOTFOUND;
+
+        journal_begin(0);
+
+        if ((dir = object_create(name, src, chunk_size, -dir)) < 0)
+            return dir;
+
+        journal_end(OT_FILE);
+    }
+    
+    else {
+        // Object found, continue like fupdate()
+        if (is_open_option(option, (FFS_O_CREATE)) 
+			&& is_open_option(option, (FFS_O_EXCL)))
+            return EFFS_EXISTS;
+
+        if (get_fdi(i) >= 0) 
+            return EFFS_LOCKED;
+
+        // Even though the ffs architecture allows to have data in
+        // directory objects, we don't want to complicate matters, so we
+        // return an error
+        if (is_object(inode_addr(i), OT_DIR) && !(fs.flags & FS_DIR_DATA)) {
+            return EFFS_NOTAFILE;
+        }
+    
+        if ((i = is_readonly(i, pathname)) < 0)
+            return i;
+
+        // Save the segment (if any) in the global variable because this
+        // global variable will be updated if the inode is going to be
+        // relocated if an inode_reclaim() is triggeret by the object_create()
+        fs.i_backup = segment_next(i);
+
+        journal_begin(i);
+
+        if ((dir = object_create(name, src, chunk_size, -dir)) < 0)
+            return dir;
+        
+        // Do not link child - we are replacing the complete file!
+        fs.link_child = 0;  
+        journal_end(0);
+        
+        // If any other segments exist then remove them FIXME: If we get a
+        // power failure here then the remaining segments wil not be removed
+        // before inode_reclaim() has been executed
+        if (fs.i_backup > 0) 
+            if ((error = object_remove(fs.i_backup)) < 0)
+                return error;
+        
+    }
+    // Save dir in fs.i_backup because this will be updated if some of the
+    // chunks below trigger a inode reclaim!
+    fs.i_backup = dir;
+
+    size_remaining = size - chunk_size;
+    
+    while (size_remaining > 0) {
+
+        chunk_size = (size_remaining > fs.chunk_size_max ? 
+                      fs.chunk_size_max : size_remaining);
+        
+        journal_begin(0);
+        
+        if ((i = segment_create((char*) src + size - size_remaining,
+                                    chunk_size, fs.i_backup)) < 0)
+            return i;
+        
+        journal_end(OT_SEGMENT);
+        
+        size_remaining -= chunk_size;
+    }
+    
+    tw(tr_bstat());
+
+    return EFFS_OK;
+
+    // TASKEND
+}
+
+// Note: ffs_fcreate() is deprecated and should not be used. Use
+// ffs_file_write(..., FFS_O_CREATE | FFS_O_EXCL) instead.
+effs_t ffs_fcreate(const char *pathname, void *src, int size)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+  
+    result = ffs_file_write_b(pathname, src, size, FFS_O_CREATE | FFS_O_EXCL, 
+                              0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_fcreate_nb(const char *pathname, void *src, int size,
+                      T_RV_RETURN *cp)
+{
+    return ffs_file_write_b(pathname, src, size, FFS_O_CREATE | FFS_O_EXCL, 
+                            cp, 0);
+}
+
+// Note: ffs_fupdate() is deprecated and should not be used. Use
+// ffs_file_write(...,FFS_O_TRUNC) instead.
+effs_t ffs_fupdate(const char *pathname, void *src, int size)
+{
+
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_file_write_b(pathname, src, size, FFS_O_TRUNC, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_fupdate_nb(const char *pathname, void *src, int size,
+                      T_RV_RETURN *cp)
+{
+    return ffs_file_write_b(pathname, src, size, FFS_O_TRUNC, cp, 0);
+}
+
+// Note: ffs_fwrite() is deprecated and should not be used. Use
+// ffs_file_write(...,FFS_O_CREATE | FFS_O_TRUNC) instead.
+effs_t ffs_fwrite(const char *pathname, void *src, int size)
+{
+
+    FFS_BLOCKING_CALL_BEGIN();
+    
+    result = ffs_file_write_b(pathname, src, size, 
+                              FFS_O_CREATE | FFS_O_TRUNC, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_fwrite_nb(const char *pathname, void *src, int size,
+                     T_RV_RETURN *cp)
+{
+    return ffs_file_write_b(pathname, src, size, 
+                            FFS_O_CREATE | FFS_O_TRUNC, cp, 0);
+}
+
+effs_t ffs_file_write(const char *pathname, void *src, int size, 
+                      ffs_options_t option)
+{
+
+    FFS_BLOCKING_CALL_BEGIN();
+    
+    result = ffs_file_write_b(pathname, src, size, option, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_file_write_nb(const char *pathname, void *src, int size, 
+                         ffs_options_t option,
+                         T_RV_RETURN *cp)
+{
+    return ffs_file_write_b(pathname, src, size, option, cp, 0);
+}
+
+// Note important: ffs_fread() is deprecated and should not be used. Use
+// ffs_file_read() instead.
+int ffs_fread(const char *name, void *addr, int size)
+{
+    return ffs_file_read(name, addr, size);
+}
+
+
+int ffs_file_read(const char *name, void *addr, int size)
+{
+    int error;
+
+    tw(tr(TR_BEGIN, TrApi, "file_read('%s', 0x%x, %d) {\n",
+          name, (int) addr, size));
+    
+    if ((error = ffs_begin()) == EFFS_OK)  
+    {
+        error = file_read(name, addr, size);
+    }
+    
+    tw(tr(TR_END, TrApi, "} %d\n", error));
+    
+    return ffs_end(error);     // number of bytes read  
+}
+
+/******************************************************************************
+ * Stat, Symlink, Remove and Rename
+ ******************************************************************************/
+
+effs_t ffs_stat(const char *name, struct stat_s *stat)
+{
+    iref_t i;
+ 
+    tw(tr(TR_FUNC, TrApi, "ffs_stat('%s', ?) ?\n", name));
+	ttw(ttr(TTrApi, "ffs_stat('%s', ?) ?" NL, name)); 
+
+    if (name == NULL)
+        return EFFS_BADNAME;
+
+    if ((i = ffs_begin()) == EFFS_OK)
+    {
+        if ((i = object_stat(name, (struct xstat_s*) stat, 0, 0, 0)) > 0)
+            i = EFFS_OK;
+    }
+    
+    return ffs_end(i);
+}
+
+effs_t ffs_lstat(const char *name, struct stat_s *stat)
+{
+    iref_t i;
+ 
+    tw(tr(TR_FUNC, TrApi, "ffs_lstat('%s', ?) ?\n", name));
+	ttw(ttr(TTrApi, "ffs_lstat('%s', ?) ?" NL, name)); 
+
+    if ((i = ffs_begin()) == EFFS_OK) {
+        if ((i = object_stat(name, (struct xstat_s*)stat, 1, 0, 0)) > 0)
+            i = EFFS_OK;
+    }
+
+    return ffs_end(i);
+}
+
+effs_t ffs_xlstat(const char *name, struct xstat_s *stat)
+{
+    iref_t i;
+ 
+    tw(tr(TR_FUNC, TrApi, "ffs_xlstat('%s', ?) ?\n", name));
+	ttw(ttr(TTrApi, "ffs_xlstat('%s', ?) ?" NL, name)); 
+
+    if ((i = ffs_begin()) == EFFS_OK) {
+        if ((i = object_stat(name, stat, 1, 0, 1)) > 0)
+            i = EFFS_OK;
+    }
+
+    return ffs_end(i);
+}
+
+effs_t ffs_fstat(fd_t fdi, struct stat_s *stat)
+{
+    iref_t i;
+ 
+    tw(tr(TR_FUNC, TrApi, "ffs_fstat('%d', ?) ?\n", fdi));
+	ttw(ttr(TTrApi, "ffs_fstat('%d', ?) ?" NL, fdi)); 
+
+    if ((i = ffs_begin()) == EFFS_OK) {
+        if ((i = object_stat( 0, (struct xstat_s*) stat, 0, fdi, 0)) > 0)
+            i = EFFS_OK;
+    }
+
+    return ffs_end(i);
+}
+
+req_id_t ffs_symlink_b(const char *pathname, const char *src,
+                      T_RV_RETURN *cp, struct ffs_blocking_s *fb)
+{
+    iref_t i, dir;
+    char *name;
+    int size;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_symlink('%s', '%s') ?\n", pathname, src));
+	ttw(ttr(TTrApi, "ffs_symlink('%s', '%s') ?" NL, pathname, src)); 
+
+    // TASKBEGIN effs_t SYMLINK(path=pathname, src=src) iref_t i, dir; int size; char *name;
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    if (src == NULL)
+        return EFFS_BADNAME;
+
+    i = object_lookup(pathname, &name, &dir);
+    if (i > 0)
+        return EFFS_EXISTS;
+    if (i != EFFS_NOTFOUND)
+        return i;
+
+    size = ffs_strlen(src) + 1;  // include null-terminator
+
+    journal_begin(0);
+
+    if ((i = object_create(name, src, size, -dir)) < 0)
+        return i;
+
+    journal_end(OT_LINK);
+
+    tw(tr_bstat());
+
+    return EFFS_OK;
+
+    // TASKEND
+}
+
+effs_t ffs_symlink(const char *pathname, const char *actualpath)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_symlink_b(pathname, actualpath, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_symlink_nb(const char *pathname, const char *src,
+                      T_RV_RETURN *cp)
+{
+    return ffs_symlink_b(pathname, src, cp, 0);
+}
+
+int ffs_readlink(const char *name, char *addr, int size)
+{
+    int error;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_readlink('%s')\n", name));
+
+    if ((error = ffs_begin()) == EFFS_OK)
+    {
+        error = object_read(name, addr, size, 1);
+    }
+    return ffs_end(error);
+}
+
+req_id_t ffs_remove_b(const char *pathname, T_RV_RETURN *cp, 
+            struct ffs_blocking_s *fb)
+{
+    iref_t i;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_remove('%s')\n", pathname));
+	ttw(ttr(TTrApi, "ffs_remove('%s') ?" NL, pathname)); 
+
+    // TASKBEGIN effs_t REMOVE(path=pathname) iref_t i;
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    if ((i = object_lookup_once(pathname, 0, 0)) < 0)
+        return i;
+
+    if (get_fdi(i) >= 0)
+        return EFFS_LOCKED;
+
+    if ((i = is_readonly(i, pathname)) < 0)
+        return i;
+
+    if ((i = object_remove(i)) < 0)
+        return i;
+
+    tw(tr_bstat());
+
+    return EFFS_OK;
+
+    // TASKEND
+}
+
+effs_t ffs_remove(const char *pathname)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+    
+    result = ffs_remove_b(pathname, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result; 
+}
+
+req_id_t ffs_remove_nb(const char *pathname, T_RV_RETURN *cp)
+{
+    return ffs_remove_b(pathname, cp, 0);
+}
+
+req_id_t ffs_fcontrol_b(const char *pathname, int8 action, int param,
+              T_RV_RETURN *cp, struct ffs_blocking_s *fb)
+{
+    iref_t i;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_fcontrol('%s', %d, 0x%x) ?\n",
+          pathname, action, param));
+	ttw(ttr(TTrApi, "ffs_fcontrol('%s', %d, 0x%x) ?" NL, 
+			pathname, action, param)); 
+
+    // TASKBEGIN effs_t FCONTROL(path=pathname, value16=action, size=param) iref_t i;
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    if (pathname == NULL)
+        return EFFS_BADNAME;
+
+    if ((i = ffs_strcmp(pathname, "/dev/ffs")) != 0)
+    {
+        if ((i = object_lookup_once(pathname, 0, 0)) < 0)
+            return i;
+
+        if ((i = is_readonly(i, pathname)) < 0)
+            return i;
+    }
+
+    if ((i = object_control(i, action, param)) < 0)
+        return i;
+
+    tw(tr_bstat());
+
+    return EFFS_OK;
+
+    // TASKEND
+}
+
+effs_t ffs_fcontrol(const char *pathname, int8 action, int param)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_fcontrol_b(pathname, action, param, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;
+}
+
+req_id_t ffs_fcontrol_nb(const char *pathname, int8 action, int param,
+                       T_RV_RETURN *cp)
+{
+    return ffs_fcontrol_b(pathname, action, param, cp, 0);
+}
+
+req_id_t ffs_rename_b(const char *pathname, const char *newname,
+                     T_RV_RETURN *cp, struct ffs_blocking_s *fb)
+{
+    iref_t i, oldi, dir;
+    char *name;
+    struct inode_s *ip;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_rename('%s', '%s') ?\n", pathname, newname));
+	ttw(ttr(TTrApi, "ffs_rename('%s', '%s') ?" NL, pathname, newname)); 
+
+    // TASKBEGIN effs_t RENAME(path=pathname, src=newname) iref_t i, oldi, dir; char *name; struct inode_s *ip;
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    // pathname MUST exist, not be open and MUST be writable
+    if ((oldi = object_lookup_once(pathname, 0, 0)) < 0)
+        return oldi;
+    if ((oldi = is_readonly(oldi, pathname)) < 0)
+        return oldi;
+    if (get_fdi(oldi) >= 0)
+        return EFFS_LOCKED;
+
+    journal_begin(oldi);
+
+    if ((i = object_lookup_once(newname, &name, &dir)) < 0) {
+        if (i != EFFS_NOTFOUND)
+            return i;
+    }
+    else {                               // newname obj exist   
+        ip = inode_addr(oldi);
+        if (is_object(ip, OT_FILE)) {    // is old obj a file?
+            if ((i = is_readonly(i, newname)) < 0)
+                return i;
+
+            ip = inode_addr(i);
+            if (!is_object(ip, OT_FILE)) // newname MUST be a file 
+                return EFFS_NOTAFILE;
+
+            fs.journal.repli = i;        
+        }
+        else
+            return EFFS_EXISTS;         
+    }
+    
+    if ((i = object_rename(oldi, name, -dir)) < 0)
+        return i;
+
+    journal_end(0);
+
+    tw(tr_bstat());    
+
+    return EFFS_OK;
+
+    // TASKEND
+
+}
+
+effs_t ffs_rename(const char *pathname, const char *newname)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_rename_b(pathname, newname, 0, &fb);
+
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;
+}
+
+
+req_id_t ffs_rename_nb(const char *pathname, const char *newname,
+                     T_RV_RETURN *cp)
+{
+    return ffs_rename_b(pathname, newname, cp, 0);
+}
+
+/******************************************************************************
+ * Directory Operations
+ ******************************************************************************/
+
+// All directory operations are more or less similar to unix
+// semantics.
+req_id_t ffs_mkdir_b(const char *pathname, T_RV_RETURN *cp, 
+           struct ffs_blocking_s *fb)
+{
+    iref_t i, dir;
+    char *name;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_mkdir('%s')\n", pathname));
+	ttw(ttr(TTrApi, "ffs_mkdir('%s') ?" NL, pathname)); 
+	
+    // TASKBEGIN effs_t MKDIR(path=pathname) iref_t i, dir; char *name;
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    i = object_lookup(pathname, &name, &dir);
+    if (i > 0)
+        return EFFS_EXISTS;
+    if (i != EFFS_NOTFOUND)
+        return i;
+
+    journal_begin(0);
+
+    if ((i = object_create(name, 0, 0, -dir)) < 0)
+        return i;
+
+    journal_end(OT_DIR);
+
+    tw(tr_bstat());
+
+    return EFFS_OK;
+
+    // TASKEND
+}
+
+effs_t ffs_mkdir(const char *pathname)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_mkdir_b(pathname, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;
+}
+
+req_id_t ffs_mkdir_nb(const char *pathname, T_RV_RETURN *cp)
+{
+    return ffs_mkdir_b(pathname, cp, 0);
+}
+
+int ffs_opendir(const char *name, struct dir_s *dir)
+{
+    int i;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_opendir('%s', ?)\n", name));
+	ttw(ttr(TTrApi, "ffs_opendir('%s', ?) ?" NL, name)); 
+
+    if (dir == NULL)
+        return EFFS_INVALID;
+
+    if ((i = ffs_begin()) == EFFS_OK)
+    {
+        if ((i = dir_open(name)) >= 0)
+        {
+            dir->this = i;
+            dir->index = i;
+
+            // Now count the number of entries in the directory
+            dir_traverse(-i, (iref_t *) &i);
+        }
+    }
+    return ffs_end(i);
+}
+
+int ffs_readdir(struct dir_s *dir, char *name, int size)
+{
+    iref_t i;
+
+    tw(tr(TR_BEGIN, TrApi, "ffs_readdir(?, ?, ?) {\n"));
+	ttw(ttr(TTrApi, "ffs_readdir(?, ?, ?) ?" NL)); 
+
+    if (dir == NULL || name == NULL || size < 0) {
+        tw(tr(TR_END, TrApi, "} %d\n", EFFS_INVALID));
+        return EFFS_INVALID;
+    }
+
+    if ((i = ffs_begin()) == EFFS_OK)
+    {
+        if ((i = dir_next(dir->this, dir->index, name, size)))
+            dir->index = i;
+    }
+    tw(tr(TR_END, TrApi, "} ('%s') %d\n", name, i));
+
+    return ffs_end(i);
+}
+
+
+/******************************************************************************
+ * Preformat and Format
+ ******************************************************************************/
+
+// Note that we do NOT call ffs_begin() because it will just return
+// EFFS_NOFORMAT!
+req_id_t ffs_format_b(const char *name, uint16 magic, T_RV_RETURN *cp,
+            struct ffs_blocking_s *fb)
+{
+    effs_t i;
+	
+    tw(tr(TR_BEGIN, TrApi, "ffs_format('%s', 0x%x) {\n", name, magic));
+	ttw(ttr(TTrApi, "ffs_format('%s', 0x%x) ?" NL, name, magic)); 
+
+    // TASKBEGIN effs_t FORMAT(path=name, size=magic) iref_t i;
+	
+    if (magic != 0x2BAD) {
+        tw(tr(TR_END, TrApi, "} %d\n", EFFS_INVALID));
+        return EFFS_INVALID;
+    }
+
+	if (name == NULL) {
+        name = "/ffs-5.54";
+	}
+
+    if (*name != '/') {
+        tw(tr(TR_END, TrApi, "} %d\n", EFFS_BADNAME));
+        return EFFS_BADNAME;
+    }
+
+    if ((i = is_formattable(1)) < 0) {
+        tw(tr(TR_END, TrApi, "} %d\n", i));
+        return i;
+    }
+
+    if ((i = fs_format(name)) < 0)
+        return i;
+
+    tw(tr(TR_END, TrApi, "} %d\n", i));
+
+    tw(tr_bstat());
+
+    return EFFS_OK;
+
+    // TASKEND
+}
+ 
+effs_t ffs_format(const char *name, uint16 magic)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+    
+    result = ffs_format_b(name, magic, 0, &fb);
+    
+    FFS_BLOCKING_CALL_END();
+
+    return result;
+}
+
+req_id_t ffs_format_nb(const char *name, uint16 magic, T_RV_RETURN *cp)
+{
+   return ffs_format_b(name, magic, cp, 0);
+}
+
+req_id_t ffs_preformat_b(uint16 magic, T_RV_RETURN *cp, 
+               struct ffs_blocking_s *fb)
+{
+    effs_t i;
+
+    tw(tr(TR_BEGIN, TrApi, "ffs_preformat(0x%x) {\n", magic));
+	ttw(ttr(TTrApi, "ffs_preformat(0x%x) ?" NL, magic)); 
+
+    // TASKBEGIN effs_t PREFORMAT(path="/", size=magic) effs_t i;
+
+    if (magic != 0xDEAD) {
+        tw(tr(TR_END, TrApi, "} %d\n", EFFS_INVALID));
+        return EFFS_INVALID;
+    }
+
+    if (!ffs_is_modifiable("")) {
+        tw(tr(TR_END, TrApi, "} %d\n", EFFS_ACCESS));
+        return EFFS_ACCESS;
+    }
+
+    if ((i = is_formattable(0)) < 0) {
+        tw(tr(TR_END, TrApi, "} %d\n", i));
+        return i;
+    }
+
+    if ((i = fs_preformat()) < 0)
+        return i;
+
+    tw(tr(TR_END, TrApi, "} %d\n", i));
+
+    tw(tr_bstat());
+
+    return EFFS_OK;
+
+    // TASKEND
+}
+
+effs_t ffs_preformat(uint16 magic)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_preformat_b(magic, 0, &fb);
+    
+    FFS_BLOCKING_CALL_END();
+
+    return result;
+}
+
+req_id_t ffs_preformat_nb(uint16 magic, T_RV_RETURN *cp)
+{
+  return ffs_preformat_b(magic, cp, 0);
+}
+
+/******************************************************************************
+ * Open, Read, Write, Close
+ ******************************************************************************/
+req_id_t ffs_open_b(const char *pathname, ffs_options_t option,
+        T_RV_RETURN *cp, struct ffs_blocking_s *fb)
+{
+    iref_t i, dir, dummy;            
+    char *name;
+    fd_t other_fdi, fdi = 0;
+    int error; 
+    struct inode_s *ip;
+    
+    tw(tr(TR_FUNC, TrApi, "ffs_open('%s', 0x%x) ?\n", pathname, option));
+	ttw(ttr(TTrApi, "ffs_open('%s', 0x%x) ?" NL, pathname, option)); 
+
+    // TASKBEGIN fd_t OPEN(path=pathname, value16=option) iref_t i, dir, dummy; char *name; fd_t other_fdi, fdi = 0; int error; struct inode_s *ip;
+
+    if (fs.initerror)
+        return fs.initerror;
+    
+    // Minimum one of the flags RD or WR must be specifyed
+    if (!is_open_option(option, FFS_O_RDONLY) && 
+        !is_open_option(option, FFS_O_WRONLY))
+        return EFFS_INVALID;
+    
+    // RDONLY must not be combined with any other options if not together
+    // with WR!
+    if (is_open_option(option, FFS_O_RDONLY) && 
+        !is_open_option(option, FFS_O_WRONLY))     
+        if (!(option == FFS_O_RDONLY))
+            return EFFS_INVALID;
+    
+    for (fdi = 0; fdi < fs.fd_max; fdi++) {  // Find free fd
+        if (fs.fd[fdi].options == 0) {
+            break;
+        }
+    }
+
+    if (fdi >= fs.fd_max)
+        return EFFS_NUMFD;  // Too many open files in system
+
+    i = object_lookup(pathname, &name, &dir);
+    if (i < 0 && i != EFFS_NOTFOUND)
+        return i;
+
+    // Open one file several times in RD is okay but only one time in WR
+    if (i != EFFS_NOTFOUND && (other_fdi = get_fdi(i)) >= 0) {
+        if (is_open_option(fs.fd[other_fdi].options, FFS_O_WRONLY) || 
+			is_open_option(option, FFS_O_WRONLY))
+            return EFFS_LOCKED;
+    }
+
+    // Init default values
+    fs.fd[fdi].fp = fs.fd[fdi].size = fs.fd[fdi].wfp = fs.fd[fdi].dirty = 0;
+         
+    if (i == EFFS_NOTFOUND) {
+        if (is_open_option(option, (FFS_O_CREATE | FFS_O_WRONLY))) {
+            if ((error = is_filename(name)) < 0)
+                return error;
+
+            // Create segmenthead
+            journal_begin(0);
+
+            if ((i = object_create(name, 0, 0, -dir)) < 0)
+                return i;
+
+            journal_end(OT_FILE);  
+            tw(tr_bstat());
+            fs.fd[fdi].seghead = i;
+        }
+        else 
+            return EFFS_NOTFOUND;    
+    }
+    else {
+        if (is_open_option(option, FFS_O_WRONLY)) {
+            if (is_open_option(option, (FFS_O_CREATE | FFS_O_EXCL)))
+                return EFFS_EXISTS;   
+            if ((i = is_readonly(i, pathname)) < 0)
+                return i;
+        }
+        ip = inode_addr(i);
+        
+        if (is_object(ip, OT_DIR))
+            return EFFS_NOTAFILE;
+
+        if (is_open_option(option, FFS_O_TRUNC)) { 
+            // Save the segment (if any) in the global variable because this
+            // global variable will be updated if the inode is relocated by
+            // an inode_reclaim() triggeret by object_create()
+            fs.i_backup = segment_next(i);
+
+            // Replace old seghead with a new and remove all old segments
+            journal_begin(i);
+
+            if ((i = object_create(name, 0, 0, -dir)) < 0)
+                return i;
+
+            // Do not link child
+            fs.link_child = 0;  
+            journal_end(0);
+
+			// If any further segments exist then remove them now
+            if (fs.i_backup > 0) 
+                if ((error = object_remove(fs.i_backup)) < 0)
+                    return error;
+
+            tw(tr_bstat());
+        }
+
+        else {
+            // Get total size of the file.    
+            fs.fd[fdi].size = segfile_seek(i, INT_MAX, &dummy, 0);
+        }
+        
+        if (is_open_option(option, FFS_O_APPEND)) { 
+            fs.fd[fdi].fp = fs.fd[fdi].size;
+        }
+    }
+    
+    if (is_open_option(option, FFS_O_WRONLY)) {
+#if (TARGET == 1)
+        if ((fs.fd[fdi].buf = (char *) target_malloc(fs.fd_buf_size)) == 0)
+            return EFFS_MEMORY;
+#else
+        if ((fs.fd[fdi].buf = malloc(fs.fd_buf_size)) == 0)
+            return EFFS_MEMORY;
+#endif
+    }
+
+    // Save data in file descriptor     
+	fs.fd[fdi].seghead = i;    
+	fs.fd[fdi].options = option;  
+ 
+    return fdi + FFS_FD_OFFSET;
+
+    // TASKEND
+}
+
+fd_t ffs_open(const char *pathname, ffs_options_t option)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+    
+    result = ffs_open_b(pathname, option, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+
+req_id_t ffs_open_nb(const char *pathname, ffs_options_t option,
+         T_RV_RETURN *cp)
+{
+    return ffs_open_b(pathname, option, cp, 0);
+}
+
+req_id_t ffs_close_b(fd_t fdi, T_RV_RETURN *cp, struct ffs_blocking_s *fb)
+{
+    int error;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_close(%d) ?\n", fdi));  
+	ttw(ttr(TTrApi, "ffs_close(%d) ?" NL, fdi)); 
+
+    // TASKBEGIN effs_t CLOSE(fdi=fdi) iref_t i; int error;
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    fdi -= FFS_FD_OFFSET;
+
+    if (!is_fd_valid(fdi))
+        return EFFS_BADFD;
+    
+    if (is_open_option(fs.fd[fdi].options, FFS_O_WRONLY )) {
+        if ((error = datasync(fdi)) < 0)
+            return error;
+
+#if (TARGET == 1)
+        target_free(fs.fd[fdi].buf);
+#else
+        free(fs.fd[fdi].buf);
+#endif
+    }
+
+    // Clear all data in file descriptor
+    fs.fd[fdi].seghead = 0;
+    fs.fd[fdi].options = fs.fd[fdi].fp = 0;
+
+    return EFFS_OK;
+
+    // TASKEND
+}
+
+effs_t ffs_close(fd_t fdi)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_close_b(fdi, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+
+req_id_t ffs_close_nb(fd_t fdi, T_RV_RETURN *cp)
+{
+    return ffs_close_b( fdi, cp, 0);
+}
+
+req_id_t ffs_write_b(fd_t fdi, void *src, int amount, 
+        T_RV_RETURN *cp, struct ffs_blocking_s *fb)
+{
+    effs_t error;
+    iref_t i; 
+    int size_remaining, fp_offset;
+    int size, size_done;
+    offset_t chunk_offset;
+
+    tw(tr(TR_BEGIN, TrApi, "ffs_write_b(%d, 0x%x, %d) ?{\n", fdi, src, amount));
+    ttw(ttr(TTrApi, "ffs_write_b(%d, 0x%x, %d) ?" NL, fdi, src, amount)); 
+
+    // TASKBEGIN int WRITE(fdi=fdi, src=src, size=amount) effs_t error; iref_t i; int size_remaining, fp_offset; int size, size_done; offset_t chunk_offset; 
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    if (amount < 0 || src == NULL)
+        return EFFS_INVALID;
+  
+    fdi -= FFS_FD_OFFSET;
+    
+    if (!is_fd_valid(fdi))
+        return EFFS_BADFD;
+
+    if (!is_open_option(fs.fd[fdi].options, FFS_O_WRONLY ))
+        return EFFS_INVALID; // not opened with write flag
+
+    // If FFS_O_APPEEND is specified move fp to eof
+    if (is_open_option(fs.fd[fdi].options, FFS_O_APPEND ))
+        fs.fd[fdi].fp = fs.fd[fdi].size;
+
+    // If fp has been moved outside the write buf (by a read) then flush the
+    // write buffer.
+    if (fs.fd[fdi].fp >= (fs.fd[fdi].wfp + fs.chunk_size_max)) {
+        if ((error = datasync(fdi)) < 0)
+            return error;
+    }
+
+	size_done = 0;
+	size_remaining = amount;
+    
+    do {       
+        if (!fs.fd[fdi].dirty ) {   
+            // Buffer is not dirty so find the chunk that fp points to.
+            segfile_seek(fs.fd[fdi].seghead, fs.fd[fdi].fp, &i, 
+                             &chunk_offset);
+
+            if ((fs.fd[fdi].size == fs.fd[fdi].fp && 
+                 chunk_offset == fs.chunk_size_max) || fs.fd[fdi].size == 0 ) {
+                // End of file and last chunk is full or empty seghead.
+                fs.fd[fdi].wfp = fs.fd[fdi].size;
+                fs.fd[fdi].wch = 0;  // Create new chunk (not update).
+            }
+            else {
+                // Work on this chunk and update it later by datasyns
+                segment_read(i, fs.fd[fdi].buf, fs.fd_buf_size, 0);
+                fs.fd[fdi].wfp = fs.fd[fdi].fp - chunk_offset;
+                fs.fd[fdi].wch = i;
+            }
+        }
+       
+		fs.fd[fdi].dirty = 1;
+		fp_offset = fs.fd[fdi].fp - fs.fd[fdi].wfp;
+
+        // Fill the buffer to max or just add the rest
+        size = fs.chunk_size_max - fp_offset;
+
+		if (size_remaining <= fs.chunk_size_max - fp_offset)
+                size = size_remaining;
+       
+		tw(tr(TR_FUNC, TrApi, "Copy data to buffer (size: %d)\n", size));
+
+        memcpy(fs.fd[fdi].buf + fp_offset, (uint8*)src + size_done, 
+               size);
+        
+        fs.fd[fdi].fp += size;
+        if (fs.fd[fdi].fp > fs.fd[fdi].size)
+            fs.fd[fdi].size = fs.fd[fdi].fp;
+
+        size_done += size;         // FIXME: remove size_done or size_remaining
+        size_remaining -= size;
+        
+        // If wrbuf is full (size = chunk_size_max) so create a chunk.
+        if (fs.fd[fdi].fp >= (fs.fd[fdi].wfp + fs.chunk_size_max)) {
+            if ((error = datasync(fdi)) < 0)
+                return error;
+        }
+    } while(size_remaining > 0);
+    
+    tw(tr(TR_END, TrApi, "} %d\n", amount));
+    return amount;         
+
+    // TASKEND
+}
+
+int ffs_write(fd_t fdi, void *src, int amount)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_write_b(fdi, src, amount, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_write_nb(fd_t fdi, void *src, int amount, T_RV_RETURN *cp)
+{
+    
+    tw(tr(TR_FUNC, TrApi, "ffs_write_nb(%d, 0x%x, %d) ?\n", fdi, src, amount));
+
+    return ffs_write_b(fdi, src, amount, cp, 0);      
+}
+
+int ffs_read(fd_t fdi, void *src, int size)
+{
+    int error;
+    
+    tw(tr(TR_BEGIN, TrApi, "ffs_read(%d, 0x%x, %d) {\n", fdi, src, size));
+    ttw(ttr(TTrApi, "ffs_read(%d, 0x%x, %d) ?" NL, fdi, src, size)); 
+
+    if ((error = ffs_begin()) == EFFS_OK)    
+    {
+        error = stream_read(fdi - FFS_FD_OFFSET, src, size);
+    }
+
+    tw(tr(TR_END, TrApi, "} %d\n", error));
+    return ffs_end(error);     // number of bytes read  
+}
+
+// The seek function will not allow the file offset to be set beyond the end
+// of the existing data in the file or the final offset to be negative.
+req_id_t ffs_seek_b(fd_t fdi, int offset, int whence, T_RV_RETURN *cp, 
+               struct ffs_blocking_s *fb)
+{
+	effs_t error;
+    int fp_new;
+
+    tw(tr(TR_FUNC, TrApi, "ffs_seek(%d, %d, %d) ?\n", fdi, offset, whence));
+    ttw(ttr(TTrApi, "ffs_seek(%d, %d, %d) ?" NL, fdi, offset, whence)); 
+
+    // TASKBEGIN int SEEK(fdi=fdi, size=offset, value16=whence) effs_t error; iref_t i; int fp_new, foffset;
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    fdi -= FFS_FD_OFFSET;
+
+    if (!is_fd_valid(fdi))
+        return EFFS_BADFD;
+    
+    switch(whence) {
+    case FFS_SEEK_SET:
+        if (offset < 0 || offset > fs.fd[fdi].size)
+            return EFFS_INVALID;  
+        fp_new = offset;
+        break;
+    case FFS_SEEK_CUR:
+        if (fs.fd[fdi].fp + offset < 0 || 
+            fs.fd[fdi].fp + offset > fs.fd[fdi].size)
+            return EFFS_INVALID;  
+        fp_new = fs.fd[fdi].fp + offset;
+        break;
+    case FFS_SEEK_END:
+        if (offset > 0 || fs.fd[fdi].size < -offset)
+            return EFFS_INVALID;
+        fp_new = (offset + fs.fd[fdi].size);
+        break;
+    default:
+        return EFFS_INVALID;
+    }
+    
+    if (!is_offset_in_buf(fp_new, fdi))
+        if ((error = datasync(fdi)) < 0)
+            return error;
+    
+    return fs.fd[fdi].fp = fp_new;
+    
+    // TASKEND
+}
+
+int ffs_seek(fd_t fdi, int offset, int whence) 
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_seek_b(fdi, offset, whence, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_seek_nb(fd_t fdi, int offset, int whence, T_RV_RETURN *cp) 
+{
+    return ffs_seek_b(fdi, offset, whence, cp, 0);
+}
+
+req_id_t ffs_truncate_b(const char *path, offset_t length, T_RV_RETURN *cp, 
+                      struct ffs_blocking_s *fb) 
+{
+    tw(tr(TR_FUNC, TrApi, "ffs_truncate('%s', %d) \n", path, length));
+    ttw(ttr(TTrApi, "ffs_ftruncate('%s', %d) ?" NL, path, length)); 
+
+    // TASKBEGIN effs_t TRUNC(path=path, size=length) iref_t i;
+
+    if (fs.initerror)
+        return fs.initerror;
+
+    if (path == NULL)
+        return EFFS_BADNAME;
+
+    return object_truncate(path, -1, length);
+        
+    // TASKEND
+}
+
+effs_t ffs_truncate(const char *path, offset_t length) 
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_truncate_b(path, length, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_truncate_nb(const char *path, offset_t length, T_RV_RETURN *cp) 
+{
+    return ffs_truncate_b(path, length, cp, 0);
+}
+
+req_id_t ffs_ftruncate_b(fd_t fdi, offset_t length, T_RV_RETURN *cp, 
+                       struct ffs_blocking_s *fb)
+{
+    tw(tr(TR_FUNC, TrApi, "ffs_ftruncate(%d, %d) \n", fdi, length));
+    ttw(ttr(TTrApi, "ffs_ftruncate(%d, %d) ?" NL, fdi, length)); 
+
+    // TASKBEGIN effs_t FTRUNC(fdi=fdi, size=length) iref_t i;
+
+    if (fs.initerror)
+        return fs.initerror;    
+    
+    return object_truncate(0, fdi - FFS_FD_OFFSET, length);
+    
+    // TASKEND
+}
+
+effs_t ffs_ftruncate(fd_t fdi, offset_t length)
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_ftruncate_b(fdi, length, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_ftruncate_nb(fd_t fdi, offset_t length, T_RV_RETURN *cp)
+{
+    return ffs_ftruncate_b(fdi, length, cp, 0);
+}
+
+req_id_t ffs_fdatasync_b(fd_t fdi, T_RV_RETURN *cp, struct ffs_blocking_s *fb) 
+{
+    tw(tr(TR_FUNC, TrApi, "ffs_fdatasync(%d) \n", fdi));
+    ttw(ttr(TTrApi, "ffs_fdatasync(%d) ?" NL, fdi)); 
+    
+    // TASKBEGIN effs_t FDATASYNC(fdi=fdi) effs_t error;
+
+    if (fs.initerror)
+        return fs.initerror;
+  
+    return datasync(fdi - FFS_FD_OFFSET);
+    
+    // TASKEND
+}
+
+effs_t ffs_fdatasync(fd_t fdi) 
+{
+    FFS_BLOCKING_CALL_BEGIN();
+
+    result = ffs_fdatasync_b(fdi, 0, &fb);
+
+    FFS_BLOCKING_CALL_END();
+
+    return result;             
+}
+
+req_id_t ffs_fdatasync_nb(fd_t fdi, T_RV_RETURN *cp) 
+{
+    return ffs_fdatasync_b(fdi, cp, 0);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/services/ffs/ffs_env.c	Mon Jan 06 04:20:29 2014 +0000
@@ -0,0 +1,205 @@
+/****************************************************************************/
+/*                                                                          */
+/*	File Name:	ffs_env.c   												*/
+/*                                                                          */
+/*	Purpose:	This file contains definitions for RV manager related	*/
+/*				functions used to get info, start and stop the FFS SWE.	    */
+/*                                                                          */
+/*  Version		0.1															*/
+/*																			*/
+/* 	Date       	Modification												*/
+/*  ------------------------------------									*/
+/*  10/24/2000	Create														*/
+/*																			*/
+/*	Author		Pascal Puel                         						*/
+/*																			*/
+/* (C) Copyright 2000 by Texas Instruments Incorporated, All Rights Reserved*/
+/****************************************************************************/
+
+#include "board.cfg"
+#include "ffs.cfg"
+#include "ffs/ffs_env.h" 
+#include "rvm/rvm_gen.h"
+#include "rvm/rvm_priorities.h"
+#include "rvm/rvm_use_id_list.h"
+#include "ffs/board/task.h" 
+#include <string.h>
+
+extern void ffs_task_init(T_RVF_MB_ID mbid, T_RVF_ADDR_ID  addr_id);
+T_FFS_TASK_INFO ffs_task_info;
+
+/* global pointer to the error function */
+static T_RVM_RETURN (*ffs_error_ft)(T_RVM_NAME swe_name, T_RVM_RETURN error_cause,
+												T_RVM_ERROR_TYPE error_type,T_RVM_STRING error_msg);
+
+/******************************************************************************
+* Function	  : ffs_get_info
+*
+* Description : This function is called by the RV manager to learn 
+*				driver requirements in terms of memory, SWEs...
+*
+* Parameters  : T_RVM_INFO_SWE  * swe_info: pointer to the structure to fill
+*				containing infos related to the driver SWE.
+*
+* Return      :  T_RVM_RETURN
+* 
+* History	  : 0.1 (20-August-2000)
+*									
+*
+******************************************************************************/
+T_RVM_RETURN ffs_get_info(T_RVM_INFO_SWE  * infoSWE)
+{
+
+	/* SWE info */
+	infoSWE->swe_type = RVM_SWE_TYPE_4;
+
+	infoSWE->type_info.type4.swe_use_id	= FFS_USE_ID;
+	memcpy ( (UINT8 *) infoSWE->type_info.type4.swe_name, "FFS", sizeof("FFS") );
+	infoSWE->type_info.type4.version    = 0;
+
+	infoSWE->type_info.type4.stack_size = FFS_STACK_SIZE;
+	infoSWE->type_info.type4.priority   = RVM_FFS_TASK_PRIORITY;
+	
+	/* memory bank info */
+	infoSWE->type_info.type4.nb_mem_bank = 1;
+	
+	memcpy ((UINT8 *) infoSWE->type_info.type4.mem_bank[0].bank_name, "FFS_PRIM", RVM_NAME_MAX_LEN);
+	infoSWE->type_info.type4.mem_bank[0].initial_params.size          = FFS_MB_PRIM_SIZE;
+	infoSWE->type_info.type4.mem_bank[0].initial_params.watermark     = FFS_MB_PRIM_WATERMARK;
+
+	/* linked SWE info */
+	infoSWE->type_info.type4.nb_linked_swe = 0;
+
+	/* generic functions */
+	infoSWE->type_info.type4.set_info = ffs_set_info;
+	infoSWE->type_info.type4.init     = ffs_init;
+	infoSWE->type_info.type4.core     = ffs_start;
+	infoSWE->type_info.type4.stop     = ffs_stop;
+	infoSWE->type_info.type4.kill     = ffs_kill;
+
+	/* Set return_path */
+	infoSWE->type_info.type4.return_path.callback_func = NULL;
+	infoSWE->type_info.type4.return_path.addr_id       = 0;
+
+	return RV_OK;
+}
+
+
+/******************************************************************************
+* Function	  : ffs_set_info
+*
+* Description : This function is called by the RV manager to inform  
+*				the driver SWE about task_id, mb_id and error function.
+*
+* Parameters  : - T_RVF_ADDR_ID  addr_id: unique path to the SWE.
+*					 - T_RV_RETURN		ReturnPath[], array of return path for linked SWE
+*					 - T_RVF_MB_ID mbId[]: array of memory bank ids.
+*					 - callback function to call in case of unrecoverable error.
+*
+* Return      : T_RVM_RETURN
+* 
+* History	  : 0.1 (20-August-2000)
+*									
+*
+******************************************************************************/
+T_RVM_RETURN ffs_set_info(T_RVF_ADDR_ID  addr_id,
+										T_RV_RETURN		ReturnPath[],
+										T_RVF_MB_ID mbId[],
+										T_RVM_RETURN (*callBackFct)(T_RVM_NAME SWEntName,
+																		T_RVM_RETURN errorCause,
+																		T_RVM_ERROR_TYPE errorType,
+																		T_RVM_STRING errorMsg))
+{
+	/* store the pointer to the error function */
+	ffs_error_ft = callBackFct ;
+
+   ffs_task_init(mbId[0], addr_id);
+
+	ffs_task_info.addr_id = addr_id;
+	ffs_task_info.mbid = mbId[0];
+
+	return RV_OK;
+}
+
+
+/******************************************************************************
+* Function	  : ffs_init
+*
+* Description : This function is called by the RV manager to initialize the 
+*				ffs SWE before creating the task and calling ffs_start. 
+*
+* Parameters  : None
+*
+* Return      : T_RVM_RETURN
+* 
+* History	  : 0.1 (20-August-2000)
+*									
+*
+******************************************************************************/
+T_RVM_RETURN ffs_init(void)
+{
+	return RV_OK;
+}
+
+
+/******************************************************************************
+* Function	  : ffs_start
+*
+* Description : This function is called by the RV manager to start the ffs
+*				SWE, it is the body of the task.
+*
+* Parameters  : None
+*
+* Return      : T_RVM_RETURN
+* 
+* History	  : 0.1 (20-August-2000)
+*									
+*
+******************************************************************************/
+T_RVM_RETURN ffs_start(void)
+{
+    ffs_task();
+	return RV_OK;
+}
+
+
+/******************************************************************************
+* Function	  : ffs_stop
+*
+* Description : This function is called by the RV manager to stop the ffs SWE.
+*
+* Parameters  : None
+*
+* Return      : T_RVM_RETURN
+* 
+* History	  : 0.1 (20-August-2000)
+*									
+*
+******************************************************************************/
+T_RVM_RETURN ffs_stop(void)
+{
+	/* other SWEs have not been killed yet, ffs can send messages to other SWEs */
+
+	return RV_OK;
+}
+
+
+/******************************************************************************
+* Function	  : ffs_kill
+*
+* Description : This function is called by the RV manager to kill the ffs 
+*				SWE, after the ffs_stop function has been called.
+*
+* Parameters  : None
+*
+* Return      : T_RVM_RETURN
+* 
+* History	  : 0.1 (20-August-2000)
+*									
+*
+******************************************************************************/
+T_RVM_RETURN ffs_kill (void)
+{
+	/* free all memory buffer previously allocated */
+	return RV_OK;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/services/ffs/ffstrace.c	Mon Jan 06 04:20:29 2014 +0000
@@ -0,0 +1,206 @@
+/******************************************************************************
+ * Flash File System (ffs)
+ * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
+ *
+ * ffs deprecated testing
+ *
+ * $Id: ffstrace.c 1.32.1.10 Thu, 18 Dec 2003 10:50:52 +0100 tsj $
+ *
+ ******************************************************************************/
+
+#ifndef TARGET
+#include "ffs.cfg"
+#endif
+
+#include "ffs/ffs.h"
+#include "ffs/board/drv.h"
+#include "ffs/board/ffstrace.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/******************************************************************************
+ * LED control
+ *****************************************************************************/
+
+#if (TARGET == 1)
+
+#include "rvf/rvf_api.h"
+#include "rv/rv_general.h"
+#include "rvm/rvm_use_id_list.h"
+
+static uint8 led_state     =   0;
+static uint8 led_countbits =   0; // number of counter bits
+static uint8 led_mask      = 0x0; // mask containing the counter bits
+
+// configure the number of counter bits in the leds
+void led_config(unsigned char n)
+{
+    led_countbits = (n <= 8 ? n : 0);
+    led_mask      = (n <= 8 ? (1 << n) - 1 : 0);
+}
+
+// just set the bits, no checking whatsoever
+void led_set(unsigned char n)
+{
+    *(char *) 0x2800000 = led_state = n;
+}
+
+void led_counter(unsigned char n)
+{
+    *(char *) 0x2800000 = led_state = led_state & ~led_mask | (n & led_mask);
+}
+
+void led_on(unsigned char n)
+{
+    *(char *) 0x2800000 = led_state = led_state | (1 << (led_countbits + n));
+}
+
+void led_off(unsigned char n)
+{
+    *(char *) 0x2800000 = led_state = led_state & ~(1 << (led_countbits + n));
+}
+// FIXME
+void led_toggle(unsigned char n)
+{
+    *(char *) 0x2700000 = led_state = led_state ^ (1 << (led_countbits + n));
+}
+#endif
+
+
+/******************************************************************************
+ * Target Tracing
+ *****************************************************************************/
+
+#if (TARGET == 1)
+
+static unsigned int ttr_mask = TTrFatal | TTrTest;
+
+void ttr_init(unsigned int mask)
+{
+    ttr_mask = mask | TTrFatal | TTrTest;
+}
+
+void ttr(unsigned int mask, char *format, ...)
+{
+    va_list args;
+    static char buf[256];
+
+    if (ttr_mask & mask)
+    {
+        // build string ala tr() then call str()
+        va_start(args, format);
+        vsprintf(buf, format, args);
+        str(mask, buf);
+        va_end(args);
+    }
+}
+
+void str(unsigned mask, char *string)
+{
+    if (ttr_mask & mask) {
+        rvf_send_trace(string, strlen(string), NULL_PARAM,
+                       RV_TRACE_LEVEL_WARNING, FFS_USE_ID);
+        rvf_delay(5);
+    }
+}
+
+
+/******************************************************************************
+ ** PC side Tracing and logging
+ *****************************************************************************/
+
+#else // (TARGET == 0)
+
+static int tr_mask;    // bitmask of which modules to trace
+
+static int tr_spaces;  // number of spaces to indent per level
+static FILE *tr_fd;    // unused; file descriptor of file to write traces to
+
+
+void tr_init(unsigned int mask, int spaces, char *filename)
+{
+    tr_mask = mask;
+    tr_spaces = spaces;
+
+    if (filename == NULL) {
+        tr_fd = stdout;
+    }
+    else {
+        if ( !(tr_fd = fopen(filename, "a+b")) ) {
+            fprintf(stderr, "failed to open logfile: %s for append\n", filename);
+            exit(1);
+        }
+    }
+}
+
+// Trace/Log the printf-like string if abs(level) > tr_level. If level is
+// negative, the sematics are the same except that no indentation will occur
+void tr(int type, unsigned int mask, char *format, ...)
+{
+    va_list args;
+    int indent;
+    static int indent_level = 0;
+    static char buf[1024];
+    const char spaces[] =
+        "                                        "
+                           "                                        "
+                           "                                        "
+                           "                                        ";
+        
+    if ((mask & tr_mask) == 0)
+        return;
+
+    // If tracing/debugging trace system
+    if ((tr_mask & TrTrace) && (type & TR_END))
+        fprintf(tr_fd, "END(%d)\n", indent_level);
+    
+    if (type & TR_END)
+        indent_level--;
+
+    indent = (type & TR_NULL ? 0 : indent_level);
+
+    if (strlen(format) > 0)
+    {
+        va_start(args, format);
+        vsprintf(buf, format, args);
+        
+        indent = tr_spaces * indent;
+        if (indent < 0) {
+            fprintf(tr_fd, "WARNING: tr() indenting too left (%d)\n",
+                    indent_level);
+            indent = 0;
+        }
+        if (indent > sizeof(spaces) - 1) {
+            fprintf(tr_fd, "WARNING: tr() indenting too right (%d)\n",
+                    indent_level);
+            indent = sizeof(spaces) - 1;
+        }
+        fprintf(tr_fd, "%s%s", &spaces[sizeof(spaces) - 1 - indent], buf);
+        fflush(tr_fd);
+    }
+    if (type & TR_BEGIN)
+        indent_level++;
+
+    // If tracing/debugging trace system
+    if ((tr_mask & TrTrace) && (type & TR_BEGIN))
+        fprintf(tr_fd, "BEGIN(%d)\n", indent_level);
+}
+
+#endif // (TARGET == 0)
+
+
+/******************************************************************************
+ ** Common Tracing and logging
+ *****************************************************************************/
+
+int tr_query(int mask)
+{
+#if (TARGET == 1)
+    return (ttr_mask & mask);
+#else
+    return (tr_mask & mask);
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/services/ffs/fsck.c	Mon Jan 06 04:20:29 2014 +0000
@@ -0,0 +1,1475 @@
+/******************************************************************************
+ * Flash File System (ffs)
+ * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
+ *
+ * FFS file system integrity checking, journalling, init and exit
+ *
+ * $Id: fsck.c 1.3.1.1.1.33 Thu, 08 Jan 2004 15:05:23 +0100 tsj $
+ *
+ ******************************************************************************/
+
+#ifndef TARGET
+#include "ffs.cfg"
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include "ffs/ffs.h"
+#include "ffs/board/core.h"
+#include "ffs/board/drv.h"
+#include "ffs/board/ffstrace.h"
+
+/******************************************************************************
+ * Functions
+ ******************************************************************************/
+
+bref_t blocks_fsck(void);
+iref_t inodes_fsck(void);
+
+/******************************************************************************
+ * Init and Exit
+ ******************************************************************************/
+
+effs_t ffs_initialize(void)
+{
+    bref_t b;
+    struct inode_s *ip;
+    int i;
+    
+    tlw(led_set(0));
+    tlw(led_on(LED_INIT));
+    ttw(str(TTrInit, "initialize {" NL));
+    tw(tr(TR_BEGIN, TrFsck, "ffs_initialize() {\n"));
+
+    // default to non-initialized ffs
+    fs.root = 0;
+    fs.debug[0] = fs.debug[1] = fs.debug[2] = fs.debug[3] = 0;
+    fs.testflags = 0;
+
+    tlw(led_on(LED_DRV_INIT));
+    fs.initerror = ffsdrv_init();  // read manufacturer and device ID
+    tlw(led_off(LED_DRV_INIT));
+    if (fs.initerror < 0) {
+        tlw(led_off(0));
+        tw(tr(TR_END, TrFsck, "} %d\n", fs.initerror));
+        ttw(ttr(TTrInit, "} %d" NL, fs.initerror));
+        return fs.initerror;
+    }
+
+    for (i = 0; i < 2; i++)
+    {
+        tlw(led_on(LED_BLOCKS_FSCK));
+        fs.initerror = EFFS_INVALID;
+        fs.initerror = b = blocks_fsck();
+        tlw(led_off(LED_BLOCKS_FSCK));
+        if (fs.initerror < 0) {
+            tlw(led_off(0));
+            tw(tr(TR_END, TrFsck, "} %d\n", fs.initerror));
+            ttw(ttr(TTrInit, "} %d" NL, fs.initerror));
+            return fs.initerror;
+        }
+        
+        tlw(led_on(LED_INODES_FSCK));
+        fs.initerror = EFFS_INVALID;
+        fs.initerror = inodes_fsck();
+        tlw(led_off(LED_INODES_FSCK));
+        if (fs.initerror < 0) {
+            tlw(led_off(0));
+            tw(tr(TR_END, TrFsck, "} %d\n", fs.initerror));
+            ttw(ttr(TTrInit, "} %d" NL, fs.initerror));
+            return fs.initerror;
+        }
+
+        // parse the fs options in the root inode's name
+        ip = inode_addr(fs.root);
+        fs_params_init(addr2name(offset2addr(location2offset(ip->location))));
+
+        if ((fs.initerror = journal_init(fs.ijournal)) == 0)
+            break;    
+    }
+
+	// Init all file_descriptors to zero
+	memset(fs.fd, 0, sizeof(struct file_descriptor_s) * fs.fd_max);
+
+    // If blocks_fsck() found a block that needs cleaning, we do it, now
+    // that all the file system has been initialized.
+    if (b > 0) {
+        block_clean(b - 1);
+		block_free(b - 1);
+    }
+
+    statistics_init();
+
+    // In target, we do this before entering the task event loop...
+    // Otherwise we would in some cases impose a long reboot delay if we did
+    // it here. If we test in target it is nessesary to call
+    // blocks_reclaim() anyway because we re-init ffs.
+
+#if (TARGET == 1)         //NOTEME: can this be done in another/better way?
+#if (WITH_TFFS == 1)
+    blocks_reclaim(); 
+#endif
+#else
+    blocks_reclaim();
+#endif
+    tlw(led_off(LED_INIT));
+    tw(tr(TR_END, TrFsck, "} %d\n", EFFS_OK));
+    ttw(str(TTrInit, "} 0" NL));
+
+    return EFFS_OK;
+}
+
+void fs_params_init(const char *p)
+{
+    uint8 opt, digit;
+    uint32 n;
+	int numdatablocks;
+
+    tw(tr(TR_BEGIN, TrFsck, "fsparams_init('%s') {\n", p));
+
+	// Compiled default values
+    fs.filename_max   = FFS_FILENAME_MAX;
+	fs.path_depth_max = FFS_PATH_DEPTH_MAX;
+    fs.fd_max         = FFS_FD_MAX;
+	fs.journal_size   = FFS_JOURNAL_SIZE_IN256THS;
+	fs.flags          = 0;
+    fs.testflags      = 0;
+	
+    // Flag that it not has been changed by an input arg.
+	fs.block_files_max = 0;
+
+    // The default lost bytes percentage of a block before it is reclaimed
+    // is approx. 90%.
+    fs.lost_threshold  = (256 - 256/10);
+
+    // If we only have two blocks, we cannot make any reclaims and thus we
+    // have a write-once FFS system.
+    fs.blocks_free_min = (dev.numblocks > 2 ? 1 : 0);
+
+    // Don't count free and inodes blocks
+	numdatablocks = dev.numblocks - fs.blocks_free_min - 1;
+
+	// Abselute max number of inodes.
+    fs.inodes_max = dev.blocksize / sizeof(struct inode_s);
+    if (fs.inodes_max > FFS_INODES_MAX)
+		fs.inodes_max = FFS_INODES_MAX;
+
+   	// MUST be true: objects_max <= inodes_max - block_files_max, this is do
+   	// to the fact that we always need to have block_files_max number of
+   	// inodes left when we run a data reclaim.
+    fs.objects_max = fs.inodes_max / 2;
+
+	// Find a suitable chunk_size
+    if (dev.numblocks*dev.blocksize > 1024*1024)
+        fs.chunk_size_max = 8192;
+    else
+        fs.chunk_size_max = (2048 > (dev.blocksize / 8) 
+                             ? (dev.blocksize / 8)
+                             : 2048);
+    fs.fd_buf_size = fs.chunk_size_max;
+    
+    fs.journal_size = fs.journal_size * dev.blocksize / 256;
+    if (fs.journal_size < FFS_JOURNAL_SIZE_MIN)
+        fs.journal_size = FFS_JOURNAL_SIZE_MIN;
+
+    // Set it just below the same amount as entries in one journal file
+    fs.block_files_max = (fs.journal_size / sizeof(struct journal_s) 
+                          - FFS_JOURNAL_MARGIN - 2);
+
+    // MUST be true: block_files_max < objects_max / 2. But if we want
+    // to reach objects_max must block_files_max >= objects_max / number
+    // of datablocks, however a big block_files_max require higher
+    // reserved_space.
+    if (fs.block_files_max > fs.objects_max / 2)
+        fs.block_files_max = fs.objects_max / 2 - 4;
+		
+    // Are we able to reach objects_max? If not then lower the number
+    if (fs.objects_max > numdatablocks * fs.block_files_max)
+        fs.objects_max = numdatablocks * fs.block_files_max + 10;
+
+    // Absolute minimum is RESERVED_LOW the rest is 'workspace' which is
+    // needed to have a reasonable performance.
+    fs.reserved_space = dev.blocksize / 2 + 
+        numdatablocks * dev.blocksize / 16 + RESERVED_LOW; 
+    
+    // skip to first char following second slash in name
+    n = 0;
+    while (*p) {
+        if (*p++ == '/') {
+            n++;
+            if (n == 2)
+                break;
+        }
+    }
+    if (n == 2) {
+        // while still options to process...
+        while (*p) {
+            opt = *p++; // save option letter for later
+            // collect option value...
+            n = 0;
+            while ((digit = *p)) {
+                if (digit >= '0' && digit <= '9') {
+                    n = 10 * n + digit - '0';
+                    p++;
+                }
+                else
+                    break;
+            }
+            switch (opt) {
+            case 'b': dev.numblocks = n;       break;
+            case 'm': fs.blocks_free_min = n;  break;
+            case 'i': fs.inodes_max = n;       break;
+            case 'o': fs.objects_max = n;      break;
+            case 'n': fs.filename_max = n;     break;
+            case 'f': fs.block_files_max = n;  break;
+            case 'd': fs.fd_max = n;           break;
+            case 's': fs.fd_buf_size = n;      break;
+            case 't': fs.lost_threshold = n;   break;
+            case 'z': fs.flags = n;            break;
+            case 'j': fs.journal_size = n;     break;
+            case 'c': fs.chunk_size_max = n;   break;
+            case 'r': fs.reserved_space = n;   break;
+                // d = &fs.path_depth_max;  // really necessary?
+            default:
+                break;
+            }
+        }
+    }
+
+    // Now recompute a few parameters based on adjusted values.
+
+	// No journal file thuse no reserved space.
+	if (fs.journal_size == 0) {   
+		fs.block_files_max = fs.objects_max / 2;
+		fs.reserved_space = 0;
+		fs.block_files_reserved = 0;
+	}
+
+	else {
+        // If journal size is less than minimum must it have been changed by an
+        // input arg, recalculate.
+        if (fs.journal_size < FFS_JOURNAL_SIZE_MIN)
+            fs.journal_size = fs.journal_size * dev.blocksize / 256;
+
+        if (fs.reserved_space < RESERVED_LOW)
+            fs.reserved_space = fs.reserved_space * dev.blocksize / 256;
+
+		// Only one reserved is needed however we want a margin and set it to two 
+		fs.block_files_reserved = 2;
+    }
+	
+	// Don't count free blocks, inode block, reserved space, block headers
+	// and the size of one filename.
+	fs.filesize_max = numdatablocks * dev.blocksize - fs.reserved_space - 
+		numdatablocks * BHEADER_SIZE - FFS_FILENAME_MAX;
+	
+	// Furthermore don't count the overhead from each chunk (alignment) 
+	fs.filesize_max -= ((fs.filesize_max / fs.chunk_size_max) * dev.atomsize 
+                        + dev.atomsize);
+	
+    // NOTEME: chunk_size_min is never used
+	fs.chunk_size_min = numdatablocks / fs.objects_max;
+    
+    tw(tr(TR_FUNC, TrFsck, "dev.numblocks      = %d\n", dev.numblocks));
+    tw(tr(TR_FUNC, TrFsck, "fs.blocks_free_min = %d\n", fs.blocks_free_min));
+    tw(tr(TR_FUNC, TrFsck, "fs.inodes_max      = %d\n", fs.inodes_max));
+    tw(tr(TR_FUNC, TrFsck, "fs.objects_max     = %d\n", fs.objects_max));
+    tw(tr(TR_FUNC, TrFsck, "fs.block_files_max = %d\n", fs.block_files_max));
+	tw(tr(TR_FUNC, TrFsck, "fs.block_files_reserved    = %d\n", fs.block_files_reserved));
+    tw(tr(TR_FUNC, TrFsck, "fs.chunk_size_max  = %d\n", fs.chunk_size_max));
+    tw(tr(TR_FUNC, TrFsck, "fs.filename_max    = %d\n", fs.filename_max));
+    tw(tr(TR_FUNC, TrFsck, "fs.lost_threshold  = %d\n", fs.lost_threshold));
+    tw(tr(TR_FUNC, TrFsck, "fs.path_depth_max  = %d\n", fs.path_depth_max));
+    tw(tr(TR_FUNC, TrFsck, "fs.journal_size    = %d\n", fs.journal_size));
+    tw(tr(TR_FUNC, TrFsck, "fs.reserved_space  = %d\n", fs.reserved_space));
+    tw(tr(TR_FUNC, TrFsck, "fs.fd_max          = %d\n", fs.fd_max));
+    tw(tr(TR_FUNC, TrFsck, "fs.fd_buf_size     = 0x%02x\n", fs.fd_buf_size));
+    tw(tr(TR_FUNC, TrFsck, "fs.flags           = 0x%02x\n", fs.flags));
+    tw(tr(TR_END,  TrFsck, "}\n"));
+}
+
+// TODO: Finish pending commits/writes. 
+effs_t ffs_exit(void)
+{
+    tw(tr(TR_FUNC, TrFsck, "exit() 0\n"));
+
+
+    return EFFS_OK;
+}
+
+#if 0 // Not used in this version
+// Purely for core internal use; Read a file.
+effs_t file_read_int(const char *path, void *src, int size)
+{
+    if (fs.initerror != EFFS_OK)
+        return fs.initerror;
+
+    return object_read(path, src, size, 0);
+}
+
+// Purely for core internal use; Update a file.
+effs_t file_update(const char *path, void *src, int size)
+{
+    char *name;
+    iref_t i, dir;
+    
+    if (fs.initerror != EFFS_OK)
+        return fs.initerror;
+
+    if ((i = object_lookup(path, &name, &dir)) < 0)
+        return i;
+    
+    journal_begin(i);
+
+    if ((i = object_create(name, src, size, -dir)) < 0)
+        return i;
+
+    journal_end(0);
+
+    return EFFS_OK;
+}
+#endif
+
+/******************************************************************************
+ * blocks_fsck()
+ ******************************************************************************/
+
+blocksize_t block_used(bref_t b)
+{
+    blocksize_t used;
+    uint32 *p, *q;
+
+    tlw(led_toggle(LED_BLOCKS_FSCK));
+        
+    // We search backwards through block to find the last used byte and
+    // thus the total number of used bytes. Note that this code depends
+    // on the fact that an erased flash location is 0xFF!
+    p = (uint32 *) offset2addr(dev.binfo[b].offset);
+    for (q = p + dev.blocksize/4 - 4; q > p; q -= 4) {
+        if ( ~(q[0] & q[1] & q[2] & q[3]) )
+            break;
+    }
+
+    if ( ~(q[0] & q[1] & q[2] & q[3]) )
+        q += 4;
+    used = atomalign((char *) q - (char *) p);
+
+    tw(tr(TR_FUNC, TrFsckLow, "ffs_block_used(%d) %d\n", b, used));
+
+    return used;
+}
+
+
+age_t age_distance(age_t x, age_t y)
+{
+    age_t a = x - y;
+    
+    if (a > 0x8000)
+        a = -a;
+
+    tw(tr(TR_FUNC, TrFsckLow, "age_distance(%d, %d) %d\n", x, y, a));
+    
+    return a;
+}
+
+// For each ffs block, we initialise the basic bstat array information,
+// namely the number of used bytes. Also, we locate the inodes block and if
+// a previous operation was interrupted by a powerfail, we clean it up.
+//
+// We return EFFS_OK if all is fine. If a positive integer is returned, it
+// denotes a block that needs to be cleaned by block_clean() once FFS
+// has been properly intialized (we actually return the block number + 1
+// because otherwise it would clash with EFFS_OK return code). If no inodes
+// block is found or another error occurs, we return the error code.
+bref_t blocks_fsck(void)
+{
+	bref_t b, b_to_clean, b_inode_lost;
+    int age_valid;
+    age_t age_min, age_max, age_dist, age_dist_min, age_dist_max;
+    struct block_header_s *bhp;
+    struct block_header_old_s *obhp;
+
+    ttw(str(TTrInitLow, "blocks_fsck {" NL));
+    tw(tr(TR_BEGIN, TrFsck, "blocks_fsck() {\n"));
+
+    // initialize ages to the illegal/unset value
+    age_min = age_max = age_dist = 0;
+
+    fs.format    =  0;
+    fs.inodes    = -1;
+    fs.newinodes = -1;
+	b_inode_lost = -1;
+    b_to_clean   = EFFS_OK;
+
+    for (b = 0; b < dev.numblocks; b++)
+    {
+        tlw(led_toggle(LED_DRV_INIT));
+
+        // read block flags from flash
+        bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset);
+        obhp = (struct block_header_old_s *) bhp;
+
+        bstat[b].used    = dev.blocksize;
+        bstat[b].lost    = bstat[b].used;
+        bstat[b].flags   = bhp->flags;
+        bstat[b].objects = 0;
+
+        age_valid = 0;
+
+        if (bhp->magic_low  != BLOCK_MAGIC_LOW ||
+            bhp->magic_high != BLOCK_MAGIC_HIGH) {
+            // The block magic as bad! It *could* be because the flash
+            // memory map is incorrect or because another application has
+            // spuriously written to the flash or ... who knows what. First
+            // we check to see if the reason is that we are dealing with a
+            // (really) old ffs format version.
+            if (obhp->magic_low == OLD_BLOCK_MAGIC_LOW &&
+                obhp->magic_high == OLD_FFS_FORMAT_VERSION) {
+                tw(tr(TR_FUNC, TrFsck, "OLD     "));
+                fs.format = obhp->magic_high;
+                // We simulate that all the blocks are data blocks, in order
+                // to have some well-defined state that preformat() can work
+                // on. Later we will return EFFS_BADFORMAT and otherwise
+                // leave everything as it is, *without* modifying anything!
+                bstat[b].flags = BF_IS_DATA;
+            }
+            else {
+                // Quickly test if block is in empty state. We do not make a
+                // full check with block_used() because that takes too
+                // long --- we let preformat() do that.
+                if (bhp->magic_low  == FLASH_NULL16 &&
+                    bhp->magic_high == FLASH_NULL16 &&
+                    bhp->age        == FLASH_NULL16 &&
+                    bhp->version    == FLASH_NULL16 &&
+                    bhp->flags      == FLASH_NULL16)
+                {
+                    bstat[b].used  = 0;
+                    bstat[b].lost  = 0;
+                    bstat[b].flags = BF_IS_EMPTY;
+                    tw(tr(TR_FUNC, TrFsck, "EMPTY     "));
+                }
+                else {
+                    // If the block is not free, it is probably corrupted.
+                    // Thus we reset its age and free it.
+                    tw(tr(TR_FUNC, TrFsck, "magic = 0x%08x\n",
+                          bhp->magic_low | (bhp->magic_high << 16)));
+                    ffsdrv.write_halfword(&bhp->age, 0);
+                    block_free(b);
+                    tw(tr(TR_FUNC, TrFsck, "BAD       "));
+                }
+            }
+        }
+        else {
+            fs.format = bhp->version;
+            age_valid = 1;
+
+            if (!is_block(b, BF_IS_FREE)) {
+                bstat[b].used = block_used(b);
+                bstat[b].lost = bstat[b].used - BHEADER_SIZE; 
+            }
+
+            if (is_block(b, BF_IS_FREE)) {
+                // The only case where we do not call block_used() is
+                // when the block is truly free.
+                bstat[b].used = 0;
+                bstat[b].lost = 0;
+                tw(tr(TR_FUNC, TrFsck, "FREE      "));
+                ttw(ttr(TTrInitLow, "FREE" NL));
+
+            }
+            else if (is_block(b, BF_IS_DATA)) {
+                tw(tr(TR_FUNC, TrFsck, "DATA      "));
+                ttw(ttr(TTrInitLow, "DATA" NL)); 
+            }
+            else if (is_block(b, BF_IS_CLEANING)) {
+                // Here we schedule a block_clean(). Note that we can
+                // and do not execute the block cleaning now, as the info
+                // that block_clean() needs is not at all ready at this
+                // point in the initialization. So we set a flag and then
+                // clean the block at the end of ffs_initialize()
+                tw(tr(TR_FUNC, TrFsck, "CLEANING  "));
+                ttw(ttr(TTrInitLow, "CLEANING" NL)); 
+                b_to_clean = b + 1;
+            }
+            else if (is_block(b, BF_IS_COPYING)) {
+                tw(tr(TR_FUNC, TrFsck, "COPYING   "));
+                ttw(ttr(TTrInitLow, "COPYING" NL)); 
+                fs.newinodes = b;
+            }
+            else if (is_block(b, BF_IS_INODES)) {
+                tw(tr(TR_FUNC, TrFsck, "INODES    "));
+                ttw(ttr(TTrInitLow, "INODES" NL)); 
+                    fs.inodes = b;
+            }
+            else if (is_block(b, BF_IS_INODES_LOST)) {
+                tw(tr(TR_FUNC, TrFsck, "INODESLOST"));
+                ttw(ttr(TTrInitLow, "INODESLOST" NL)); 
+                b_inode_lost = b;
+            }
+            else {
+                block_free(b);
+                tw(tr(TR_FUNC, TrFsck, "INVALID   "));
+                ttw(ttr(TTrInitLow, "INVALID" NL)); 
+            }
+        }
+
+        tw(tr(TR_NULL, TrFsck, " %2d: (0x%05x) %02x, used = %6d\n",
+              b, dev.binfo[b].offset, bstat[b].flags & 0xFF, bstat[b].used));
+
+        if (age_valid) {
+            if (age_min == 0) {
+                // Initialize minimum and maximum block ages
+                age_min = age_max = bhp->age;
+                tw(tr(TR_FUNC, TrFsckLow, "age_min/max = %d\n", age_min));
+            }
+            else {
+                age_dist_min = age_distance(bhp->age, age_min);
+                age_dist_max = age_distance(bhp->age, age_max);
+                if (age_dist_min > age_dist ||
+                    age_dist_max > age_dist) {
+                    if (age_dist_max > age_dist_min) {
+                        age_dist = age_dist_max;
+                        age_min  = bhp->age;
+                        tw(tr(TR_FUNC, TrFsckLow, "age_min = %d (dist = %d)\n",
+                              age_min, age_dist));
+                    }
+                    else {
+                        age_dist = age_dist_min;
+                        age_max  = bhp->age;
+                        tw(tr(TR_FUNC, TrFsckLow, "age_max = %d (dist = %d)\n",
+                              age_max, age_dist));
+                    }
+                }
+            }
+        }
+    }
+    tlw(led_off(LED_DRV_INIT));
+    tw(tr(TR_FUNC, TrFsck, "age min, max, max-min = %d, %d, %d\n",
+          age_min, age_max, (uint16) (age_max-age_min)));
+    // If age_max is untouched is is because all blocks were in the 'Empty'
+    // state. In this case we let the age be as it is (0xFFFF).
+    if (age_max == 0)
+        age_max = age_min = BLOCK_AGE_MAX;
+
+    // Handle age wrap around thus ensuring fs.age_max is set correctly. We
+    // have to type-cast the whole computation, otherwise it will be
+    // incorrect.
+    if ((age_t) (age_max - age_min) > 0x8000) {
+        age_dist = age_max;
+        age_max  = age_min;
+        age_min  = age_dist;
+    }
+
+    // save maximum age found for the case of a bad block that is going to
+    // be reclaimed later on by blocks_reclaim()
+    fs.age_max = age_max;
+
+    tw(tr(TR_FUNC, TrFsck, "fs.format = 0x%04x\n", fs.format));
+    tw(tr(TR_FUNC, TrFsck, "fs.inodes, newinodes = %d, %d\n",
+          fs.inodes, fs.newinodes));
+    ttw(ttr(TTrInit, "fs.inodes, newinodes = %d, %d" NL, 
+            fs.inodes, fs.newinodes));
+    tw(tr(TR_FUNC, TrFsck, "age min, max = %d, %d\n", age_min, age_max));
+    
+    // If any blocks were in the EMPTY state, now is the time to bring them
+    // into the FREE state. Note that we must only do this *after*
+    // fs.age_max has been initialized.
+    for (b = 0; b < dev.numblocks; b++) {
+        if (is_block(b, BF_IS_EMPTY)) {
+            if ((bstat[b].used = block_used(b)) == 0)
+                block_preformat(b, 0);
+            else
+                block_free(b);
+        }
+    }
+
+	if (fs.inodes >= 0) {  
+        // The 'old' inode block is still valid thus we keep it.
+		if (fs.newinodes >= 0)
+            // The copying of inodes to the new block was not finished thus
+            // we free the block
+			block_free(fs.newinodes);   
+		inodes_set(fs.inodes);
+	}
+	else {                                 
+		// Copying must have been finished
+		if (fs.newinodes >= 0 && b_inode_lost >= 0) {
+            // The inode reclaim did finish but currently there is no valid
+            // inode block thus the operation must be finished by committing
+            // the new block as the valid inode block.
+			fs.inodes = b_inode_lost;
+			block_commit();
+			
+		}
+		else {
+            // No old or new Inode block!
+			tw(tr(TR_END, TrFsck, "} %d\n", EFFS_NOFORMAT));
+			ttw(ttr(TTrInitLow, "} %d" NL, EFFS_NOFORMAT));
+			return EFFS_NOFORMAT;
+		}
+	}
+	
+    if ((fs.format >> 8) != (FFS_FORMAT_VERSION >> 8)) {
+        tw(tr(TR_END, TrFsck, "} %d\n", EFFS_BADFORMAT));
+        ttw(ttr(TTrInitLow, "} %d" NL, EFFS_BADFORMAT));
+        return EFFS_BADFORMAT;
+    }
+
+    // FIXME: Insert age sanity check; age distance must not be too big (> 2
+    // * FFS_AGE_DISTANCE)?
+
+    tw(tr(TR_END, TrFsck, "} %d\n", b_to_clean));
+    ttw(ttr(TTrInitLow, "} %d" NL, b_to_clean));
+
+    return b_to_clean;
+}
+
+// Set fs.inodes and fs.inodes_addr
+void inodes_set(iref_t i)
+{
+    fs.inodes = i;
+    fs.inodes_addr = (struct inode_s *)
+        (offset2addr(dev.binfo[fs.inodes].offset)
+         + dev.atomsize - sizeof(struct inode_s));
+}
+
+
+/******************************************************************************
+ * inodes_fsck()
+ ******************************************************************************/
+
+// Now for each inode in the inodes block, update the bstat array
+// information: free, used, objects. Also, locate the root inode. We could
+// optimize this a little, because bstat[binodes].used gives an inidication
+// of how many inodes are actually present in the system.
+iref_t inodes_fsck(void)
+{
+    iref_t i;
+    struct inode_s *ip;
+    char *addr;
+    bref_t block;
+
+    ttw(str(TTrInitLow, "inodes_fsck {" NL));
+    tw(tr(TR_BEGIN, TrFsck, "inodes_fsck() {\n"));
+    tw(tr(TR_FUNC, TrFsck, "inodes in block %d:\n", fs.inodes));
+
+    // the fields of the bstat entry for the inodes have the meaning:
+    // used = total number of used inodes (valid, erased, invalid)
+    // lost = total number of lost inodes (erased, invalid)
+    // objects = index of first free inode (used by inode_alloc())
+
+    fs.root = 0;     // default to root inode not found
+    fs.ijournal = 0; // default to journal file inode not found
+    bstat[fs.inodes].objects = 1;
+    bstat[fs.inodes].used = 0;
+    bstat[fs.inodes].lost = 0;
+    fs.sequence = 0; // just for debug (fun)
+
+    // we must set some default value for this, so we set it to max possible!
+    fs.inodes_max = dev.blocksize / sizeof(struct inode_s);
+
+    ip = inode_addr(1);
+    tw(tr(TR_FUNC, TrFsck, "  i    addr  cld sib  seq upd  flag size name\n"));
+    for (i = 1; i < fs.inodes_max; i++, ip++)
+    {
+        // just for debug (fun)
+        if (ip->sequence > fs.sequence)
+            fs.sequence = ip->sequence;
+                
+        // compute block index and total data space occupied
+        block = offset2block(location2offset(ip->location));
+
+        // Only scan used inodes. blocks_fsck() accounted all used space as
+        // also being lost space, so now we subtract from the lost space,
+        // the space used by valid objects
+        if (ip->location != FLASH_NULL32)
+        {
+            bstat[fs.inodes].used++;
+
+            tw(tr(TR_FUNC, TrFsck, "%3d 0x%05X  %3d %3d %4d %3d  %s%s%s%s%s%s %6d %s\n",
+                  i,
+                  location2offset(ip->location),
+                  ip->child, ip->sibling,
+                  ip->sequence, ip->updates,
+                  is_object(ip, OT_DIR)     ? "d" : "",
+                  is_object(ip, OT_LINK)    ? "l" : "",
+                  is_object(ip, OT_FILE)    ? "f" : "",
+                  is_object(ip, OT_SEGMENT) ? "s" : "",
+                  is_object(ip, OT_ERASED)  ? " " : "",
+                  IS_BIT_SET(ip->flags, OF_READONLY) && !is_object(ip, OT_ERASED) ?
+                  "r" : " ",
+                  ip->size,
+                  // Erased chunks do not have any name so we can not trace erased objects!
+                  (ip->size && !is_object(ip, OT_SEGMENT) && !is_object(ip, OT_ERASED) ? 
+                   addr2name(offset2addr(location2offset(ip->location))) : "")
+                  ));
+            
+            if (is_object_valid(ip)) {
+                // This inode is valid, so we account the data space as used
+                // and the inode as used too.
+                bstat[block].lost -= ip->size;
+                bstat[block].objects++;
+                // test if this is the root inode. store index if it is.
+                if (!is_object(ip, OT_SEGMENT)) {
+                    addr = addr2name(offset2addr(location2offset(ip->location)));
+                    if (*addr == '/')
+                        fs.root = i;
+                    else if (*addr == '.' &&
+                             ffs_strcmp(addr, FFS_JOURNAL_NAME) == 0) {
+                        fs.ijournal = i;
+                    }
+                }
+            }
+            else if (is_object(ip, OT_ERASED)) {
+                // this inode's data is deleted, so we account the data
+                // space as used and lost and the inode as lost too.
+                bstat[fs.inodes].lost++;
+            }
+            else {
+                // This is an invalid object, so we account the data space
+                // as used and lost and the inode as lost too. NOTEME: error
+                // what should we do? Perhaps we should record semi-lost
+                // inodes? Can we safely account for it here if this is an
+                // object to be recovered because another inode.copied is
+                // referring to this?  Will used/lost etc. be updated
+                // correctly then?
+                bstat[fs.inodes].lost++;
+                tw(tr(TR_NULL, TrFsck, "(invalid = %d)\n", ip->flags & OT_MASK));
+            }
+        }
+    }
+    ttw(ttr(TTrInit, "fs.root=%d, journal=%d" NL, fs.root, fs.ijournal));
+    tw(tr(TR_END, TrFsck, "} used: %d, lost: %d, root: %d, journal: %d\n",
+       bstat[fs.inodes].used, bstat[fs.inodes].lost, fs.root, fs.ijournal));
+
+    fs.sequence++;
+    
+    tw(tr_bstat());
+
+    if (fs.root == 0) {
+        ttw(ttr(TTrInitLow, "} %d" NL, EFFS_NOFORMAT));
+        return EFFS_NOFORMAT;
+    }
+
+    ttw(str(TTrInitLow, "} 0" NL));
+
+    return EFFS_OK;
+}
+
+
+/******************************************************************************
+ * Preformat and format
+ ******************************************************************************/
+
+// Prepare all blocks for fs_format(). Because ffs_is_formattable() has
+// already been called prior to this function, we know that no sector erase
+// is in progress! The blocks are prepared by putting them into the 'Free'
+// state.
+effs_t fs_preformat(void)
+{
+    bref_t b;
+
+    ttw(str(TTrFormat, "preformat {" NL));
+    tw(tr(TR_BEGIN, TrFormat, "fs_preformat() {\n"));
+
+    // Mark ffs as being non-formatted from now on.
+    fs.root = 0;
+
+    // We must initialize bstat[fs.inodes].used and inodes_high, such that
+    // inodes_reclaim() isn't triggered in reclaim() on the following
+    // fs_format().
+    inodes_set(0);
+    bstat[fs.inodes].used    = 0;
+    bstat[fs.inodes].lost    = 0;
+    bstat[fs.inodes].objects = 0;
+
+    // While format is in progress, we make FFS inaccessible to other
+    // functions...
+    fs.initerror = EFFS_NOFORMAT;
+
+    if (dev.manufact == 0) {
+        b = EFFS_NODEVICE;
+    }
+    else {
+        for (b = 0; b < dev.numblocks; b++) {
+            if (is_block(b, BF_IS_EMPTY)) {
+                if ((bstat[b].used = block_used(b)) == 0)
+                    block_preformat(b, 0);
+                else
+                    block_free(b);
+            }
+            else if (!is_block(b, BF_IS_FREE)) {
+                block_free(b);
+            }
+        }
+        b = EFFS_OK;
+    }
+
+    tw(tr(TR_END, TrFormat, "} %d\n", b));
+    ttw(ttr(TTrFormat, "} %d" NL, b));
+
+    return b;
+}
+
+// Preformat a single block thus taking it from the 'Empty' state into
+// 'Free' state.
+void block_preformat(bref_t b, age_t age)
+{
+    int set_age_max;
+    struct block_header_s *bhp =
+        (struct block_header_s *) offset2addr(dev.binfo[b].offset);
+
+    tw(tr(TR_BEGIN, TrFormat, "fs_block_preformat(%d, %d)\n", b, age));
+
+    if (age == 0) {
+        age = fs.age_max;
+    }
+    else {
+        // We schedule an update of fs.age_max. Due to proper handling of
+        // age wrap-around, we can not actually set it now.
+        set_age_max = (age == fs.age_max);
+        age++;
+        if (age == 0)
+            age++;
+        if (set_age_max) {
+            fs.age_max = age;
+            tw(tr(TR_FUNC, TrFormat, "new fs.age_max = %d\n", fs.age_max));
+        }
+    }
+
+    ffsdrv.write_halfword(&bhp->age,        age);
+    ffsdrv.write_halfword(&bhp->version,    FFS_FORMAT_VERSION);
+    ffsdrv.write_halfword(&bhp->magic_low,  BLOCK_MAGIC_LOW);
+    ffsdrv.write_halfword(&bhp->magic_high, BLOCK_MAGIC_HIGH);
+
+    bstat[b].flags = BF_IS_EMPTY;
+    bstat[b].used = 0;
+    bstat[b].lost = 0;
+    bstat[b].objects = 0;
+
+    block_flags_write(b, BF_FREE);
+
+    tw(tr(TR_END, TrFormat, ""));
+}
+
+// After preformat() has erased two blocks, this function can be called to
+// initialize ffs by writing fs data and metadata.  Note that ffs_begin() is
+// *not* called before this function in ffs.c. Otherwise we would never
+// enter this function because fs.root is zero. NOTEME: this is also a bug
+// as this means we risk that this operation is started while an erase (or a
+// write) is in progress! How the flash device reacts to this is currently
+// unknown.
+effs_t fs_format(const char *name)
+{
+    bref_t i, b;
+
+    ttw(str(TTrFormat, "format {" NL));
+    tw(tr(TR_BEGIN, TrFormat, "fs_format('%s') {\n", name));
+
+    // Initialize file system parameters. It should be safe to change these
+    // now, as the format cannot fail at this point onwards.
+    fs_params_init(name);
+
+    // Make the first block be the inodes block
+    if ((fs.inodes = block_alloc(1, BF_COPYING)) < 0)
+        return EFFS_AGAIN;
+    block_flags_write(fs.inodes, BF_INODES);
+    inodes_set(fs.inodes);
+
+    // Make all block as data blocks except from the free_min and inode block
+    for (i = 0; i < dev.numblocks - fs.blocks_free_min - 1; i++) 
+        if ((b = block_alloc(0, BF_DATA)) < 0)
+            return EFFS_AGAIN;
+
+    // Restart object sequencing (debug feature only)
+    fs.sequence = 0;
+
+    // Create root directory
+    journal_begin(0);
+    if ((fs.root = object_create(name, 0, 0, 0)) < 0) {
+        tw(tr(TR_END, TrFormat, "} %d\n", fs.root));
+        return fs.root;
+    }
+    journal_commit(OT_DIR);
+
+    if ((fs.ijournal = journal_create(0)) < 0) {
+        tw(tr(TR_END, TrFormat, "} %d\n", fs.ijournal));
+        return fs.ijournal;
+    }
+
+    fs.initerror = ffs_initialize();
+
+    ttw(ttr(TTrFormat, "} %d" NL, fs.initerror));
+    tw(tr(TR_END, TrFormat, "} %d\n", fs.initerror));
+
+    return fs.initerror;
+}
+
+// Check if we are ready to preformat (flag = 0) or format (flag = 1)
+//
+// For a format, we must first ensure no blocks are valid e.g. a preformat
+// has already been run. Next, we must ensure we have preformatted all
+// blocks e.g. all blocks are in the 'Free' state. This is actually the same
+// thing but it sure helps the user because it yields a more precise error
+// code when the format fails. In future we might be able to start a format
+// when only two blocks have been preformatted, but this is harder because
+// we have to make sure not to read from the physical sector that we are
+// erasing, and this is exactly what ffs_ffs_initialize() currently does
+// (when it is called at the end of format()).
+//
+// For a preformat, we must ensure an erase is not in progress (because we
+// don't know how the device will react to a new erase when an erase is
+// currently suspended).
+effs_t is_formattable(int8 flag)
+{
+    bref_t i, free, valid;
+    effs_t error = EFFS_OK;
+
+    tw(tr(TR_FUNC, TrFormat, "is_formattable() "));
+
+    // Count the number of valid and free blocks. These numbers will later
+    // be checked to see if we are really ready for a (pre)format(). Note
+    // that we *only* read block flags from the bstat[] array. We must not
+    // read directly from the flash sectors because an erase might be in
+    // progress!
+    for (i = 0, free = 0, valid = 0; i < dev.numblocks; i++) {
+        if (is_block(i, BF_IS_DATA) || is_block(i, BF_IS_INODES))
+            valid++;
+        if (is_block(i, BF_IS_FREE))
+            free++;
+    }
+    if (flag == 0) {
+        // In the case of a preformat, ensure an erase is not in
+        // progress (because we don't know how the device will react to a new
+        // erase when an erase is currently suspended).
+        if (dev.state == DEV_ERASE || dev.state == DEV_ERASE_SUSPEND) {
+            tw(tr(TR_NULL, TrFormat, "(%d)\n", EFFS_AGAIN));
+            return EFFS_AGAIN;
+        }
+    }
+    else {
+        if (valid > 0)
+            // Ensure we have preformatted prior to a format.
+            error = EFFS_NOPREFORMAT;
+        else if (free < dev.numblocks)
+            // Ensure all blocks are free before a format(). If not, a
+            // preformat() is currently in progress.
+            error = EFFS_AGAIN;
+    }
+
+    tw(tr(TR_NULL, TrFormat, "(%d)\n", error));
+    return error;
+}
+
+
+/******************************************************************************
+ * Journalling
+ ******************************************************************************/
+
+// The following matrix illustrates how the members of an inode change for
+// the various (journalled) operations:
+//
+//          | flags | size | loc | child | siblg | dir | oldi | updates
+// ---------+-------+------+-----+-------+-------+-----+------+--------
+// create   | new   | new  | new | -     | -     | ins | n/a  | 0
+// fupdate  | o     | new  | new | o     | -     | ins | del  | old+1
+// relocate | o     | o    | new | o     | -     | ins | del  | old+1
+// fctrl    | new   | o    | o   | o     | -     | ins | del  | old+1
+// remove   | n/a   | n/a  | n/a | n/a   | n/a   | n/a | del  | n/a  
+//
+//  -  = leave empty (0xFFFF)
+// ins = insert/append into directory
+// o   = old value
+//
+// We don't have to store child member in the journal entry because either
+// it is EMPTY (fs.journal.oldi = 0) or it is retrieved from oldip->child.
+
+// NOTEME: With journalling implemented, object_relocate might be able just
+// to make a simple data copy!
+
+// block_clean() is safe (without journalling), now that only ip->size is
+// set to zero.
+
+// Begin a new journal. Either a fresh object create (oldi == 0) or an
+// update of an existing object (oldi == iref of old object)
+void journal_begin(iref_t oldi)
+{
+    tw(tr(TR_FUNC, TrJournal, "journal_begin(%d)\n", oldi));
+
+    fs.journal.i = 0;
+    fs.journal.state = JOURNAL_IS_EMPTY;
+    fs.journal.repli = 0;
+    fs.link_child = 1;   //Default link child in journal_commit()
+
+    if (oldi == 0) {
+        fs.journal.flags    = 0xFF;
+        fs.journal.diri     = 0;
+        fs.journal.oldi     = 0;
+        fs.journal.location = 0;
+        fs.journal.size     = 0;
+    }
+    else {
+        struct inode_s *oldip = inode_addr(oldi);
+        fs.journal.flags    = oldip->flags;
+        fs.journal.diri     = oldi;
+        fs.journal.oldi     = oldi;
+        fs.journal.location = oldip->location;
+        fs.journal.size     = oldip->size;
+    }
+}
+
+// NOTEME: We have compressed the macro code because it will NOT compile on
+// Unix otherwise. So until we find out why, we use this as a work-around.
+#if (FFS_TEST == 1)
+#define JOURNAL_TEST(testcase, text) if (fs.testflags == testcase) { tw(tr(TR_END, TrJournal, "} (" text ")\n")); return; }
+#else
+#define JOURNAL_TEST(testcase, text)
+#endif
+
+// NOTEME: Should we empty journal file when we are anyway relocating it in
+// data_reclaim()?
+void journal_end(uint8 type)
+{
+    struct inode_s *ip = inode_addr(fs.ijournal);
+    struct journal_s *addr = (struct journal_s *)
+        offset2addr(location2offset(ip->location) + fs.journal_pos);
+    
+    tw(tr(TR_BEGIN, TrJournal, "journal_end(0x%x) {\n", type));
+    tw(tr(TR_FUNC, TrJournal, "journal_pos = 0x%04x (%d)\n", fs.journal_pos,
+          (fs.journal_pos - JOURNAL_POS_INITIAL) / sizeof(struct journal_s)));
+    
+    // If this is a create, set the object type
+    if (type != 0 && fs.journal.oldi == 0)
+        fs.journal.flags = (fs.journal.flags & OF_MASK) | type;
+
+    // If there is no journal file, we can do without it, although we
+    // certainly don't like it!
+    if (fs.ijournal == 0) {
+        journal_commit(0); 
+        tw(tr(TR_END, TrJournal, "} No jounal file\n"));
+        return;
+    }
+    
+    JOURNAL_TEST(JOURNAL_TEST_EMPTY, "Oops in JOURNAL_IS_EMPTY");
+        
+    // Write RAM journal to journal file.
+    if (fs.journal.state == (uint8) JOURNAL_IS_EMPTY) {
+        fs.journal.state = JOURNAL_IS_WRITING;
+        ffsdrv.write(addr, &fs.journal, sizeof(fs.journal));
+    }
+    
+    JOURNAL_TEST(JOURNAL_TEST_WRITING, "Oops in JOURNAL_IS_WRITING");
+        
+    // Advance journal file's state
+    if (fs.journal.state == (uint8) JOURNAL_IS_WRITING) {
+        fs.journal.state = JOURNAL_IS_READY;
+        ffsdrv_write_byte(&addr->state, fs.journal.state);
+    }
+    
+    JOURNAL_TEST(JOURNAL_TEST_READY, "Oops in JOURNAL_IS_READY");
+    
+    journal_commit(0);
+    
+    JOURNAL_TEST(JOURNAL_TEST_COMMITTING, "Oops in JOURNAL_TEST_COMMITTING");
+    JOURNAL_TEST(JOURNAL_TEST_COMMITTED, "Oops in JOURNAL_COMMITTED");
+
+    // Advance journal file's state
+    ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE);
+
+    JOURNAL_TEST(JOURNAL_TEST_DONE, "Oops in JOURNAL_IS_DONE");
+
+    // Advance journal
+    fs.journal_pos += sizeof(struct journal_s);
+
+    // Unless we are currently relocating the journal file itself, check if
+    // journal file is near full and relocate it if it is.
+    if (fs.journal_pos >= fs.journal_size - FFS_JOURNAL_MARGIN * 
+		sizeof(struct journal_s) && fs.journal.oldi != fs.ijournal) {
+        tw(tr(TR_FUNC, TrJournal, "Journal file (near) full!\n"));
+        journal_create(fs.ijournal);
+    }
+
+    // Check if we have just committed the journal file itself
+    if (fs.journal.oldi == fs.ijournal) {
+        fs.journal_pos = JOURNAL_POS_INITIAL;
+        fs.ijournal = fs.journal.i;
+        tw(tr(TR_FUNC, TrJournal, "Journal file re-created, fs.ijournal = %d\n",
+              fs.ijournal));
+    }
+    tw(tr(TR_END, TrJournal, "}\n"));
+}
+
+// Write contents of fs.journal to FFS meta data (inodes). Note that we do
+// NOT traverse ip->copied as we used to do in the old
+// object_update_commit(). Also, we do not check if object has been
+// erased after traversing ip->copied. All this code has been removed
+// because we will very soon have full callback functionality and thus the
+// code is redundant.
+void journal_commit(uint8 type)
+{
+    struct inode_s *ip    = inode_addr(fs.journal.i);
+    struct inode_s *oldip = inode_addr(fs.journal.oldi);
+    struct inode_s *dp;
+    bref_t b;
+
+    tw(tr(TR_BEGIN, TrJournal, "journal_commit(%d) {\n", type));
+    tw(tr(TR_FUNC, TrJournal, "i = %d\n", fs.journal.i));
+    ttw(ttr(TTrObj, "jc(){" NL));
+
+    if (fs.journal.i)
+    {
+        // If this is a create, set the object type
+        if (type != 0 && fs.journal.oldi == 0)
+            fs.journal.flags = (fs.journal.flags & OF_MASK) | type;
+        
+        tw(tr(TR_FUNC, TrJournal, "loc   = 0x%04x, size = %d\n",
+              fs.journal.location, fs.journal.size));
+        ffsdrv.write((uint32 *) &ip->location, (uint32 *) &fs.journal.location, sizeof(location_t));
+        ffsdrv.write_halfword((uint16 *) &ip->size,     fs.journal.size);
+
+        if (fs.journal.oldi != 0 && fs.link_child != 0)
+            // If this is an update, we copy the child member from old
+            // inode. We must do this before we validate the new object,
+            // otherwise an intermediate readdir() will detect an empty
+            // directory!
+            ffsdrv.write_halfword((uint16*) &ip->child, oldip->child);
+    
+        tw(tr(TR_FUNC, TrJournal, "seq   = %d\n", fs.sequence));
+        // We must check if sequence is already written because if this
+        // commit was inititiated by journal_init(), we don't know exactly
+        // what was written
+        if (ip->sequence == FLASH_NULL16)
+            ffsdrv.write_halfword(&ip->sequence, fs.sequence++);
+        if (fs.journal.oldi == 0)
+            ffsdrv.write_halfword(&ip->updates,  0);
+        else
+            ffsdrv.write_halfword(&ip->updates,  oldip->updates + 1);
+
+        JOURNAL_TEST(JOURNAL_TEST_COMMITTING, "Oops in JOURNAL_TEST_COMMITTING")
+
+        // Insert object into directory structure. We must do this before
+        // deleting old object, otherwise an intermediate readdir() will
+        // fail with EFFS_NOTFOUND. Note that when the root directory is
+        // created, fs.journal.diri is zero --- thus the test!
+        if (fs.journal.diri != 0) {
+            tw(tr(TR_FUNC,  TrJournal, "diri  = %d ", fs.journal.diri));
+            if (fs.journal.diri < 0) {
+                tw(tr(TR_NULL,  TrJournal, "child\n"));
+                dp = inode_addr(-fs.journal.diri);
+                ffsdrv.write_halfword((uint16 *) &dp->child, fs.journal.i);
+            }
+            else {
+                tw(tr(TR_NULL,  TrJournal, "sibling\n"));
+                dp = inode_addr(fs.journal.diri);
+                ffsdrv.write_halfword((uint16 *) &dp->sibling, fs.journal.i);
+            }
+        }
+
+        // The new object is validated before the old object is deleted.
+        // This is in order to avoid an interrupting stat or read operation
+        // to fail with EFFS_NOTFOUND
+        tw(tr(TR_FUNC,  TrJournal, "flags = 0x%02x\n", fs.journal.flags));
+        ffsdrv_write_byte(&ip->flags, fs.journal.flags);
+
+        // Update bstat[] appropriately
+        b = offset2block(location2offset(ip->location));
+        bstat[b].objects++;
+        tw(tr(TR_FUNC,  TrJournal, "bstat[%d].objects = %d\n", b, bstat[b].objects));
+    }
+
+    tw(tr(TR_FUNC,  TrJournal, "oldi  = %d\n", fs.journal.oldi));
+    if (fs.journal.oldi != 0)
+    {
+        // If this is an update or an erase, we erase the old object
+        ffsdrv_write_byte(&oldip->flags, OT_ERASED);
+
+        // Update bstat according to deletion of the old object.
+        b = offset2block(location2offset(oldip->location));
+        bstat[b].objects--;
+        tw(tr(TR_FUNC,  TrJournal, "bstat[%d].objects = %d\n", b, bstat[b].objects));
+
+        // If we moved the data (all cases, except fcontrol), update lost
+        if (fs.journal.location != oldip->location)
+            bstat[b].lost += oldip->size;
+
+        bstat[fs.inodes].lost++;
+        
+        // If we renamed a file to an existing filename, remove the replaced file. 
+        if (fs.journal.repli > 0)
+            object_remove(fs.journal.repli); // Ignore error! 
+    }
+
+    tw(tr(TR_END, TrJournal, "}\n"));
+    ttw(ttr(TTrObj, "}" NL));
+}
+
+// Save the current journal into "old" journal. We need this because an
+// object_create() can call data_reclaim() which can call object_relocate()
+// which uses the journal system.
+int journal_push(void)
+{
+    memcpy(&fs.ojournal, &fs.journal, sizeof(struct journal_s));
+    fs.journal_depth++;
+    if (fs.journal_depth > 1) {
+        tw(tr(TR_FUNC, TrAll, "FATAL: journal_push() to depth %d\n",
+              fs.journal_depth));
+		return -1;
+	}
+
+    tw(tr(TR_FUNC, TrJournal, "journal_push() to depth %d\n",
+          fs.journal_depth));
+	
+	return EFFS_OK;
+}
+
+// Recall "old" journal into current journal
+int journal_pop(void)
+{
+    tw(tr(TR_FUNC, TrJournal, "journal_pop() from depth %d\n",
+          fs.journal_depth));
+
+    fs.journal_depth--;
+    if (fs.journal_depth < 0) {
+        tw(tr(TR_FUNC, TrAll, "FATAL: journal_pop() to depth %d\n",
+              fs.journal_depth));
+		return -1;
+    }
+    memcpy(&fs.journal, &fs.ojournal, sizeof(struct journal_s));
+
+	return EFFS_OK;
+}
+
+// Initialize the journalling system. Create journal file if it not already
+// exist. Commit/write pending journal if such exists --- return 1 in that
+// case. Otherwise, if journal file is clean (no journals pending) and all
+// is fine, return EFFS_OK.
+effs_t journal_init(iref_t i)
+{
+    int j;
+    struct inode_s *ip = inode_addr(i);
+    struct journal_s *addr;
+
+    if (i == 0) {
+        // Journal file does not exist, so create it
+        if ((i = journal_create(0)) <= 0) {
+            fs.ijournal = 0;
+            return i;
+        }
+    }
+
+    fs.journal_depth = 0;
+    fs.journal_pos = JOURNAL_POS_INITIAL;
+
+    addr = (struct journal_s *)
+        offset2addr(location2offset(ip->location) + fs.journal_pos);
+
+    tw(tr(TR_BEGIN, TrJournal, "journal_init(%d) {\n", i));
+
+    fs.ijournal = i;
+
+    // Search for first non-completed journal entry.
+    for (j = 0; /* FIXME: limit to end of journal */; j++, addr++) {
+        if (addr->state != (uint8) JOURNAL_IS_DONE)
+            break;
+    }
+    tw(tr(TR_FUNC, TrJournal, "entry %d is in state 0x%x\n", j, addr->state));
+
+    fs.journal_pos += j * sizeof(fs.journal);
+    i = EFFS_OK;
+
+    if (addr->state == (uint8) JOURNAL_IS_EMPTY) {
+        tw(tr(TR_FUNC, TrJournal, "Last journal is in EMPTY state\n"));
+        // Journal file is proper, so just record position
+    }
+    else if (addr->state == (uint8) JOURNAL_IS_READY) {
+        // Copy the entry into fs.journal.
+        tw(tr(TR_FUNC, TrJournal, "Last journal is in READY state\n"));
+        memcpy(&fs.journal, addr, sizeof(fs.journal));
+        journal_end(0);
+        i = 1;
+    }
+    else {
+        // Journal entry wasn't finished, so just ignore it after updating
+        // its state to JOURNAL_IS_DONE.
+        tw(tr(TR_FUNC, TrJournal, "Last journal is between EMPTY and READY\n"));
+        ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE);
+        fs.journal_pos += sizeof(fs.journal);
+    }
+
+    if (ip->size != fs.journal_size + atomalign(sizeof(FFS_JOURNAL_NAME) + 1)) {
+        tw(tr(TR_FUNC, TrJournal, "Wrong journal size, create new\n"));
+        // Journal size do not match default size, so create new. This
+        // should only happen if we use an old FFS image with a newer FFS
+        // version.
+        if ((i = journal_create(fs.ijournal)) <= 0) {
+            fs.ijournal = 0;
+            return i;
+        }
+    }
+
+    tw(tr(TR_FUNC, TrJournal, "journal_pos = 0x%04x\n", fs.journal_pos));
+    tw(tr(TR_END, TrJournal, "} %d\n", i));
+    
+    return i;
+}
+
+// Create the journal file from scratch or relocate an existing one. It is
+// marked read-only just for clarity --- it cannot be deleted anyway!
+// fs_format() calls this function. Note that no data are written in
+// object_create() because the journal file is handled specially in that
+// function.
+iref_t journal_create(iref_t oldi)
+{
+    iref_t i;
+
+    tw(tr(TR_BEGIN, TrJournal, "journal_create(%d) {\n", oldi));
+    tw(tr(TR_FUNC, TrJournal, "journal file size = %d\n", fs.journal_size));
+
+    if (fs.journal_size == 0) {
+        tw(tr(TR_FUNC, TrJournal, "Journal file creation aborted because fs.journal_size = 0 (No journal file wanted)\n"));
+        tw(tr(TR_END, TrJournal, "} %d\n", 0));
+        return 0;
+    }
+
+    // If we are working on a write-once file system, we do not need a
+    // journal.
+    if (fs.blocks_free_min == 0) {
+        tw(tr(TR_FUNC, TrJournal, "Journal file creation aborted because fs.blocks_free_min = 0 (write-once system)\n"));
+        tw(tr(TR_END, TrJournal, "} %d\n", 0));
+        return 0;
+    }
+
+    journal_begin(oldi);
+
+    i = object_create(FFS_JOURNAL_NAME, 0, fs.journal_size, -fs.root);
+    if (i < 0) {
+        tw(tr(TR_END, TrJournal, "} %d\n", i));
+        return i;
+    }
+    fs.journal.flags = BIT_SET(fs.journal.flags, OF_READONLY);
+
+    // commit the creation or relocation
+    if (oldi != 0)
+        journal_end(0);
+    else {
+        journal_commit(OT_FILE);
+        fs.journal_pos = JOURNAL_POS_INITIAL;
+    }
+
+    tw(tr(TR_END, TrJournal, "} %d\n", i));
+
+    return i;
+}
+
+/******************************************************************************
+ * FFS Begin and End
+ ******************************************************************************/
+
+// The following two functions should surround the code of every API
+// function in ffs.c (except preformat and format). The functions
+// ensures that the operation about to be executed can be made without
+// race-conditions or other problems.
+#if (TARGET == 0)
+int debug_suspend = 0;
+#endif
+
+
+// Check if ffs has been initialized. Suspend an erase operation.
+effs_t ffs_begin(void)
+{
+#if (TARGET == 0)  
+    if (debug_suspend > 0) {
+        tw(tr(TR_FUNC, TrAll, "FATAL: Previous erase_suspend was not resumed\n"));
+        return EFFS_CORRUPTED;
+    }
+//    tw(tr(TR_FUNC, TrHelper, "Set debug_suspend\n"));
+    debug_suspend = 1;
+#endif
+
+    if (fs.initerror != EFFS_OK)
+        return fs.initerror;
+
+    // Suspend an erase in progress (only applicable if we are using a
+    // multi-bank device driver)
+    if (dev.state == DEV_ERASE) {
+        ffsdrv.erase_suspend();
+    }
+    else if (dev.state == DEV_WRITE) {
+        ffsdrv.write_end();
+    }
+        
+    return EFFS_OK;
+}
+
+// Resume an erase operation that was in progress.
+int ffs_end(int error)
+{
+#if (TARGET == 1)
+    // Resume an erase in progress (only applicable if we are using a
+    // multi-bank device driver)
+    if (dev.state == DEV_ERASE_SUSPEND) {
+        ffsdrv.erase_resume();
+    }
+#else
+    debug_suspend = 0;
+#endif
+
+    return error;
+}
+
+/******************************************************************************
+ * FFS Statistics functions
+ ******************************************************************************/
+
+// Not implemented:
+int statistics_file_create(void)
+{
+    return 0;
+}
+
+// Not implemented:
+// Rewrite the statistics file if it exists. Otherwise return error
+// code. The function is called after each data and inodes reclaim (after
+// writing the file that provoked the reclaim).
+int statistics_write(void)
+{
+    return 0;
+}
+
+// Read the statistics file if it exists. Otherwise reset all statistics to
+// zero and set the magic. This function is called from ffs_init().
+void statistics_init(void)
+{
+	memset(&stats, 0, sizeof(struct ffs_stats_s));
+}
+
+void statistics_update_drec(int valid, int lost, int candidate)
+{
+    unsigned int old;
+
+    switch (candidate) {
+    case MOST_LOST:   stats.drec.most_lost++;   break;
+    case MOST_UNUSED: stats.drec.most_unused++; break;
+    case YOUNGEST:    stats.drec.youngest++;    break;
+    }
+
+    // Increment Most Significant Word if overflow is detected
+    old = stats.drec.valid[0];
+    stats.drec.valid[0] += valid;
+    if (old > stats.drec.valid[0])
+        stats.drec.valid[1]++;
+
+    old = stats.drec.lost[0];
+    stats.drec.lost[0] += lost;
+    if (old > stats.drec.lost[0])
+        stats.drec.lost[1]++;
+}
+
+void statistics_update_irec(int valid, int lost)
+{
+    stats.irec.num++;
+    stats.irec.valid += valid;
+    stats.irec.lost += lost;
+}
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/services/ffs/reclaim.c	Mon Jan 06 04:20:29 2014 +0000
@@ -0,0 +1,883 @@
+/******************************************************************************
+ * Flash File System (ffs)
+ * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
+ *
+ * FFS core reclaim functionality
+ *
+ * $Id: reclaim.c 1.4.1.28 Thu, 08 Jan 2004 15:05:23 +0100 tsj $
+ *
+ ******************************************************************************/
+
+#ifndef TARGET
+#include "ffs.cfg"
+#endif
+
+#include "ffs/ffs.h"
+#include "ffs/board/core.h"
+#include "ffs/board/drv.h"
+#include "ffs/board/ffstrace.h"
+
+#include <stdlib.h>  // rand()
+
+/******************************************************************************
+ * Inodes Reclaim
+ ******************************************************************************/
+
+void inodes_recurse(iref_t i)
+{
+    iref_t pi;
+    struct inode_s *ip, *newip;
+
+    tw(tr(TR_BEGIN, TrReclaimLow, "inodes_recurse(%d) {\n", i));
+
+    ip    = inode_addr(i);
+    newip = (struct inode_s *) offset2addr(dev.binfo[fs.newinodes].offset) + i;
+    
+    // copy inode dir to new block, except child, sibling and copied
+    ffsdrv.write((uint32*) &newip->location, (uint32*) &ip->location, sizeof(location_t));
+    ffsdrv.write_halfword((uint16*) &newip->size,     ip->size);
+    ffsdrv_write_byte    (&newip->flags,    ip->flags);
+    ffsdrv.write_halfword((uint16*) &newip->sequence, ip->sequence);
+    ffsdrv.write_halfword((uint16*) &newip->updates,  ip->updates);
+    bstat[fs.newinodes].used++;
+
+    // if no children of this dir, we have no more work to do
+    if (ip->child == (iref_t) IREF_NULL) {
+        tw(tr(TR_END, TrReclaimLow, "}\n"));
+        return;
+    }
+
+    pi = -i;
+    i = ip->child;
+    ip = inode_addr(i);
+
+    do {
+        tw(tr(TR_FUNC, TrReclaimLow, "pi = %d, i = %d", pi, i));
+
+        tw(tr(TR_NULL, TrReclaimLow, ", size = %d, location = 0x%x", ip->size, 
+              ip->location));
+        
+        tw(tr(TR_NULL, TrReclaimLow, ", name_addr = 0x%x",
+              addr2name(offset2addr(location2offset(ip->location)))));
+        
+        if (is_object(ip, OT_SEGMENT)) 
+            tw(tr(TR_NULL, TrReclaimLow, ", (segment)\n"));
+        
+        else
+            tw(tr(TR_NULL, TrReclaimLow, ", '%s'\n",
+                  (ip->size ? addr2name(offset2addr(location2offset(ip->location)))
+                   : "(cleaned)")));
+
+        if (is_object_valid(ip))
+        {
+            if (is_object(ip, OT_DIR)) {
+                tw(tr(TR_NULL, TrReclaimLow, "recursing...\n", i));
+                inodes_recurse(i);
+            }
+            else {
+                tw(tr(TR_NULL, TrReclaimLow, "copying...\n"));
+                // copy inode to new block, except child, sibling and copied
+                newip = (struct inode_s *)
+                    offset2addr(dev.binfo[fs.newinodes].offset) + i;
+                ffsdrv.write((uint32*) &newip->location, (uint32*) &ip->location, sizeof(location_t));
+                ffsdrv.write_halfword((uint16*) &newip->size,     ip->size);
+                ffsdrv_write_byte    (&newip->flags,    ip->flags);
+                ffsdrv.write_halfword((uint16*) &newip->sequence, ip->sequence);
+                ffsdrv.write_halfword((uint16*) &newip->updates,  ip->updates);
+                bstat[fs.newinodes].used++;
+            }
+
+            tw(tr(TR_FUNC, TrReclaimLow, "Linking: %d->%d\n",pi, i));
+            // now write the child or sibling link of previous inode
+            newip = (struct inode_s *)
+                offset2addr(dev.binfo[fs.newinodes].offset);
+            if (pi > 0)
+                ffsdrv.write_halfword((uint16*) &(newip + pi)->sibling, i);
+            else
+                ffsdrv.write_halfword((uint16*) &(newip + (-pi))->child, i);
+            
+            pi = i; // save index of previous inode
+            
+            if (ip->child != (iref_t) IREF_NULL && is_object(ip, OT_FILE)) {
+                iref_t pis, is;
+                struct inode_s *ips;
+                pis = i;
+                ips = ip;
+
+                tw(tr(TR_FUNC, TrReclaimLow, "Follow segment head\n"));
+                // While child is valid
+                while ((is = ips->child) != (iref_t) IREF_NULL) {
+
+                    // Get child
+                    is = ips->child;
+                    ips = inode_addr(is);
+                    tw(tr(TR_FUNC, TrReclaimLow, "Child ok, got new child i = %d\n", is));
+                    // While object not is valid
+                    while (!is_object_valid(ips)) {
+                        tw(tr(TR_FUNC, TrReclaimLow, "pi = %d, i = %d c(cleaned)\n", pis, is));
+                        // If sibling are valid
+                        if (ips->sibling != (iref_t) IREF_NULL) {  
+                            // Get sibling
+                            is = ips->sibling;
+                            ips = inode_addr(is);
+                            tw(tr(TR_FUNC, TrReclaimLow, "Sibling ok, got new sibling i = %d\n", is));
+                        }
+                        else {
+                            tw(tr(TR_FUNC, TrReclaimLow, "Sibling = FF (%d)\n", ips->sibling));
+                            break;  // Nothing more todo, child and sibling = FF
+                        }
+                    }
+                    // If object is valid
+                    if (is_object_valid(ips)) {
+                        tw(tr(TR_NULL, TrReclaimLow, "copying...\n"));
+                        // copy inode to new block, except child, sibling and copied
+                        newip = (struct inode_s *)
+                            offset2addr(dev.binfo[fs.newinodes].offset) + is;
+                        ffsdrv.write((uint32*) &newip->location, (uint32*) &ips->location, sizeof(location_t));
+                        ffsdrv.write_halfword((uint16*) &newip->size,     ips->size);
+                        ffsdrv_write_byte    (&newip->flags,              ips->flags);
+                        ffsdrv.write_halfword((uint16*) &newip->sequence, ips->sequence);
+                        ffsdrv.write_halfword((uint16*) &newip->updates,  ips->updates);
+                        bstat[fs.newinodes].used++;
+                        
+                        tw(tr(TR_FUNC, TrReclaimLow, "Linking child: %d->%d\n",pis, is));
+                        // now write the child link of previous inode
+                        newip = (struct inode_s *)
+                            offset2addr(dev.binfo[fs.newinodes].offset);
+                        ffsdrv.write_halfword((uint16*) &(newip + (pis))->child, is);
+                        
+                        pis = is; // save index of previous inode   
+               
+                    }     
+                    else {
+                        tw(tr(TR_FUNC, TrReclaimLow, "Sibling = FF (%d, %d)\n", 
+                              ips->sibling, ips->child));
+                    }
+
+                }
+            }
+        }       
+        else {
+            tw(tr(TR_NULL, TrReclaimLow, "(ignoring)\n"));
+        }
+        i = ip->sibling;
+        ip = inode_addr(i);
+            
+    } while (i != (iref_t) IREF_NULL);
+    
+    tw(tr(TR_END, TrReclaimLow, "}\n"));
+}
+
+// Reclaim inodes, eg. move inodes to another block and erase old one.
+effs_t inodes_reclaim(void)
+{
+    tw(tr(TR_BEGIN, TrIReclaim, "inodes_reclaim() {\n"));
+    ttw(str(TTrRec, "irec{"));
+
+    if (fs.initerror != EFFS_OK) {
+        tw(tr(TR_END, TrIReclaim, "} %d\n", fs.initerror));
+        ttw(ttr(TTrRec, "} %d" NL, fs.initerror));
+        return fs.initerror;
+    }
+
+    if ((fs.newinodes = block_alloc(1, BF_COPYING)) < 0) {
+        tw(tr(TR_END, TrIReclaim, "} %d\n", EFFS_NOBLOCKS));
+        ttw(ttr(TTrRec, "} %d" NL, EFFS_NOBLOCKS));
+        return EFFS_NOBLOCKS;
+    }
+
+    statistics_update_irec(bstat[fs.inodes].used - bstat[fs.inodes].lost, 
+                           bstat[fs.inodes].lost);
+
+    // copy all inodes...
+    bstat[fs.newinodes].used = 0;
+    inodes_recurse(fs.root);
+
+    block_commit();
+
+    tw(tr(TR_END, TrIReclaim, "} 0\n"));
+    ttw(str(TTrRec, "} 0" NL));
+
+    return EFFS_OK;
+}
+
+#if (FFS_TEST == 0)
+#define BLOCK_COMMIT_TEST(testcase, text)
+#else
+#if (TARGET == 0)
+// NOTEME: We have compressed the macro code because it will NOT compile on
+// Unix otherwise. So until we find out why, we use this as a work-around.
+#define BLOCK_COMMIT_TEST(testcase, text) if (fs.testflags == testcase) { tw(tr(TR_FUNC, TrData, "} (" text ")\n")); return; }
+#else
+#define BLOCK_COMMIT_TEST(testcase, text) if (fs.testflags == testcase) { ttw(ttr(TTrData, "} (" text ")\n")); return; }
+#endif
+#endif
+
+// Inode -> Lost, Copying -> Inode, Lost -> Free
+void block_commit(void)
+{
+    int oldinodes = fs.inodes;
+
+    tw(tr(TR_BEGIN, TrIReclaim, "block_commit(%d -> %d) {\n", 
+       oldinodes, fs.newinodes));
+    ttw(ttr(TTrRec, "block_commit(%d -> %d) {\n" NL, 
+       oldinodes, fs.newinodes)); 
+
+    BLOCK_COMMIT_TEST(BLOCK_COMMIT_BEFORE, "Oops before commit");
+
+    block_flags_write(oldinodes, BF_LOST);
+ 
+    BLOCK_COMMIT_TEST(BLOCK_COMMIT_NO_VALID, "Oops no valid inode block");
+
+    // Validate new block as an inodes block
+    block_flags_write(fs.newinodes, BF_INODES);
+
+    bstat[fs.newinodes].lost = 0;
+    bstat[fs.newinodes].objects = 1;
+    inodes_set(fs.newinodes);
+
+    // Free old inodes block
+    block_free(oldinodes);
+
+    BLOCK_COMMIT_TEST(BLOCK_COMMIT_OLD_FREE, "Oops after freeing old block");
+
+    BLOCK_COMMIT_TEST(BLOCK_COMMIT_AFTER, "Oops after commit");
+
+    ttw(str(TTrRec, "} 0" NL));
+    tw(tr(TR_END, TrIReclaim, "}\n"));
+}
+
+
+/******************************************************************************
+ * Data Reclaim
+ ******************************************************************************/
+
+// Important note: We must NOT perform a data reclaim when we are in the
+// process of creating the journal file!
+
+// Reclaim a data block, eg. move files to other blocks and erase old one.
+// When the reclaim is done, we must completely delete the old inodes which
+// are pointing into the old data sector which is going to be erased now.
+iref_t data_reclaim(int space)
+{
+    iref_t error;
+
+    tw(tr(TR_BEGIN, TrDReclaim, "data_reclaim(%d) {\n", space));
+
+    if (fs.initerror != EFFS_OK) {
+        tw(tr(TR_END, TrDReclaim, "} %d\n", fs.initerror));
+        return fs.initerror;
+    }
+
+    error = data_reclaim_try(space);
+
+    tw(tr(TR_END, TrDReclaim, "} (data_reclaim) %d\n", error));
+
+    return error;
+}
+
+int dage_max_reached(int dage_blk, int agegain)
+{
+    int reclaim, early, log2, mask;
+
+    tw(tr(TR_BEGIN, TrDReclaim, "young(%d, %d) {\n", dage_blk, agegain));
+    
+    // Simple algorithm
+    reclaim = (dage_blk + agegain - 2 * FFS_DAGE_MAX >= 0);
+
+    // Early exponential probability based reclaim
+    early = FFS_DAGE_MAX - dage_blk;
+    if (agegain > dage_blk - 4 && 0 < early && early <= FFS_DAGE_EARLY_WIDTH) {
+        if (early < 4)
+            early = 2;
+        if (early < FFS_DAGE_EARLY_WIDTH) {
+            // Now make an exponential probability distributon by
+            // generating a bitmask of a size relative to (dage_blk
+            // - DAGE_EARLY_WIDTH)
+            log2 = -1;
+            while (early > 0) {
+                early >>= 1;
+                log2++;
+            }
+            reclaim = log2;
+            
+            mask = (1 << (log2 + 1)) - 1;
+            reclaim = ((rand() & mask) == 0);
+        }
+    }
+
+    // Do not perform a reclaim unless we gain a certain minimum
+    if (agegain < FFS_DAGE_GAIN_MIN)
+        reclaim = 0;
+
+    tw(tr(TR_END, TrDReclaim, "} (%d)\n", reclaim));
+    return reclaim;
+}
+
+
+// Try to reclaim at least <space> bytes of data space. On success, return
+// the number of bytes actually reclaimed. Otherwise, on failure, return a
+// (negative) error.
+int data_reclaim_try(int space)
+{
+    // 1. Find a suitable block to reclaim.
+    //
+    // 2. Relocate each valid object from old block (to another block). An
+    // object relocation is similar to a normal file update, e.g. similar to
+    // fupdate().
+    //
+    // 3. If there is not enough space to relocate a file, we must alloc a
+    // new block then data_format() it.
+    //
+    // 4. set BF_CLEANING flag of old block.
+    //
+    // 5. ALL inodes (also invalid an erased ones) referring into reclaimed
+    // block must now be totally wiped out.
+    //
+    // 6. Free (invalidate) old block.
+
+    int result = 0, reserved_ok = 0;
+    bref_t b, blocks_free;
+    bref_t brc_young_b, brc_lost_b, brc_unused_b;
+
+    blocksize_t brc_lost_lost,   brc_lost_unused;
+    blocksize_t brc_unused_unused;
+    blocksize_t unused, unused_total, lost, lost_total, free;
+
+    age_t brc_young_dage, free_dage, dage;
+    struct block_header_s *bhp;
+    // Note gain can be negative if the free block is younger than the youngest data block
+    int age_gain; 
+
+    tw(tr(TR_BEGIN, TrDReclaim, "data_reclaim_try(%d) {\n", space));
+    ttw(str(TTrRec, "drec{" NL));
+
+    // While searching for a block to reclaim, we maintain three block
+    // reclaim candidates (brc): One with the maximum number of lost bytes,
+    // one with the maximum number of unused bytes and another for the
+    // youngest block, e.g. the one with the largest age distance to
+    // fs.age_max. The candidates are tried in the order mentioned.
+    
+    // This counts free blocks, so we initialize to number of blocks minus
+    // one for inodes.
+    blocks_free = dev.numblocks - 1;
+
+    // Initialize Block Reclaim Candidate (brc) variables
+    brc_lost_b   = -1; brc_lost_unused   = 0; brc_lost_lost   = 0;
+    brc_unused_b = -1; brc_unused_unused = 0; 
+
+    brc_young_b  = -1; brc_young_dage = 0;  free_dage  = 0;
+
+    lost_total   = 0;
+    unused_total = 0;
+
+    tw(tr(TR_FUNC, TrDReclaim,
+          "blk  unused    lost  w/age   age dist  objs\n"));
+    for (b = 0; b < dev.numblocks; b++)
+    {
+        bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset);
+
+        if (is_block(b, BF_IS_DATA))
+        {
+            // Record number of lost bytes and number of unused bytes,
+            // eg. total space that would be freed if this block was
+            // reclaimed
+            lost   = bstat[b].lost;
+            unused = dev.blocksize - (bstat[b].used - bstat[b].lost);
+            free   = dev.blocksize - bstat[b].used;
+
+            lost_total   += lost;
+            unused_total += unused;
+
+            if (free >= RESERVED_LOW) 
+                reserved_ok = 1;
+            if (lost > brc_lost_lost) {
+                brc_lost_b = b;
+                brc_lost_lost = lost;
+                brc_lost_unused = unused;
+            }
+            if (unused > brc_unused_unused) {
+                brc_unused_b = b;
+                brc_unused_unused = unused;
+            }
+
+            tw(tr(TR_FUNC, TrDReclaim, "%3d %7d %7d ", b, unused, lost));
+
+            dage = saturate_dage(fs.age_max - bhp->age);
+
+            tw(tr(TR_NULL, TrDReclaim, "%6d %5d %4d   %3d\n",
+                  lost, bhp->age, dage, bstat[b].objects));
+
+            if (dage >= brc_young_dage) {
+                brc_young_b = b;
+                brc_young_dage = dage;
+            }
+            blocks_free--;
+        }
+        else if (is_block(b, BF_IS_FREE)) {
+            unused_total += dev.blocksize;
+
+            // Find youngest free block (in must cases we will only have one free b)
+            dage = saturate_dage(fs.age_max - bhp->age);
+
+            if (dage >= free_dage)
+                free_dage = dage;   // Delta age of youngest free block
+        }
+    }
+    tw(tr(TR_FUNC, TrDReclaim, "sum %7d %7d\n", unused_total, lost_total));
+    tw(tr(TR_FUNC, TrDReclaim, "blocks_free = %d, fs.age_max = %d\n", blocks_free, fs.age_max));
+
+    age_gain = brc_young_dage - free_dage; // Same as free - block age
+  
+    if (space > unused_total) {
+        // We will never be able to reclaim this amount...
+        result = 0;
+    }
+    else {
+        // No additional blocks (apart from spare block) are free...
+        tw(tr(TR_FUNC, TrDReclaim,
+              "brc_young_dage = %d, brc_lost_unused = %d, brc_unused_unused = %d\n",
+              brc_young_dage, brc_lost_unused, brc_unused_unused));
+    
+        if (reserved_ok == 0) {
+            tw(tr(TR_FUNC, TrDReclaim, 
+                  "No reserved, reclaim most-lost block (%d)\n", brc_unused_b));
+            result = data_block_reclaim(brc_lost_b, MOST_LOST);
+        }
+        else if (dage_max_reached(brc_young_dage, age_gain) > 0 ) {
+            tw(tr(TR_FUNC, TrDReclaim, "Reclaiming youngest block (%d)\n",
+                  brc_young_b));
+            result = data_block_reclaim(brc_young_b, YOUNGEST);
+        }
+        else if (brc_lost_unused >= space) {
+            tw(tr(TR_FUNC, TrDReclaim, "Reclaiming most-lost block (%d)\n",
+                  brc_lost_b));
+            result = data_block_reclaim(brc_lost_b, MOST_LOST);
+        }
+        else if (brc_unused_unused >= space) {
+            tw(tr(TR_FUNC, TrDReclaim, "Reclaiming most-unused block (%d)\n",
+                  brc_unused_b));
+            result = data_block_reclaim(brc_unused_b, MOST_UNUSED);
+        }
+        else {
+            tw(tr(TR_FUNC, TrDReclaim, "Reclaiming most-lost blockx (%d)\n",
+                  brc_lost_b));
+            result = data_block_reclaim(brc_lost_b, MOST_LOST);
+            if (result >= 0)
+                result = 0;  // We reclaimed a block but we still need more space
+        }
+
+    }
+    tw(tr(TR_END, TrDReclaim, "} (data_reclaim_try) %d\n", result));
+
+    return result;
+}
+
+
+#if (FFS_TEST == 0)
+#define BLOCK_RECLAIM_TEST(testcase, text)
+#else
+#if (TARGET == 0)
+// NOTEME: We have compressed the macro code because it will NOT compile on
+// Unix otherwise. So until we find out why, we use this as a work-around.
+#define BLOCK_RECLAIM_TEST(testcase, text) if (fs.testflags == testcase) { tw(tr(TR_FUNC, TrTestHigh, "(" text ")\n")); tw(tr(TR_END, TrDReclaim, "} (Test) -100\n", result));return -100; }
+#else
+#define BLOCK_RECLAIM_TEST(testcase, text) if (fs.testflags == testcase) { ttw(ttr(TTrData, "} (" text ")"NL)); ttw(ttr(TTrRec, "} (Test) -100" NL));return -100; }
+#endif
+#endif
+
+#if (FFS_TEST == 0)
+#define BLOCK_RECOVER_TEST_INIT(testcase, text)
+#define BLOCK_RECOVER_TEST(testcase, text)
+#else
+#if (TARGET == 0)
+#define BLOCK_RECOVER_TEST_INIT(testcase, text) int rand_object; if (fs.testflags == testcase) { rand_object = rand() % bstat[b].objects; tw(tr(TR_FUNC, TrTestHigh, "Fail when object nr %d is relocated\n", rand_object)); }
+
+#define BLOCK_RECOVER_TEST(testcase, text) if (fs.testflags == testcase) {if (rand_object == n) { tw(tr(TR_FUNC, TrTestHigh, "(" text ")\n")); tw(tr(TR_END, TrDReclaim, "} (Test) -101\n", result)); return -101; } }
+
+#else   
+#define BLOCK_RECOVER_TEST_INIT(testcase, text) int rand_object; if (fs.testflags == testcase) { rand_object = rand() % bstat[b].objects; ttw(ttr(TTrData, "Fail when object nr %d is relocated" NL, rand_object)); }
+#define BLOCK_RECOVER_TEST(testcase, text) if (fs.testflags == testcase) {if (rand_object == n) { ttw(ttr(TTrData, "(" text ")" NL)); ttw(ttr(TTrRec, "} (Test) -101" NL, result)); return -101; } }
+#endif
+#endif
+
+iref_t data_block_reclaim(bref_t b, int candidate)
+{
+    iref_t i, n, j;
+    blocksize_t used_old, lost_old;
+	int org_res_space, result = 0;
+	iref_t org_block_files_reserved;
+    offset_t lower, upper;
+    struct inode_s *ip;
+	static int is_reclaim_running = 0;
+
+    tw(tr(TR_BEGIN, TrDReclaim, "data_block_reclaim(%d) {\n", b));
+
+    // In case of no free blocks (after sudden power off) or if the file
+    // system is near full we risk to be reentered (infinity recursively
+    // loop) and we can not allow that, so just return.
+	if (is_reclaim_running == 1) {
+		tw(tr(TR_END, TrDReclaim, "} (reenteret skip reclaim) 0\n"));
+		return EFFS_RECLAIMLOOP;
+	}
+
+	is_reclaim_running = 1;
+
+    // If there are more objects in this block than there are remaining
+    // free inodes, we have to make an inodes_reclaim() first.
+    tw(tr(TR_FUNC, TrDReclaim,
+          "block_objects, fs.inodes_max, inodes: used, free\n"));
+    tw(tr(TR_FUNC, TrDReclaim,
+          "%10d, %13d, %15d, %4d\n",
+          bstat[b].objects,
+          fs.inodes_max, bstat[fs.inodes].used,
+          fs.inodes_max - (bstat[fs.inodes].used + bstat[fs.inodes].lost)));
+
+    if (bstat[b].objects >= (fs.inodes_max - (bstat[fs.inodes].used + 
+                                              bstat[fs.inodes].lost + 
+                                              FFS_INODES_MARGIN))) {
+        tw(tr(TR_FUNC, TrInode, "NOTE: Will run out of free inodes...\n"));
+        inodes_reclaim();
+    }
+
+    // Allocate a new block. NOTE: we don't return an error because if we
+	// get in the situation where we don't have any free blocks this is the
+	// only way to recover.
+    if ((result = block_alloc(1, BF_DATA)) < 0) {
+        tw(tr(TR_FUNC, TrAll, "WARNING: block_alloc failed\n"));
+    }
+
+	BLOCK_RECLAIM_TEST(BLOCK_RECLAIM_ALLOC, "Oops after ffs_block_alloc()");
+
+    // If there are any objects at all to reclaim...
+    if (bstat[b].objects > 0)
+    {
+		BLOCK_RECOVER_TEST_INIT(BLOCK_RECOVER_OBJECTS, "Dummy")
+            // Save the current journal state
+            if (journal_push() != EFFS_OK) {
+                is_reclaim_running = 0;       // NOTEME: change to goto?
+                return EFFS_CORRUPTED;
+            }
+
+        // We simulate that this block is completely full, such that we
+        // don't relocate files to the end of the block
+        used_old = bstat[b].used;
+        lost_old = bstat[b].lost;  // For statistics
+        bstat[b].used = dev.blocksize - 1;
+
+
+        // Compute lower (inclusive) and upper (exclusive) bounds of the
+        // location of files in this block
+        lower = offset2location(dev.binfo[b].offset);
+        upper = offset2location(dev.binfo[b].offset + dev.blocksize);
+
+        tw(tr(TR_FUNC, TrDReclaim, "Block addr range = 0x%X..0x%X\n",
+              location2offset(lower), location2offset(upper)));
+
+		// This is the only time we are allowed to use the reserved 
+		org_block_files_reserved= fs.block_files_reserved;
+		fs.block_files_reserved = 0;
+
+		org_res_space = fs.reserved_space;
+        fs.reserved_space = RESERVED_NONE;
+
+		ip = inode_addr(1);
+        for (i = 1, n = 0; i < fs.inodes_max; i++, ip++)
+        {
+			BLOCK_RECOVER_TEST(BLOCK_RECOVER_OBJECTS, "Oops before relocate all objects");
+            // Ensure object is valid and within the block to be reclaimed
+            if (is_object_valid(ip) &&
+                lower <= ip->location && ip->location < upper)
+            {
+                if ((result = object_relocate(i)) < 0) {
+                    tw(tr(TR_FUNC, TrAll, "FATAL object_relocate failed\n"));
+                    break;
+                }
+                
+                // If we reclaim a segment head or wch that is in use we must
+                // update the file descriptor as well
+                for (j = 0; j < fs.fd_max; j++) {
+                    if (i == fs.fd[j].seghead) {
+                        tw(tr(TR_FUNC, TrDReclaim, 
+                              "Updated seghead %d -> %d \n",
+                              fs.fd[j].seghead, result));
+                        fs.fd[j].seghead = result;
+                    }
+                    if (i == fs.fd[j].wch) {
+                        tw(tr(TR_FUNC, TrDReclaim, 
+                              "Updated wch %d -> %d \n",
+                              fs.fd[j].wch, result));
+                        fs.fd[j].wch = result;
+                    }
+                }
+
+                // If we have just reclaimed an object which we started on
+                // updating we must also update ojournal
+                if (i == fs.ojournal.oldi) {
+                    struct inode_s *ip = inode_addr(result);
+                    tw(tr(TR_FUNC, TrDReclaim, 
+                          "Updated ojournal oldi %d -> %d \n",
+                          fs.ojournal.oldi, result));
+                    fs.ojournal.oldi     = result;
+                    fs.ojournal.location = ip->location;
+                }
+
+                if (i == fs.ojournal.diri || i == -fs.ojournal.diri) {
+                    fs.ojournal.diri = (fs.ojournal.diri < 0 ? -result : result);
+                    tw(tr(TR_FUNC, TrDReclaim, 
+                          "Updated ojournal: diri %d -> %d \n", 
+                          i, fs.ojournal.diri));
+                }
+
+                if (i == fs.ojournal.repli || i == -fs.ojournal.repli) {
+                    fs.ojournal.repli = (fs.ojournal.repli < 0 ? -result : result);
+                    tw(tr(TR_FUNC, TrDReclaim, 
+                          "Updated ojournal: repli %d -> %d \n", 
+                          i, fs.ojournal.repli));
+                }
+ 
+                if (i == fs.i_backup || i == -fs.i_backup) {
+                    fs.i_backup = (fs.i_backup < 0 ? -result : result);
+                    tw(tr(TR_FUNC, TrDReclaim, 
+                          "Updated i_backup: %d -> %d \n", i, fs.i_backup));
+                }
+
+                n++;
+            }
+        }
+
+		fs.block_files_reserved = org_block_files_reserved; // Restore
+		fs.reserved_space = org_res_space;
+
+        tw(tr(TR_FUNC, TrDReclaim, "Reclaimed %d objects\n", n));
+        if (result >= 0)
+            result = n; // We return number of objects relocated
+
+        if (i < fs.inodes_max) {
+            // We did not finish, so restore the old bstat[].used of the block.
+            bstat[b].used = used_old;
+            tw(tr(TR_FUNC, TrAll,
+                  "WARNING: data_block_reclaim() not completed\n"));
+            result = EFFS_DBR;
+		}
+
+        // Restore the saved journal state
+        if (journal_pop() != EFFS_OK) {
+			is_reclaim_running = 0;       // NOTEME: change to goto?
+			return EFFS_CORRUPTED;
+		}
+    }
+	BLOCK_RECLAIM_TEST(BLOCK_RECLAIM_NO_CLEAN, "Oops before clean old data block");
+
+    if (result >= 0) {
+        // Clean the block (remove all inodes that refer to this block)
+        block_flags_write(b, BF_CLEANING);
+        block_clean(b);
+
+        statistics_update_drec(used_old - lost_old, lost_old, candidate); 
+    
+		BLOCK_RECLAIM_TEST(BLOCK_RECLAIM_CLEANING, "Oops before free old data block");
+
+        // Free the old block
+        block_free(b);
+    }
+
+	is_reclaim_running = 0;
+
+    tw(tr(TR_END, TrDReclaim, "} (data_block_reclaim) %d\n", result));
+    ttw(ttr(TTrRec, "} %d" NL, result));
+
+    return result;
+}
+
+// Relocate object represented by inode reference <i>. 
+iref_t object_relocate(iref_t oldi)
+{
+    iref_t newi;
+    struct inode_s *oldip;
+    char *olddata, *oldname;
+    int oldsize;
+
+    tw(tr(TR_BEGIN, TrReclaimLow, "object_relocate(%d) {\n", oldi));
+
+    journal_begin(oldi);
+
+    oldip = inode_addr(oldi);
+
+    oldsize = segment_datasize(oldip);
+    olddata = offset2addr(location2offset(oldip->location));
+    oldname = addr2name(olddata);
+    olddata = addr2data(olddata, oldip);
+    
+    if (is_object(oldip, OT_SEGMENT))     
+        newi = segment_create(olddata, oldsize, -oldi);   
+    else {
+        // root inode is a special case
+        if (*oldname == '/')
+            newi = object_create(oldname, olddata, oldsize, 0);
+        else 
+            newi = object_create(oldname, olddata, oldsize, oldi);
+    }
+
+    if (newi < 0) {
+        tw(tr(TR_END, TrReclaimLow, "} %d\n", newi));
+        return newi;
+    }
+
+    // root inode is a special case
+    if ((*oldname == '/') && !is_object(oldip, OT_SEGMENT)) {
+        tw(tr(TR_FUNC, TrDReclaim, "Relocating fs.root: %d->%d\n", oldi, newi));
+        fs.root = newi;
+    }
+
+    journal_end(0);
+
+    tw(tr(TR_END, TrReclaimLow, "} %d\n", newi));
+
+    return newi;
+}
+
+// Clean a block, eg. erase all inodes that refer to this block.
+iref_t block_clean(bref_t b)
+{
+    iref_t i, n;
+    struct inode_s *ip;
+    offset_t lower, upper;
+
+    tw(tr(TR_FUNC, TrDReclaim, "block_clean(%d) { ", b));
+
+    // Compute lower (inclusive) and upper (exclusive) bounds of the
+    // location of files in this block
+    lower = offset2location(dev.binfo[b].offset);
+    upper = offset2location(dev.binfo[b].offset + dev.blocksize);
+
+    tw(tr(TR_FUNC, TrDReclaim, "offset range = 0x%X..0x%X: ", lower, upper));
+
+    ip = inode_addr(1);
+    for (i = 1, n = 0; i < fs.inodes_max; i++, ip++)
+    {
+        // Ensure object is within the block to be reclaimed. Note: if ffs
+        // is conf. with 1MB or above will all not used inodes default have
+        // the location to FFFF which will trigger a clean and make a error!
+        if (lower <= ip->location && upper > ip->location)
+        {
+            tw(tr(TR_NULL, TrReclaimLow, "%d ", i));
+            // Set the size to zero so it won't be counted in ffs_initialize()
+            ffsdrv.write_halfword((uint16 *) &ip->size, 0);
+            n++;
+        }
+    }
+    tw(tr(TR_NULL, TrDReclaim, "} %d\n", n));
+
+    return n;
+}
+
+
+/******************************************************************************
+ * Main and block reclaim
+ ******************************************************************************/
+
+// Reclaim (erase) all blocks that are marked as invalid/reclaimable. Each
+// time a block is erased, its age is incremented so as to support wear
+// levelling. Also, the global age limits are updated.  FIXME: Should we
+// avoid having ffs_initialize() do a block_reclaim() because it delays reboot?.
+int blocks_reclaim(void)
+{
+    bref_t b, n, b_lost_space;
+	int blocks_free = 0, lost_space;
+
+	int free_space, b_free_space;
+
+    tw(tr(TR_BEGIN, TrBlock, "blocks_reclaim() {\n"));
+    ttw(str(TTrRec, "blocks_reclaim() {" NL));
+
+    // Testing of fs.testflags is for the sake of testing block_commit()
+    if ((fs.testflags & BLOCK_COMMIT_BASE) != 0) {
+        tw(tr(TR_FUNC, TrBlock, "Bailing out because fs.testflags = 0x%X\n",
+              fs.testflags));
+    }
+    else {
+        for (b = 0, n = 0; b < dev.numblocks; b++) {
+            if (is_block_flag(b, BF_LOST)) {
+                block_reclaim(b);
+                n++;
+            }
+			if (is_block(b, BF_IS_FREE)) {
+				blocks_free++;
+			}
+        }
+    }
+
+	// If the number of free blocks is less than fs.blocks_free_min we
+	// call data_block_reclaim(). We will reclaim the block with most lost
+	// space. This should only happend if we got a sudden power off/reset
+	// while we reclaimed a block.
+	if (blocks_free < fs.blocks_free_min) {
+		lost_space = 0;
+		free_space = 0;
+
+		// We most never reclaim the block with most free space because this
+		// is the only block we can relocate the objects to.
+		for (b = 0; b < dev.numblocks; b++) {
+			if (is_block_flag(b, BF_DATA)) {
+				if ((dev.blocksize - bstat[b].used) > free_space) {
+					free_space = dev.blocksize - bstat[b].used;
+					b_free_space = b;
+				}
+			}
+		}
+		tw(tr(TR_FUNC, TrBlock, "most free space: %d in block: %d \n", 
+			  free_space, b_free_space));
+
+		for (b = 0; b < dev.numblocks; b++) {
+			if (is_block_flag(b, BF_DATA) && b != b_free_space) {
+				if (bstat[b].lost > lost_space) {
+					lost_space = bstat[b].lost;
+					b_lost_space = b;
+				}
+			}
+		}
+		tw(tr(TR_FUNC, TrBlock, "most lost space: %d in block: %d \n", 
+			  lost_space, b_lost_space));
+
+		data_block_reclaim(b_lost_space, MOST_LOST);
+	}
+    tw(tr(TR_END, TrBlock, "} %d\n", n));
+    ttw(ttr(TTrRec, "} %d" NL, n));
+
+    return n;
+}
+
+int block_reclaim(bref_t b)
+{
+    age_t age;
+    struct block_header_s *bhp;
+
+    tw(tr(TR_BEGIN, TrBlock, "block_reclaim(%d) {\n", b));
+
+    // In ffs_initialize() we set fs.initerror = EFFS_INVALID while we call
+    // blocks_fsck(). We test for that condition now, in order to avoid
+    // doing sector erases that will delay the whole target boot process.
+    if (fs.initerror == EFFS_INVALID) {
+        tw(tr(TR_END, TrBlock, "} %d\n", fs.initerror));
+        return fs.initerror;
+    }
+
+    // Testing of fs.testflags is for the sake of testing block_commit()
+    if ((fs.testflags & BLOCK_COMMIT_BASE) != 0 && 
+		fs.testflags != BLOCK_COMMIT_OLD_FREE) {
+        tw(tr(TR_FUNC, TrBlock, "Bailing out because fs.testflags = 0x%X\n",
+              fs.testflags));
+    }
+    else {
+        // We must read block's age before we erase it.
+        bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset);
+        age = bhp->age;
+        ffsdrv.erase(b);
+        block_preformat(b, age);
+    }
+
+    tw(tr(TR_END, TrBlock, "} %d\n", 0));
+
+    return 0;
+}