static int
nffs_restore_should_sweep_inode_entry(struct nffs_inode_entry *inode_entry,
                                      int *out_should_sweep)
{
    struct nffs_inode inode;
    int rc;

    /* Determine if the inode is a dummy.  Dummy inodes have a reference count
     * of 0.  If it is a dummy, increment its reference count back to 1 so that
     * it can be properly deleted.  The presence of a dummy inode during the
     * final sweep step indicates file system corruption.  If the inode is a
     * directory, all its children should have been migrated to the /lost+found
     * directory prior to this.
     */
    if (inode_entry->nie_refcnt == 0) {
        *out_should_sweep = 1;
        inode_entry->nie_refcnt++;
        return 0;
    }

    /* Determine if the inode has been deleted.  If an inode has no parent (and
     * it isn't the root directory), it has been deleted from the disk and
     * should be swept from the RAM representation.
     */
    if (inode_entry->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) {
        rc = nffs_inode_from_entry(&inode, inode_entry);
        if (rc != 0) {
            *out_should_sweep = 0;
            return rc;
        }

        if (inode.ni_parent == NULL) {
            *out_should_sweep = 1;
            return 0;
        }
    }

    /* If this is a file inode, verify that all of its constituent blocks are
     * present.
     */
    if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
        rc = nffs_restore_validate_block_chain(
                inode_entry->nie_last_block_entry);
        if (rc == FS_ECORRUPT) {
            *out_should_sweep = 1;
            return 0;
        } else if (rc != 0) {
            *out_should_sweep = 0;
            return rc;
        }
    }

    /* This is a valid inode; don't sweep it. */
    *out_should_sweep = 0;
    return 0;
}
Ejemplo n.º 2
0
/**
 * Triggers a garbage collection cycle.  This is implemented as follows:
 *
 *  (1) The non-scratch area with the lowest garbage collection sequence
 *      number is selected as the "source area."  If there are other areas
 *      with the same sequence number, the first one encountered is selected.
 *
 *  (2) The source area's ID is written to the scratch area's header,
 *      transforming it into a non-scratch ID.  The former scratch area is now
 *      known as the "destination area."
 *
 *  (3) The RAM representation is exhaustively searched for objects which are
 *      resident in the source area.  The copy is accomplished as follows:
 *
 *      For each inode:
 *          (a) If the inode is resident in the source area, copy the inode
 *              record to the destination area.
 *
 *          (b) Walk the inode's list of data blocks, starting with the last
 *              block in the file.  Each block that is resident in the source
 *              area is copied to the destination area.  If there is a run of
 *              two or more blocks that are resident in the source area, they
 *              are consolidated and copied to the destination area as a single
 *              new block.
 *
 *  (4) The source area is reformatted as a scratch sector (i.e., its header
 *      indicates an ID of 0xffff).  The area's garbage collection sequence
 *      number is incremented prior to rewriting the header.  This area is now
 *      the new scratch sector.
 *
 * NOTE:
 *     Garbage collection invalidates all cached data blocks.  Whenever this
 *     function is called, all existing nffs_cache_block pointers are rendered
 *     invalid.  If you maintain any such pointers, you need to reset them
 *     after calling this function.  Cached inodes are not invalidated by
 *     garbage collection.
 *
 *     If a parent function potentially calls this function, the caller of the
 *     parent function needs to explicitly check if garbage collection
 *     occurred.  This is done by inspecting the nffs_gc_count variable before
 *     and after calling the function.
 *
 * @param out_area_idx      On success, the ID of the cleaned up area gets
 *                              written here.  Pass null if you do not need
 *                              this information.
 *
 * @return                  0 on success; nonzero on error.
 */
