www.pudn.com > drivers.rar > fsck.c


/****************************************************************************** 
 * 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  
#include  
 
#include "ffs/ffs.h" 
#include "ffs/board/core.h" 
#include "ffs/board/drv.h" 
#include "ffs/board/ffstrace.h" 
 
#if (TARGET == 0 || WITH_TFFS == 1) 
#include "ffs/board/tffs.h" 
#endif 
 
#include "swconfig.cfg" 
 
/****************************************************************************** 
 * Functions 
 ******************************************************************************/ 
 
bref_t blocks_fsck(void); 
iref_t inodes_fsck(void); 
 
/****************************************************************************** 
 * Init and Exit 
 ******************************************************************************/ 
char 	ffs_initerror[5]; 
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 
    ffs_initerror[0]  = fs.initerror;//fangcjdebug 
    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 < 3; i++) 
    { 
        tlw(led_on(LED_BLOCKS_FSCK)); 
        fs.initerror = EFFS_INVALID; 
        fs.initerror = b = blocks_fsck(); 
		ttr(TTrFatal, "xxx:   blocks_fsck() return: %d,for(%d)" NL, fs.initerror, i); 
	ffs_initerror[1] = fs.initerror;//fangcjdebug 
        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(); 
	ffs_initerror[2] = fs.initerror; 
        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) 
        	{ 
        	ffs_initerror[3] = fs.initerror; 
            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)          
#if (WITH_TFFS == 1) 
    blocks_reclaim();  // target and test 
    if (fs.repi_table[0] != 0)  
        inodes_reclaim(); 
#endif 
#else 
    blocks_reclaim();  // not target 
    if (fs.repi_table[0] != 0)  
        inodes_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; 
    fs.is_reclaim_running = 0; 
	 
    // Flag that it not has been changed by an input arg. 
	fs.block_files_max = 0; 
 
    // 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	(LOCOSTO_LITE) 
//    	fs.chunk_size_max = 2048; 
//#else 
	// The buffer size is optimized to 8k, since not much performance improvement seen with 16 k. 