int
nffs_gc(uint8_t *out_area_idx)
{
    struct nffs_hash_entry *entry;
    struct nffs_hash_entry *next;
    struct nffs_area *from_area;
    struct nffs_area *to_area;
    struct nffs_inode_entry *inode_entry;
    uint32_t area_offset;
    uint8_t from_area_idx;
    uint8_t area_idx;
    int rc;
    int i;

    from_area_idx = nffs_gc_select_area();
    from_area = nffs_areas + from_area_idx;
    to_area = nffs_areas + nffs_scratch_area_idx;

    rc = nffs_format_from_scratch_area(nffs_scratch_area_idx,
                                       from_area->na_id);
    if (rc != 0) {
        return rc;
    }

    for (i = 0; i < NFFS_HASH_SIZE; i++) {
        entry = SLIST_FIRST(nffs_hash + i);
        while (entry != NULL) {
            next = SLIST_NEXT(entry, nhe_next);

            if (nffs_hash_id_is_inode(entry->nhe_id)) {
                /* The inode gets copied if it is in the source area. */
                nffs_flash_loc_expand(entry->nhe_flash_loc,
                                      &area_idx, &area_offset);
                inode_entry = (struct nffs_inode_entry *)entry;
                if (area_idx == from_area_idx) {
                    rc = nffs_gc_copy_inode(inode_entry,
                                            nffs_scratch_area_idx);
                    if (rc != 0) {
                        return rc;
                    }
                }

                /* If the inode is a file, all constituent data blocks that are
                 * resident in the source area get copied.
                 */
                if (nffs_hash_id_is_file(entry->nhe_id)) {
                    rc = nffs_gc_inode_blocks(inode_entry, from_area_idx,
                                              nffs_scratch_area_idx, &next);
                    if (rc != 0) {
                        return rc;
                    }
                }
            }

            entry = next;
        }
    }

    /* The amount of written data should never increase as a result of a gc
     * cycle.
     */
    assert(to_area->na_cur <= from_area->na_cur);

    /* Turn the source area into the new scratch area. */
    from_area->na_gc_seq++;
    rc = nffs_format_area(from_area_idx, 1);
    if (rc != 0) {
        return rc;
    }

    if (out_area_idx != NULL) {
        *out_area_idx = nffs_scratch_area_idx;
    }

    nffs_scratch_area_idx = from_area_idx;

    /* Garbage collection renders the cache invalid:
     *     o All cached blocks are now invalid; drop them.
     *     o Flash locations of inodes may have changed; the cached inodes need
     *       updated to reflect this.
     */
    rc = nffs_cache_inode_refresh();
    if (rc != 0) {
        return rc;
    }

    /* Increment the garbage collection counter so that client code knows to
     * reset its pointers to cached objects.
     */
    nffs_gc_count++;
    STATS_INC(nffs_stats, nffs_gccnt);

    return 0;
}
Ejemplo n.º 3
0
static int
nffs_gc_inode_blocks(struct nffs_inode_entry *inode_entry,
                     uint8_t from_area_idx, uint8_t to_area_idx,
                     struct nffs_hash_entry **inout_next)
{
    struct nffs_hash_entry *last_entry;
    struct nffs_hash_entry *entry;
    struct nffs_block block;
    uint32_t prospective_data_len;
    uint32_t area_offset;
    uint32_t data_len;
    uint8_t area_idx;
    int multiple_blocks;
    int rc;

    assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));

    data_len = 0;
    last_entry = NULL;
    multiple_blocks = 0;
    entry = inode_entry->nie_last_block_entry;
    while (entry != NULL) {
        rc = nffs_block_from_hash_entry(&block, entry);
        if (rc != 0) {
            return rc;
        }

        nffs_flash_loc_expand(entry->nhe_flash_loc, &area_idx, &area_offset);
        if (area_idx == from_area_idx) {
            if (last_entry == NULL) {
                last_entry = entry;
            }

            prospective_data_len = data_len + block.nb_data_len;
            if (prospective_data_len <= nffs_block_max_data_sz) {
                data_len = prospective_data_len;
                if (last_entry != entry) {
                    multiple_blocks = 1;
                }
            } else {
                rc = nffs_gc_block_chain(last_entry, multiple_blocks, data_len,
                                         to_area_idx, inout_next);
                if (rc != 0) {
                    return rc;
                }
                last_entry = entry;
                data_len = block.nb_data_len;
                multiple_blocks = 0;
            }
        } else {
            if (last_entry != NULL) {
                rc = nffs_gc_block_chain(last_entry, multiple_blocks, data_len,
                                         to_area_idx, inout_next);
                if (rc != 0) {
                    return rc;
                }

                last_entry = NULL;
                data_len = 0;
                multiple_blocks = 0;
            }
        }

        entry = block.nb_prev;
    }

    if (last_entry != NULL) {
        rc = nffs_gc_block_chain(last_entry, multiple_blocks, data_len,
                                 to_area_idx, inout_next);
        if (rc != 0) {
            return rc;
        }
    }

    return 0;
}
Ejemplo n.º 4
0
/**
 * Triggers a garbage collection cycle.  This is implemented as follows:
 *
 *  (1) The non-scratch area with the lowest garbage collection sequence
 *      number is selected as the "source area."  If there are other areas
 *      with the same sequence number, the first one encountered is selected.
 *
 *  (2) The source area's ID is written to the scratch area's header,
 *      transforming it into a non-scratch ID.  The former scratch area is now
 *      known as the "destination area."
 *
 *  (3) The RAM representation is exhaustively searched for objects which are
 *      resident in the source area.  The copy is accomplished as follows:
 *
 *      For each inode:
 *          (a) If the inode is resident in the source area, copy the inode
 *              record to the destination area.
 *
 *          (b) Walk the inode's list of data blocks, starting with the last
 *              block in the file.  Each block that is resident in the source
 *              area is copied to the destination area.  If there is a run of
 *              two or more blocks that are resident in the source area, they
 *              are consolidated and copied to the destination area as a single
 *              new block.
 *
 *  (4) The source area is reformatted as a scratch sector (i.e., its header
 *      indicates an ID of 0xffff).  The area's garbage collection sequence
 *      number is incremented prior to rewriting the header.  This area is now
 *      the new scratch sector.
 *
 * @param out_area_idx      On success, the ID of the cleaned up area gets
 *                              written here.  Pass null if you do not need
 *                              this information.
 *
 * @return                  0 on success; nonzero on error.
 */
int
nffs_gc(uint8_t *out_area_idx)
{
    struct nffs_hash_entry *entry;
    struct nffs_hash_entry *next;
    struct nffs_area *from_area;
    struct nffs_area *to_area;
    struct nffs_inode_entry *inode_entry;
    uint32_t area_offset;
    uint8_t from_area_idx;
    uint8_t area_idx;
    int rc;
    int i;

    from_area_idx = nffs_gc_select_area();
    from_area = nffs_areas + from_area_idx;
    to_area = nffs_areas + nffs_scratch_area_idx;

    rc = nffs_format_from_scratch_area(nffs_scratch_area_idx,
                                       from_area->na_id);
    if (rc != 0) {
        return rc;
    }

    for (i = 0; i < NFFS_HASH_SIZE; i++) {
        entry = SLIST_FIRST(nffs_hash + i);
        while (entry != NULL) {
            next = SLIST_NEXT(entry, nhe_next);

            if (nffs_hash_id_is_inode(entry->nhe_id)) {
                /* The inode gets copied if it is in the source area. */
                nffs_flash_loc_expand(entry->nhe_flash_loc,
                                      &area_idx, &area_offset);
                inode_entry = (struct nffs_inode_entry *)entry;
                if (area_idx == from_area_idx) {
                    rc = nffs_gc_copy_inode(inode_entry,
                                            nffs_scratch_area_idx);
                    if (rc != 0) {
                        return rc;
                    }
                }

                /* If the inode is a file, all constituent data blocks that are
                 * resident in the source area get copied.
                 */
                if (nffs_hash_id_is_file(entry->nhe_id)) {
                    rc = nffs_gc_inode_blocks(inode_entry, from_area_idx,
                                              nffs_scratch_area_idx, &next);
                    if (rc != 0) {
                        return rc;
                    }
                }
            }

            entry = next;
        }
    }

    /* The amount of written data should never increase as a result of a gc
     * cycle.
     */
    assert(to_area->na_cur <= from_area->na_cur);

    /* Turn the source area into the new scratch area. */
    from_area->na_gc_seq++;
    rc = nffs_format_area(from_area_idx, 1);
    if (rc != 0) {
        return rc;
    }

    if (out_area_idx != NULL) {
        *out_area_idx = nffs_scratch_area_idx;
    }

    nffs_scratch_area_idx = from_area_idx;

    return 0;
}
/**
 * Determines if the specified inode should be added to the RAM representation
 * and adds it if appropriate.
 *
 * @param disk_inode            The inode just read from flash.
 * @param area_idx              The index of the area containing the inode.
 * @param area_offset           The offset within the area of the inode.
 *
 * @return                      0 on success; nonzero on failure.
 */