//	fs.chunk_size_max = 8192; 
//#endif	 
    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 '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.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) 
{ 
#if (OP_WCP == 1) 
    // Make FFS inaccessible. 
    fs.initerror = EFFS_AGAIN; 
 
    // Finish pending commits/writes. 
    if (ffs_remove("dummy") != EFFS_AGAIN) 
      return EFFS_CORRUPTED; 
#else 
    tw(tr(TR_FUNC, TrFsck, "exit() 0\n")); 
 
#endif 
 
    return EFFS_OK; 
} 
 
 
/****************************************************************************** 
 * 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; 
    uint16 flags_msb, flags_lsb; 
 
    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); 
        fs.format = bhp->version; 
             
        // Format check is performed early because this new version thinks 
        // that the old state is in an intermediate state thus it tries to 
        // fix it and we can not allow that 
        if ((bhp->magic_low  == BLOCK_MAGIC_LOW && 
             bhp->magic_high == BLOCK_MAGIC_HIGH) &&  
            (bhp->version >> 8) != (FFS_FORMAT_VERSION >> 8)) { 
            tw(tr(TR_END, TrFsck, "} %d\n", EFFS_BADFORMAT)); 
            ttw(ttr(TTrInitLow, "} %d" NL, EFFS_BADFORMAT)); 
            return EFFS_BADFORMAT; 
        }      
 
		tw(tr(TR_FUNC, TrFsck, "Block %d, flags 0x%x\n", b, bhp->flags)); 
#if 0          
        // Check block header flag for intermediate states (01 or 10) 
        // NOTEME add further comments? 
        if ((bhp->flags & 0xAAAA) != ((bhp->flags & 0x5555) << 1)) { 
            tw(tr(TR_FUNC, TrFsck,"Intermediate state detected in block header:\n")); 
            // Push the flags to well defined states (01,10 -> 00) 
            flags_msb = (bhp->flags << 1) & 0xAAAA & bhp->flags; 
            flags_lsb = (bhp->flags >> 1) & 0x5555 & bhp->flags; 
            tw(tr(TR_FUNC, TrFsck,"    blk %d (0x%x->0x%x) \n", b, bhp->flags,  
                  BIT_SET(bstat[b].flags, ~(flags_msb | flags_lsb)))); 
            block_flags_write(b, ~(flags_msb | flags_lsb)); 
        } 
#endif 
        bstat[b].flags   = bhp->flags; 
        bstat[b].used    = dev.blocksize; 
        bstat[b].lost    = bstat[b].used; 
        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.  
            // 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 { 
            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; 
		} 
	} 
	 
    // 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, nrepi = 0; 
    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) 
    memset(fs.repi_table, 0, sizeof(fs.repi_table)); 
 
    // 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++; 
#if 1 
            // Check ERASE flag for intermediate states (10 or 01) 
            if ((ip->flags & OT_ERASED_MSb) !=  
                ((ip->flags & (OT_ERASED_MSb >> 1)) << 1)) 
            { 
                tw(tr(TR_FUNC, TrFsck,"Intermediate state detected in inode flag:\n"));     
                tw(tr(TR_FUNC, TrFsck, "    inode %d (0x%x->0x%x)\n",  
                      i, ip->flags, ip->flags & OT_ERASED)); 
                ffsdrv_write_byte(&ip->flags, ip->flags & OT_ERASED); 
            } 
#endif 
            tw(tr(TR_FUNC, TrFsck, "%3d 0x%05X  %3d %3d %4d %3d  %s%s%s%s%s%s%s %6d %s\n", 
                  i, 
                  location2offset(ip->location), 
                  ip->child, ip->sibling, 
                  ip->sequence, ip->updates, 
                  is_object(ip, OTE_DIR)     ? " d" : "", 
                  is_object(ip, OTE_LINK)    ? " l" : "", 
                  is_object(ip, OTE_FILE)    ? " f" : "", 
                  is_object(ip, OTE_SEGMENT) ? " s" : "", 
                  is_object(ip, OT_REP)     ? "rp" : "", 
                  is_object_erased(ip)  ? "  " : "", 
                  IS_BIT_SET(ip->flags, OFE_READONLY) && !is_object_erased(ip) ? 
                  "r" : " ", 
                  ip->size, 
                  // Erased chunks do not have any name so we cannot trace erased objects! 
                  (ip->size && !is_object(ip, OTE_SEGMENT) && !is_object_erased(ip) ?  
                   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. Only exception is the 
                // replacement inode, it is not counted as an object 
                if (!is_object(ip, OT_REP)) { 
                    bstat[block].lost -= ip->size; 
                    bstat[block].objects++; 
                } 
                // test if this is the root inode. store index if it is. 
                if (!is_object(ip, OTE_SEGMENT) && !is_object(ip, OT_REP)) { 
                    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_erased(ip)) { 
                // 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 = 0x%x)\n", ip->flags)); 
            } 
 
            if (is_object(ip, OT_REP)) { 
                // Add the replacement inode in the ram table 
                fs.repi_table[nrepi] = i; 
                nrepi++; 
                if (nrepi == FFS_REPLACEMENT_INODES_MAX) 
                    ffs_panic(EFFS_2MANYREPI); 
            } 
        } 
    } 
    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 bhb; 
    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)); 
 
    POWERFAIL_DOMAIN_BEGIN(PFM_BLOCKHEADER); 
    POWERFAIL_SET_ADDR((int)bhp); 
 
    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)); 
        } 
    } 
 
    bstat[b].flags = BF_IS_EMPTY; 
    bstat[b].used = 0; 
    bstat[b].lost = 0; 
    bstat[b].objects = 0; 
 
    block_flags_write(b, BF_WRITING); 
 
    // Write block header from local buffer 
    bhb.magic_low  = BLOCK_MAGIC_LOW; 
    bhb.magic_high = BLOCK_MAGIC_HIGH; 
    bhb.version    = FFS_FORMAT_VERSION; 
    bhb.age        = age; 
    // Do not write flag and reserved  
    ffsdrv.write(bhp, &bhb, sizeof(bhb.magic_low) + sizeof(bhb.magic_high) +  
                 sizeof(bhb.version) + sizeof(age_t)); 
 
    block_flags_write(b, BF_FREE); 
 
    POWERFAIL_DOMAIN_END(); 
 
    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(OTE_DIR); 
 
    if ((fs.ijournal = journal_create(0)) < 0) { 
        tw(tr(TR_END, TrFormat, "} %d\n", fs.ijournal)); 
        return fs.ijournal; 
    } 
 
    fs.initerror = ffs_initialize(); 
	ffs_initerror[4] = fs.initerror; 
 
    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_ram.repli = 0; 
    fs.link_child = 1;   //Default link child in journal_commit() 
 
    if (oldi == 0) { 
        fs.journal.diri     = 0; 
        fs.journal.oldi     = 0; 
        fs.journal_ram.flags = 0xFF; 
        fs.journal_ram.location = 0; 
        fs.journal_ram.size     = 0; 
    } 
    else { 
        struct inode_s *oldip = inode_addr(oldi); 
        fs.journal.diri     = oldi; 
        fs.journal.oldi     = oldi; 
        fs.journal_ram.flags    = oldip->flags | OF_MASK;  // Clean valid flag 
        fs.journal_ram.location = oldip->location; 
        fs.journal_ram.size     = oldip->size; 
    } 
} 
 
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))); 
 
    POWERFAIL_DOMAIN_BEGIN(PFM_JOURNAL); 
    POWERFAIL_SET_ADDR(0); 
 
    // If this is a create, set the object type 
    if (type != 0 && fs.journal.oldi == 0) 
        fs.journal_ram.flags = (fs.journal_ram.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; 
    } 
     
    POWERFAIL_BOUNDARY(JOURNAL_TEST_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)); 
    } 
     
    POWERFAIL_BOUNDARY(JOURNAL_TEST_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); 
    } 
     
    POWERFAIL_BOUNDARY(JOURNAL_TEST_READY); 
  
    journal_commit(0); 
 
    // journal_commit() change the domain thus we must set it again 
    POWERFAIL_DOMAIN_BEGIN(PFM_JOURNAL); 
    POWERFAIL_SET_ADDR(0); 
     
    POWERFAIL_BOUNDARY(JOURNAL_TEST_COMMITTED); 
 
    // Advance journal file's state 
    ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE); 
 
    POWERFAIL_BOUNDARY(JOURNAL_TEST_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) { 
 
        if (fs.journal_pos == fs.journal_size - sizeof(struct journal_s))  
            ffs_panic(EFFS_JNLFULL); 
         
        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)); 
    } 
     
    POWERFAIL_DOMAIN_END(); 
    tw(tr(TR_END, TrJournal, "}\n")); 
} 
 
// Write contents of fs.journal to FFS meta data (inodes).  
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)); 
 
    POWERFAIL_DOMAIN_BEGIN(PFM_INODES); 
    POWERFAIL_SET_ADDR(0); 
 
    if (fs.journal.i) 
    { 
        struct inode_s ib;  // Inode buffer 
         
        // Set all elements to 0xFF.. NOTEME: do this in another way? 
        memcpy(&ib, ip, sizeof(ib)); 
 
#if (LINEAR_FILE_SYSTEM) 
	if(fs.journal.align == 0xee) { 
		ib.reserved = 0xee; 
		fs.journal.align = 0xff; 
	} 
#endif 
	 
        // If this is a create, set the object type 
        if (type != 0 && fs.journal.oldi == 0) 
            fs.journal_ram.flags = (fs.journal_ram.flags & OF_MASK) | type; 
         
        tw(tr(TR_FUNC, TrJournal, "loc   = 0x%04x, size = %d\n", 
              fs.journal_ram.location, fs.journal_ram.size)); 
        ib.location = fs.journal_ram.location; 
        ib.size = fs.journal_ram.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! 
            ib.child = get_child(oldip); 
 
        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) 
            ib.sequence = fs.sequence++; 
        if (fs.journal.oldi == 0) 
            ib.updates = 0; 
        else 
            ib.updates = oldip->updates + 1; 
 
        // 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_ram.flags)); 
        ib.flags = ip->flags & fs.journal_ram.flags; 
 
        // Write the inode to flash 
        ffsdrv.write(ip, &ib, sizeof(ib)); 
 
            // 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(get_repi(-fs.journal.diri)); 
                    ffsdrv.write_halfword((uint16 *) &dp->child, fs.journal.i); 
                } 
                else { 
                    tw(tr(TR_NULL,  TrJournal, "sibling\n")); 
                    dp = inode_addr(get_repi(fs.journal.diri)); 
                    ffsdrv.write_halfword((uint16 *) &dp->sibling, fs.journal.i); 
                } 
            } 
 
        // 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)); 
        POWERFAIL_BOUNDARY(JOURNAL_TEST_COMMITTING); 
 
        // Validate the object (write valid flags) 
        ffsdrv_write_byte(&ip->flags, (ip->flags & OT_VALID)); 
    } 
 
    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 & oldip->flags); 
 
        // 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_ram.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_ram.repli > 0) 
            object_remove(fs.journal_ram.repli); // Ignore error!  
    } 
 
    POWERFAIL_DOMAIN_END(); 
 
    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)); 
    memcpy(&fs.ojournal_ram, &fs.journal_ram, sizeof(struct journal_ram_s)); 
    fs.journal_depth++; 
    if (fs.journal_depth > 1) { 
        ffs_panic(EFFS_JNLPUSH2DPTH); 
		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) { 
        ffs_panic(EFFS_JNLPOP2DPTH); 
		return -1; 
    } 
    memcpy(&fs.journal, &fs.ojournal, sizeof(struct journal_s)); 
    memcpy(&fs.journal_ram, &fs.ojournal_ram, sizeof(struct journal_ram_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; 
    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; 
        } 
    } 
 
	ip = inode_addr(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) { 
        struct inode_s *obj_ip = inode_addr(addr->i); 
        tw(tr(TR_FUNC, TrJournal, "First non-complete journal is in READY state\n")); 
 
        // Is the object valid? 
        if (is_object_valid(obj_ip)) { 
            struct inode_s *ip; 
 
            // Copy the entry into fs.journal. 
            tw(tr(TR_FUNC, TrJournal, "Last journal is READY/PREVALID\n")); 
            memcpy(&fs.journal, addr, sizeof(fs.journal)); 
 
            ip = inode_addr(fs.journal.i); 
            // The existing fields 'flags', 'location' and 'size' is already 
            // written but they are re-written in journal_commit() thus the 
            // journal_ram struct must be filled with valid data. 
            fs.journal_ram.flags    = ip->flags ; 
            fs.journal_ram.location = ip->location; 
            fs.journal_ram.size     = ip->size;                         
            journal_end(0); // Validate obj and maybe erase old obj 
            i = 1; 
        } 
        else { 
            // Skip last modify operation and replace the diri with a 
            // replacement inode. Furthermore, clean the last allocated inode  
            struct inode_s ib;   
            // Clean inode 
            memset(&ib, 0, sizeof(ib)); 
            ffsdrv.write(obj_ip, &ib, sizeof(ib));   
 
            create_replacementinode(addr); 
            ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE);  
            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 WRITING and READY\n")); 
        ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE); 
        fs.journal_pos += sizeof(fs.journal); 
    } 
 
    // Never create a new journal file if a replacementinode just have been 
    // made because the new replacementinode is not yet added in the table 
    // fs.repi_table[] and it could be the journal which is replaced. 
    if (i == EFFS_OK) { 
        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 there is use an old FFS image with a newer FFS 
            // version. 
            if ((i = journal_create(fs.ijournal)) <= 0) { 
                fs.ijournal = 0; 
                return i; 
            } 
        } 
        // Minimize the risk of running out of journal space. 
        if (fs.journal_pos >= fs.journal_size - FFS_JOURNAL_MARGIN *  
            sizeof(struct journal_s)) { 
            tw(tr(TR_FUNC, TrJournal, "Journal file (near) full! Create new\n")); 
            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_ram.flags = BIT_SET(fs.journal_ram.flags, OFE_READONLY); 
 
    // commit the creation or relocation 
    if (oldi != 0) 
        journal_end(0); 
    else { 
        journal_commit(OTE_FILE); 
        fs.journal_pos = JOURNAL_POS_INITIAL; 
    } 
 
    tw(tr(TR_END, TrJournal, "} %d\n", i)); 
 
    return i; 
} 
 
/****************************************************************************** 
 * Replacementinode 
 ******************************************************************************/ 
 