static int
nffs_restore_inode(const struct nffs_disk_inode *disk_inode, uint8_t area_idx,
                   uint32_t area_offset)
{
    struct nffs_inode_entry *inode_entry;
    struct nffs_inode_entry *parent;
    struct nffs_inode inode;
    int new_inode;
    int do_add;
    int rc;

    new_inode = 0;

    /* Check the inode's CRC.  If the inode is corrupt, discard it. */
    rc = nffs_crc_disk_inode_validate(disk_inode, area_idx, area_offset);
    if (rc != 0) {
        goto err;
    }

    inode_entry = nffs_hash_find_inode(disk_inode->ndi_id);
    if (inode_entry != NULL) {
        rc = nffs_restore_inode_gets_replaced(inode_entry, disk_inode,
                                              &do_add);
        if (rc != 0) {
            goto err;
        }

        if (do_add) {
            if (inode_entry->nie_hash_entry.nhe_flash_loc !=
                NFFS_FLASH_LOC_NONE) {

                rc = nffs_inode_from_entry(&inode, inode_entry);
                if (rc != 0) {
                    return rc;
                }
                if (inode.ni_parent != NULL) {
                    nffs_inode_remove_child(&inode);
                }
            }
 
            inode_entry->nie_hash_entry.nhe_flash_loc =
                nffs_flash_loc(area_idx, area_offset);
        }
    } else {
        inode_entry = nffs_inode_entry_alloc();
        if (inode_entry == NULL) {
            rc = FS_ENOMEM;
            goto err;
        }
        new_inode = 1;
        do_add = 1;

        inode_entry->nie_hash_entry.nhe_id = disk_inode->ndi_id;
        inode_entry->nie_hash_entry.nhe_flash_loc =
            nffs_flash_loc(area_idx, area_offset);

        nffs_hash_insert(&inode_entry->nie_hash_entry);
    }

    if (do_add) {
        inode_entry->nie_refcnt = 1;

        if (disk_inode->ndi_parent_id != NFFS_ID_NONE) {
            parent = nffs_hash_find_inode(disk_inode->ndi_parent_id);
            if (parent == NULL) {
                rc = nffs_restore_dummy_inode(disk_inode->ndi_parent_id,
                                             &parent);
                if (rc != 0) {
                    goto err;
                }
            }

            rc = nffs_inode_add_child(parent, inode_entry);
            if (rc != 0) {
                goto err;
            }
        } 


        if (inode_entry->nie_hash_entry.nhe_id == NFFS_ID_ROOT_DIR) {
            nffs_root_dir = inode_entry;
        }
    }

    if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
        if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_file_id) {
            nffs_hash_next_file_id = inode_entry->nie_hash_entry.nhe_id + 1;
        }
    } else {
        if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_dir_id) {
            nffs_hash_next_dir_id = inode_entry->nie_hash_entry.nhe_id + 1;
        }
    }

    return 0;

err:
    if (new_inode) {
        nffs_inode_entry_free(inode_entry);
    }
    return rc;
}
Ejemplo n.º 6
0
/**
 * Determines if the specified inode should be added to the RAM representation
 * and adds it if appropriate.
 *
 * @param disk_inode            The inode just read from flash.
 * @param area_idx              The index of the area containing the inode.
 * @param area_offset           The offset within the area of the inode.
 *
 * @return                      0 on success; nonzero on failure.
 */
static int
nffs_restore_inode(const struct nffs_disk_inode *disk_inode, uint8_t area_idx,
                   uint32_t area_offset)
{
    struct nffs_inode_entry *inode_entry;
    struct nffs_inode_entry *parent;
    struct nffs_inode inode;
    struct nffs_hash_entry *lastblock_entry = NULL;
    int new_inode;
    int do_add;
    int rc;

    new_inode = 0;

    /* Check the inode's CRC.  If the inode is corrupt, discard it. */
    rc = nffs_crc_disk_inode_validate(disk_inode, area_idx, area_offset);
    if (rc != 0) {
        goto err;
    }

    inode_entry = nffs_hash_find_inode(disk_inode->ndi_id);

    /*
     * Inode has already been restored. Determine whether this version
     * from disk should replace the previous version referenced in RAM.
     */
    if (inode_entry != NULL) {

        if (disk_inode->ndi_flags & NFFS_INODE_FLAG_DELETED) {
            /*
             * Restore this inode even though deleted on disk
             * so the additional restored blocks have a place to go
             */
            NFFS_LOG(DEBUG, "restoring deleted inode %x\n",
                     (unsigned int)disk_inode->ndi_id);
            nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DELETED);
        }

        /*
         * inodes get replaced if they're dummy entries (i.e. allocated
         * as place holders until the actual inode is restored), or this is
         * a more recent version of the inode which supercedes the old.
         */
        rc = nffs_restore_inode_gets_replaced(inode_entry, disk_inode, &do_add);
        if (rc != 0) {
            goto err;
        }

        if (do_add) { /* replace in this case */
            if (!nffs_inode_is_dummy(inode_entry)) {
                /*
                 * if it's not a dummy, read block from flash
                 */
                rc = nffs_inode_from_entry(&inode, inode_entry);
                if (rc != 0) {
                    return rc;
                }

                /*
                 * inode is known to be obsolete
                 */
                if (nffs_inode_getflags(inode_entry, 
                                        NFFS_INODE_FLAG_OBSOLETE)) {
                    nffs_inode_unsetflags(inode_entry,
                                          NFFS_INODE_FLAG_OBSOLETE);
                }

                if (inode.ni_parent != NULL) {
                    nffs_inode_remove_child(&inode);
                }

                /*
                 * If this is a delete record, subsequent inode and restore
                 * records from flash may be ignored.
                 * If the parent is NULL, this inode has been deleted. (old)
                 * XXX if we could count on delete records for every inode,
                 * we wouldn't need to check for the root directory looking
                 * like a delete record because of it's parent ID.
                 */
                if (inode_entry->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) {
                    if (disk_inode->ndi_flags & NFFS_INODE_FLAG_DELETED ||
                        disk_inode->ndi_parent_id == NFFS_ID_NONE) {

                        nffs_inode_setflags(inode_entry,
                                            NFFS_INODE_FLAG_DELETED);
                    }
                }

            } else {
                /*
                 * The existing inode entry was added as dummy.
                 * The restore operation clears that state.
                 */

                /* If it's a directory, it was added as a parent to
                 * one of it's children who were restored first.
                 */
                if (nffs_inode_getflags(inode_entry, 
                                         NFFS_INODE_FLAG_DUMMYPARENT)) {
                    assert(nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id));
                    nffs_inode_unsetflags(inode_entry, 
                                         NFFS_INODE_FLAG_DUMMYPARENT);
                }

                /*
                 * If it's a file, it was added to store a lastblock
                 */
                if (nffs_inode_getflags(inode_entry, 
                                         NFFS_INODE_FLAG_DUMMYINOBLK)) {
                    assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id));
                    nffs_inode_unsetflags(inode_entry, 
                                         NFFS_INODE_FLAG_DUMMYINOBLK);
                }

                /*
                 * Also, since it's a dummy, clear this flag too
                 */
                if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMY)) {
                    nffs_inode_unsetflags(inode_entry, NFFS_INODE_FLAG_DUMMY);
                }
            }
 
            /*
             * Update location to reference new location in flash
             */
            inode_entry->nie_hash_entry.nhe_flash_loc =
                                    nffs_flash_loc(area_idx, area_offset);
        }
        
    } else {
        inode_entry = nffs_inode_entry_alloc();
        if (inode_entry == NULL) {
            rc = FS_ENOMEM;
            goto err;
        }
        new_inode = 1;
        do_add = 1;

        inode_entry->nie_hash_entry.nhe_id = disk_inode->ndi_id;
        inode_entry->nie_hash_entry.nhe_flash_loc =
                              nffs_flash_loc(area_idx, area_offset);
        inode_entry->nie_last_block_entry = NULL; /* for now */

        nffs_hash_insert(&inode_entry->nie_hash_entry);
    }

    /*
     * inode object has been restored and the entry is in the hash
     * Check whether the lastblock and parent have also been restored
     * and link up or allocate dummy entries as appropriate.
     */
    if (do_add) {
        inode_entry->nie_refcnt = 1;

        if (disk_inode->ndi_flags & NFFS_INODE_FLAG_DELETED) {
            /*
             * Restore this inode even though deleted on disk
             * so the additional restored blocks have a place to go
             */
            NFFS_LOG(DEBUG, "restoring deleted inode %x\n",
                     (unsigned int)disk_inode->ndi_id);
            nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DELETED);
        }

        /*
         * Inode has a lastblock on disk.
         * Add reference to last block entry if in hash table
         * otherwise add a dummy block entry for later update
         */
        if (disk_inode->ndi_lastblock_id != NFFS_ID_NONE &&
                nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
            lastblock_entry =
              nffs_hash_find_block(disk_inode->ndi_lastblock_id);

            /*
             * Lastblock has already been restored.
             */
            if (lastblock_entry != NULL) {
                if (lastblock_entry->nhe_id == disk_inode->ndi_lastblock_id) {
                    inode_entry->nie_last_block_entry = lastblock_entry;
                }

            } else {
                /*
                 * Insert a temporary reference to a 'dummy' block entry
                 * When block is restored, it will update this dummy and
                 * the entry of this inode is updated to flash location
                 */
                rc = nffs_block_entry_reserve(&lastblock_entry);
                if (lastblock_entry == NULL) {
                    rc = FS_ENOMEM;
                    goto err;
                }

                lastblock_entry->nhe_id = disk_inode->ndi_lastblock_id;
                lastblock_entry->nhe_flash_loc = NFFS_FLASH_LOC_NONE;
                inode_entry->nie_last_block_entry = lastblock_entry;
                nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DUMMYLSTBLK);
                nffs_hash_insert(lastblock_entry);

                if (lastblock_entry->nhe_id >= nffs_hash_next_block_id) {
                    nffs_hash_next_block_id = lastblock_entry->nhe_id + 1;
                }
            }
        }

        if (disk_inode->ndi_parent_id != NFFS_ID_NONE) {
            
            parent = nffs_hash_find_inode(disk_inode->ndi_parent_id);
            /*
             * The parent directory for this inode hasn't been restored yet.
             * Add a dummy directory so it can be added as a child.
             * When the parent inode is restored, it's hash entry will be
             * updated with the flash location.
             */
            if (parent == NULL) {
                rc = nffs_restore_dummy_inode(disk_inode->ndi_parent_id,
                                             &parent);
                /*
                 * Set the dummy parent flag in the new parent.
                 * It's turned off above when restored.
                 */
                nffs_inode_setflags(parent, NFFS_INODE_FLAG_DUMMYPARENT);
                if (rc != 0) {
                    goto err;
                }
            }

            rc = nffs_inode_add_child(parent, inode_entry);
            if (rc != 0) {
                goto err;
            }
        } 

        if (inode_entry->nie_hash_entry.nhe_id == NFFS_ID_ROOT_DIR) {
            nffs_root_dir = inode_entry;
            nffs_inode_setflags(nffs_root_dir, NFFS_INODE_FLAG_INTREE);
        }
    }

    if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
        NFFS_LOG(DEBUG, "restoring file; id=0x%08x\n",
                 (unsigned int)inode_entry->nie_hash_entry.nhe_id);
        if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_file_id) {
            nffs_hash_next_file_id = inode_entry->nie_hash_entry.nhe_id + 1;
        }
    } else {
        NFFS_LOG(DEBUG, "restoring dir; id=0x%08x\n",
                 (unsigned int)inode_entry->nie_hash_entry.nhe_id);
        if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_dir_id) {
            nffs_hash_next_dir_id = inode_entry->nie_hash_entry.nhe_id + 1;
        }
    }

    return 0;