// Validate the replacement inode, mark old diri as replaced and close 
// journal entry 
void validate_replacementinode(struct journal_s *addr) 
{ 
    struct inode_s *ip, *dir_ip; 
    iref_t zero[2] = {0, 0}; 
 
    ip = inode_addr(addr->i); 
 
    if (addr->oldi > 0) 
        dir_ip = inode_addr(addr->oldi); 
    else 
        dir_ip = inode_addr(-addr->oldi); 
 
    // Validate replacementinode 
    ffsdrv_write_byte(&ip->flags, (OT_VALID & ip->flags)); 
    // Mark directory inode as replaced (set child and sibling to zero) 
    ffsdrv.write(&dir_ip->child, zero, sizeof(zero)); 
    // Mark the creation of the replacementinode as done 
    ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE);  
 
    fs.journal_pos++;  
    if (fs.journal_pos == fs.journal_size)  
        ffs_panic(EFFS_JNLFULL); 
} 
 
// Create a replacementinode for diri. It is possible that there exist a 
// non-complete replacement inode because the previous creation of the inode 
// was interrupted. In this case the inode will be finalized (if prevalid) 
// or removed so there can be made a new. 
void create_replacementinode(struct journal_s *old_addr) 
{ 
    int j; 
    struct inode_s *ip, *dir_ip, ib; 
    struct journal_s *addr = old_addr; 
 
    tw(tr(TR_BEGIN, TrJournal, "create_repi(0x%x) {\n", old_addr)); 
 
    tw(tr(TR_NULL, TrJournal, "Search for next non-complete journal entry:")); 
    addr++;  // Advance one journal entry (the current state is READY) 
    for (j = 1 + fs.journal_pos / sizeof(fs.journal);/* 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); 
 
    if ((addr->state == (uint8) JOURNAL_IS_READY) &&  
        is_object_valid(inode_addr(addr->i))) { 
        validate_replacementinode(addr); 
        return; 
    } 
 
    // If the journal entry not is EMPTY it must be a non completed 
    // replacement inode which must be remove before there is created a new 
    if (addr->state != (uint8) JOURNAL_IS_EMPTY) { 
        tw(tr(TR_FUNC, TrJournal, "Remove old repi\n")); 
        ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE); 
        // Advance journal.  
        addr++;  
        fs.journal_pos++;  
        if (fs.journal_pos == fs.journal_size)  
            ffs_panic(EFFS_JNLFULL); 
    } 
 
    tw(tr(TR_FUNC, TrJournal, "create replacementinode\n")); 
 
    journal_push(); 
 
    journal_begin(0); 
    // Alloc an inode. Note fs.inodes_max is temporarily added 
    // a margin because this alloc is allowed to use the last inode 
    fs.inodes_max += FFS_INODES_MARGIN; 
    fs.journal.i = inode_alloc_try(); 
    fs.inodes_max -= FFS_INODES_MARGIN; 
 
    fs.journal.oldi = get_repi(old_addr->diri); 
    fs.journal_ram.location = fs.journal.oldi > 0 ? fs.journal.oldi : -fs.journal.oldi; 
    fs.journal_ram.flags = OT_REP; 
 
    ffsdrv_write_byte(&addr->state, JOURNAL_IS_WRITING); 
    fs.journal.state = JOURNAL_IS_WRITING; 
 
    // Write ram journal to flash and advance journal to READY 
    tw(tr(TR_FUNC, TrJournal, "Write ram journal to flash\n")); 
    ffsdrv.write(addr, &fs.journal, sizeof(fs.journal)); 
    ffsdrv_write_byte(&addr->state, JOURNAL_IS_READY);  
 
    ip = inode_addr(fs.journal.i); 
    // NOTEME: change the macro inode_addr() to handle a negative inode? 
    if (addr->oldi > 0) 
        dir_ip = inode_addr(addr->oldi); 
    else 
        dir_ip = inode_addr(-addr->oldi); 
 
    tw(tr(TR_FUNC, TrJournal,"Replace i: %d (%s%s%s%s%s %s) with %d\n",  
          fs.journal.oldi, 
          is_object(dir_ip, OTE_DIR)     ? " d" : "", 
          is_object(dir_ip, OTE_LINK)    ? " l" : "", 
          is_object(dir_ip, OTE_FILE)    ? " f" : "", 
          is_object(dir_ip, OTE_SEGMENT) ? " s" : "", 
          is_object(dir_ip, OT_REP)     ? "rp" : "", 
          (dir_ip->size && !is_object(dir_ip, OTE_SEGMENT) &&  
           !is_object_erased(dir_ip) && is_object_valid(dir_ip) ?  
           addr2name(offset2addr(location2offset(dir_ip->location))) : ""), 
          fs.journal.i)); 
 
    // Init inode buffer 
    // Set all elements to 0xFF.. NOTEME: do this in another way? 
    memcpy(&ib, ip, sizeof(ib)); 
    ib.size = ib.sequence = ib.updates = 0; 
    ib.flags = fs.journal_ram.flags | OF_MASK;  
    ib.location = fs.journal_ram.location;   
 
    // Write the replacement inodes Child or Sibling field 
    if (fs.journal.oldi < 0) { 
        // diri child is maybe invalid thus we keep it as empty and only 
        // write sibling 
        tw(tr(TR_FUNC, TrJournal, "copy sibling: 0x%x\n", dir_ip->sibling)); 
        ib.sibling = dir_ip->sibling; 
    } 
    else { 
        // diri sibling is maybe invalid thus we keep it as empty and only 
        // write child 
        tw(tr(TR_FUNC, TrJournal, "copy child: 0x%x\n", dir_ip->child)); 
        ib.child = dir_ip->child; 
    } 
 
    // Write the inode to flash 
    ffsdrv.write(ip, &ib, sizeof(ib)); 
 
    journal_pop(); 
 
    validate_replacementinode(addr); 
 
    tw(tr(TR_END, TrJournal, "} 0\n")); 
} 
 
/****************************************************************************** 
 * 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(); 
    } 
    // Suspend a write in progress (only applicable if we are using a 
    // multi-bank device driver) 
    else if (dev.state == DEV_WRITE) { 
        ffsdrv.write_suspend(); 
    } 
         
    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(); 
     
    // Resume a write in progress (only applicable if we are using a 
    // multi-bank device driver) 
    else if (dev.state == DEV_WRITE_SUSPEND)  
        ffsdrv.write_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 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; 
}