err:
    if (new_inode) {
        assert(nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_INHASH));
        nffs_hash_remove(&inode_entry->nie_hash_entry);
        nffs_inode_entry_free(inode_entry);
    }
    return rc;
}
Ejemplo n.º 7
0
static int
nffs_restore_should_sweep_inode_entry(struct nffs_inode_entry *inode_entry,
                                      int *out_should_sweep)
{
    struct nffs_inode inode;
    int rc;


    /*
     * if this inode was tagged to have a dummy block entry and the
     * flag is still set, that means we didn't find the block and so
     * should remove this inode and all associated blocks.
     */
    if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMYLSTBLK)) {
        *out_should_sweep = 1;
        /*nffs_inode_inc_refcnt(inode_entry);*/
        inode_entry->nie_refcnt = 1;
        assert(inode_entry->nie_refcnt >= 1);
        return 0;
    }

    /*
     * This inode was originally created to hold a block that was restored
     * before the owning inode. If the flag is still set, it means we never
     * restored the inode from disk and so this entry should be deleted.
     */
    if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMYINOBLK)) {
        *out_should_sweep = 2;
        inode_entry->nie_refcnt = 1;
        assert(inode_entry->nie_refcnt >= 1);
        return 0;
    }

    /*
     * Determine if the inode is a dummy. Dummy inodes have a flash
     * location set to LOC_NONE and should have a flag set for the reason.
     * The presence of a dummy inode during the final sweep step indicates
     * file system corruption.  It's assumed that directories have
     * previously migrated all children to /lost+found.
     */
    if (nffs_inode_is_dummy(inode_entry)) {
        *out_should_sweep = 3;
        nffs_inode_inc_refcnt(inode_entry);
        assert(inode_entry->nie_refcnt >= 1);
        return 0;
    }

    /* Determine if the inode has been deleted.  If an inode has no parent (and
     * it isn't the root directory), it has been deleted from the disk and
     * should be swept from the RAM representation.
     */
    if (inode_entry->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) {
        rc = nffs_inode_from_entry(&inode, inode_entry);
        if (rc != 0 && rc != FS_ENOENT) {
            *out_should_sweep = 0;
            return rc;
        }
        if (inode.ni_parent == NULL) {
            *out_should_sweep = 4;
            return 0;
        }
    }

    /*
     * If this inode has been marked as deleted, we can unlink it here.
     *
     * XXX Note that the record of a deletion could be lost if garbage
     * collection erases the delete but leaves inode updates on other
     * partitions which can then be restored.
     */
    if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DELETED)) {
        rc = nffs_inode_from_entry(&inode, inode_entry);
        if (rc != 0) {
            *out_should_sweep = 0;
            return rc;
        }
        *out_should_sweep = 5;
    }

    /* If this is a file inode, verify that all of its constituent blocks are
     * present.
     */
    if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) {
        rc = nffs_restore_validate_block_chain(
                inode_entry->nie_last_block_entry);
        if (rc == FS_ECORRUPT) {
            *out_should_sweep = 6;
            return 0;
        } else if (rc == FS_ENOENT) {
            *out_should_sweep = 7;
            return 0;
        } else if (rc != 0) {
            *out_should_sweep = 0;
            return rc;
        }
    }

    /* This is a valid inode; don't sweep it. */
    *out_should_sweep = 0;
    return 0;
}
Ejemplo n.º 8
0
static void
nffs_log_contents(void)
{
#if MYNEWT_VAL(LOG_LEVEL) > LOG_LEVEL_DEBUG
    return;
#endif

    struct nffs_inode_entry *inode_entry;
    struct nffs_hash_entry *entry;
    struct nffs_hash_entry *next;
    struct nffs_block block;
    struct nffs_inode inode;
    int rc;
    int i;

    NFFS_HASH_FOREACH(entry, i, next) {
        if (nffs_hash_id_is_block(entry->nhe_id)) {
            rc = nffs_block_from_hash_entry(&block, entry);
            assert(rc == 0 || rc == FS_ENOENT);
            NFFS_LOG(DEBUG, "block; id=%u inode_id=",
                     (unsigned int)entry->nhe_id);
            if (block.nb_inode_entry == NULL) {
                NFFS_LOG(DEBUG, "null ");
            } else {
                NFFS_LOG(DEBUG, "%u ",
                     (unsigned int)block.nb_inode_entry->nie_hash_entry.nhe_id);
            }

            NFFS_LOG(DEBUG, "prev_id=");
            if (block.nb_prev == NULL) {
                NFFS_LOG(DEBUG, "null ");
            } else {
                NFFS_LOG(DEBUG, "%u ", (unsigned int)block.nb_prev->nhe_id);
            }

            NFFS_LOG(DEBUG, "data_len=%u\n", block.nb_data_len);
        } else {
            inode_entry = (void *)entry;
            rc = nffs_inode_from_entry(&inode, inode_entry);
            if (rc == FS_ENOENT) {
                NFFS_LOG(DEBUG, "DUMMY file; id=%x ref=%d block_id=",
                         (unsigned int)entry->nhe_id, inode_entry->nie_refcnt);
                if (inode_entry->nie_last_block_entry == NULL) {
                    NFFS_LOG(DEBUG, "null");
                } else {
                    NFFS_LOG(DEBUG, "%x",
                     (unsigned int)inode_entry->nie_last_block_entry->nhe_id);
                }
            } else if (rc != 0) {
                continue;
            }
            /*assert(rc == 0);*/

            if (nffs_hash_id_is_file(entry->nhe_id)) {
                NFFS_LOG(DEBUG, "file; id=%u name=%.3s block_id=",
                         (unsigned int)entry->nhe_id, inode.ni_filename);
                if (inode_entry->nie_last_block_entry == NULL) {
                    NFFS_LOG(DEBUG, "null");
                } else {
                    NFFS_LOG(DEBUG, "%u",
                     (unsigned int)inode_entry->nie_last_block_entry->nhe_id);
                }
                NFFS_LOG(DEBUG, "\n");
            } else {
                inode_entry = (void *)entry;
                NFFS_LOG(DEBUG, "dir; id=%u name=%.3s\n",
                         (unsigned int)entry->nhe_id, inode.ni_filename);

            }
        }
    }
}