Пример #1
0
/**
 * Constructs a full data block representation from the specified minimal block
 * entry.  The resultant block's pointers are populated via hash table lookups.
 *
 * @param out_block             On success, this gets populated with the data
 *                                  block information.
 * @param block_entry           The source block entry to convert.
 *
 * @return                      0 on success; nonzero on failure.
 */
int
nffs_block_from_hash_entry(struct nffs_block *out_block,
                           struct nffs_hash_entry *block_entry)
{
    struct nffs_disk_block disk_block;
    uint32_t area_offset;
    uint8_t area_idx;
    int rc;

    assert(nffs_hash_id_is_block(block_entry->nhe_id));

    nffs_flash_loc_expand(block_entry->nhe_flash_loc, &area_idx, &area_offset);
    rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
    if (rc != 0) {
        return rc;
    }

    out_block->nb_hash_entry = block_entry;
    rc = nffs_block_from_disk(out_block, &disk_block, area_idx, area_offset);
    if (rc != 0) {
        return rc;
    }

    return 0;
}
/**
 * Checks that each block a chain of data blocks was properly restored.
 *
 * @param last_block_entry      The entry corresponding to the last block in
 *                                  the chain.
 *
 * @return                      0 if the block chain is OK;
 *                              FS_ECORRUPT if corruption is detected;
 *                              nonzero on other error.
 */
static int
nffs_restore_validate_block_chain(struct nffs_hash_entry *last_block_entry)
{
    struct nffs_disk_block disk_block;
    struct nffs_hash_entry *cur;
    struct nffs_block block;
    uint32_t area_offset;
    uint8_t area_idx;
    int rc;

    cur = last_block_entry;

    while (cur != NULL) {
        nffs_flash_loc_expand(cur->nhe_flash_loc, &area_idx, &area_offset);

        rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
        if (rc != 0) {
            return rc;
        }

        rc = nffs_block_from_hash_entry(&block, cur);
        if (rc != 0) {
            return rc;
        }

        cur = block.nb_prev;
    }

    return 0;
}
Пример #3
0
/**
 * Constructs a full data block representation from the specified minimal
 * block entry.  However, the resultant block's pointers are set to null,
 * rather than populated via hash table lookups.  This behavior is useful when
 * the RAM representation has not been fully constructed yet.
 *
 * @param out_block             On success, this gets populated with the data
 *                                  block information.
 * @param block_entry           The source block entry to convert.
 *
 * @return                      0 on success; nonzero on failure.
 */
int
nffs_block_from_hash_entry_no_ptrs(struct nffs_block *out_block,
                                   struct nffs_hash_entry *block_entry)
{
    struct nffs_disk_block disk_block;
    uint32_t area_offset;
    uint8_t area_idx;
    int rc;

    assert(nffs_hash_id_is_block(block_entry->nhe_id));

    if (nffs_hash_entry_is_dummy(block_entry)) {
        /*
         * We can't read this from disk so we'll be missing filling in anything
         * not already in inode_entry (e.g., prev_id).
         */
        out_block->nb_hash_entry = block_entry;
        return FS_ENOENT; /* let caller know it's a partial inode_entry */
    }

    nffs_flash_loc_expand(block_entry->nhe_flash_loc, &area_idx, &area_offset);
    rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
    if (rc != 0) {
        return rc;
    }

    out_block->nb_hash_entry = block_entry;
    nffs_block_from_disk_no_ptrs(out_block, &disk_block);

    return 0;
}
Пример #4
0
/**
 * Determines if a particular block can be found in RAM by following a chain of
 * previous block pointers, starting with the specified hash entry.
 *
 * @param start                 The block entry at which to start the search.
 * @param sought_id             The ID of the block to search for.
 *
 * @return                      0 if the sought after ID was found;
 *                              FS_ENOENT if the ID was not found;
 *                              Other FS code on error.
 */
int
nffs_block_find_predecessor(struct nffs_hash_entry *start, uint32_t sought_id)
{
    struct nffs_hash_entry *entry;
    struct nffs_disk_block disk_block;
    uint32_t area_offset;
    uint8_t area_idx;
    int rc;

    entry = start;
    while (entry != NULL && entry->nhe_id != sought_id) {
        nffs_flash_loc_expand(entry->nhe_flash_loc, &area_idx, &area_offset);
        rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
        if (rc != 0) {
            return rc;
        }

        if (disk_block.ndb_prev_id == NFFS_ID_NONE) {
            entry = NULL;
        } else {
            entry = nffs_hash_find(disk_block.ndb_prev_id);
        }
    }

    if (entry == NULL) {
        rc = FS_ENOENT;
    } else {
        rc = 0;
    }

    return rc;
}
Пример #5
0
int
nffs_block_read_data(const struct nffs_block *block, uint16_t offset,
                     uint16_t length, void *dst)
{
    uint32_t area_offset;
    uint8_t area_idx;
    int rc;

    nffs_flash_loc_expand(block->nb_hash_entry->nhe_flash_loc,
                         &area_idx, &area_offset);
    area_offset += sizeof (struct nffs_disk_block);
    area_offset += offset;

    rc = nffs_flash_read(area_idx, area_offset, dst, length);
    if (rc != 0) {
        return rc;
    }

    return 0;
}
Пример #6
0
/**
 * Constructs a block representation from a minimal block hash entry.  If the
 * hash entry references other objects (inode or previous data block), the
 * resulting block object is populated with pointers to the referenced objects.
 * If the any referenced objects are not present in the NFFS RAM
 * representation, this indicates file system corruption.  In this case, the
 * resulting block is populated with all valid references, and an FS_ECORRUPT
 * code is returned.
 *
 * @param out_block             On success, this gets populated with the data
 *                                  block information.
 * @param block_entry           The source block entry to convert.
 *
 * @return                      0 on success;
 *                              FS_ECORRUPT if one or more pointers could not
 *                                  be filled in due to file system corruption;
 *                              FS_EOFFSET on an attempt to read an invalid
 *                                  address range;
 *                              FS_EHW on flash error;
 *                              FS_EUNEXP if the specified disk location does
 *                                  not contain a block.
 */
int
nffs_block_from_hash_entry(struct nffs_block *out_block,
                           struct nffs_hash_entry *block_entry)
{
    struct nffs_disk_block disk_block;
    uint32_t area_offset;
    uint8_t area_idx;
    int rc;

    assert(nffs_hash_id_is_block(block_entry->nhe_id));

    if (nffs_block_is_dummy(block_entry)) {
        out_block->nb_hash_entry = block_entry;
        out_block->nb_inode_entry = NULL;
        out_block->nb_prev = NULL;
        /*
         * Dummy block added when inode was read in before real block
         * (see nffs_restore_inode()). Return success (because there's 
         * too many places that ned to check for this,
         * but it's the responsibility fo the upstream code to check
         * whether this is still a dummy entry.  XXX
         */
        return 0;
        /*return FS_ENOENT;*/
    }
    nffs_flash_loc_expand(block_entry->nhe_flash_loc, &area_idx, &area_offset);
    rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
    if (rc != 0) {
        return rc;
    }

    out_block->nb_hash_entry = block_entry;
    rc = nffs_block_from_disk(out_block, &disk_block);
    if (rc != 0) {
        return rc;
    }

    return 0;
}
Пример #7
0
static int
nffs_gc_copy_object(struct nffs_hash_entry *entry, uint16_t object_size,
                    uint8_t to_area_idx)
{
    uint32_t from_area_offset;
    uint32_t to_area_offset;
    uint8_t from_area_idx;
    int rc;

    nffs_flash_loc_expand(entry->nhe_flash_loc,
                          &from_area_idx, &from_area_offset);
    to_area_offset = nffs_areas[to_area_idx].na_cur;

    rc = nffs_flash_copy(from_area_idx, from_area_offset, to_area_idx,
                         to_area_offset, object_size);
    if (rc != 0) {
        return rc;
    }

    entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset);

    return 0;
}
Пример #8
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;
}
Пример #9
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;
}
Пример #10
0
/**
 * Moves a chain of blocks from one area to another.  This function attempts to
 * collate the blocks into a single new block in the destination area.
 *
 * @param last_entry            The last block entry in the chain.
 * @param data_len              The total length of data to collate.
 * @param to_area_idx           The index of the area to copy to.
 * @param inout_next            This parameter is only necessary if you are
 *                                  calling this function during an iteration
 *                                  of the entire hash table; pass null
 *                                  otherwise.
 *                              On input, this points to the next hash entry
 *                                  you plan on processing.
 *                              On output, this points to the next hash entry
 *                                  that should be processed.
 *
 * @return                      0 on success;
 *                              FS_ENOMEM if there is insufficient heap;
 *                              other nonzero on failure.
 */
static int
nffs_gc_block_chain_collate(struct nffs_hash_entry *last_entry,
                            uint32_t data_len, uint8_t to_area_idx,
                            struct nffs_hash_entry **inout_next)
{
    struct nffs_disk_block disk_block;
    struct nffs_hash_entry *entry;
    struct nffs_area *to_area;
    struct nffs_block last_block;
    struct nffs_block block;
    uint32_t to_area_offset;
    uint32_t from_area_offset;
    uint32_t data_offset;
    uint8_t *data;
    uint8_t from_area_idx;
    int rc;

    memset(&last_block, 0, sizeof last_block);

    data = malloc(data_len);
    if (data == NULL) {
        rc = FS_ENOMEM;
        goto done;
    }

    memset(&last_block, 0, sizeof(last_block));

    to_area = nffs_areas + to_area_idx;

    entry = last_entry;
    data_offset = data_len;
    while (data_offset > 0) {
        rc = nffs_block_from_hash_entry(&block, entry);
        if (rc != 0) {
            goto done;
        }
        data_offset -= block.nb_data_len;

        nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc,
                              &from_area_idx, &from_area_offset);
        from_area_offset += sizeof disk_block;
        STATS_INC(nffs_stats, nffs_readcnt_gccollate);
        rc = nffs_flash_read(from_area_idx, from_area_offset,
                             data + data_offset, block.nb_data_len);
        if (rc != 0) {
            goto done;
        }

        if (entry != last_entry) {
            if (inout_next != NULL && *inout_next == entry) {
                *inout_next = SLIST_NEXT(entry, nhe_next);
            }
            nffs_block_delete_from_ram(entry);
        } else {
            last_block = block;
        }
        entry = block.nb_prev;
    }

    /* we had better have found the last block */
    assert(last_block.nb_hash_entry);

    /* The resulting block should inherit its ID from its last constituent
     * block (this is the ID referenced by the parent inode and subsequent data
     * block).  The previous ID gets inherited from the first constituent
     * block.
     */
    memset(&disk_block, 0, sizeof disk_block);
    disk_block.ndb_id = last_block.nb_hash_entry->nhe_id;
    disk_block.ndb_seq = last_block.nb_seq + 1;
    disk_block.ndb_inode_id = last_block.nb_inode_entry->nie_hash_entry.nhe_id;
    if (entry == NULL) {
        disk_block.ndb_prev_id = NFFS_ID_NONE;
    } else {
        disk_block.ndb_prev_id = entry->nhe_id;
    }
    disk_block.ndb_data_len = data_len;
    nffs_crc_disk_block_fill(&disk_block, data);

    to_area_offset = to_area->na_cur;
    rc = nffs_flash_write(to_area_idx, to_area_offset,
                          &disk_block, sizeof disk_block);
    if (rc != 0) {
        goto done;
    }

    rc = nffs_flash_write(to_area_idx, to_area_offset + sizeof disk_block,
                          data, data_len);
    if (rc != 0) {
        goto done;
    }

    last_entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset);

    rc = 0;

    ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, to_area_idx,
                                                to_area_offset) == 0);

done:
    free(data);
    return rc;
}
Пример #11
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;
}
Пример #12
0
/**
 * Moves a chain of blocks from one area to another.  This function attempts to
 * collate the blocks into a single new block in the destination area.
 *
 * @param last_entry            The last block entry in the chain.
 * @param data_len              The total length of data to collate.
 * @param to_area_idx           The index of the area to copy to.
 * @param inout_next            This parameter is only necessary if you are
 *                                  calling this function during an iteration
 *                                  of the entire hash table; pass null
 *                                  otherwise.
 *                              On input, this points to the next hash entry
 *                                  you plan on processing.
 *                              On output, this points to the next hash entry
 *                                  that should be processed.
 *
 * @return                      0 on success;
 *                              FS_ENOMEM if there is insufficient heap;
 *                              other nonzero on failure.
 */
static int
nffs_gc_block_chain_collate(struct nffs_hash_entry *last_entry,
                            uint32_t data_len, uint8_t to_area_idx,
                            struct nffs_hash_entry **inout_next)
{
    struct nffs_disk_block disk_block;
    struct nffs_hash_entry *entry;
    struct nffs_area *to_area;
    struct nffs_block block;
    uint32_t to_area_offset;
    uint32_t from_area_offset;
    uint32_t data_offset;
    uint8_t *data;
    uint8_t from_area_idx;
    int rc;

    data = malloc(data_len);
    if (data == NULL) {
        rc = FS_ENOMEM;
        goto done;
    }

    to_area = nffs_areas + to_area_idx;

    entry = last_entry;
    data_offset = data_len;
    while (data_offset > 0) {
        rc = nffs_block_from_hash_entry(&block, entry);
        if (rc != 0) {
            goto done;
        }
        data_offset -= block.nb_data_len;

        nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc,
                              &from_area_idx, &from_area_offset);
        from_area_offset += sizeof disk_block;
        rc = nffs_flash_read(from_area_idx, from_area_offset,
                             data + data_offset, block.nb_data_len);
        if (rc != 0) {
            goto done;
        }

        if (entry != last_entry) {
            if (inout_next != NULL && *inout_next == entry) {
                *inout_next = SLIST_NEXT(entry, nhe_next);
            }
            nffs_block_delete_from_ram(entry);
        }
        entry = block.nb_prev;
    }

    memset(&disk_block, 0, sizeof disk_block);
    disk_block.ndb_magic = NFFS_BLOCK_MAGIC;
    disk_block.ndb_id = block.nb_hash_entry->nhe_id;
    disk_block.ndb_seq = block.nb_seq + 1;
    disk_block.ndb_inode_id = block.nb_inode_entry->nie_hash_entry.nhe_id;
    if (entry == NULL) {
        disk_block.ndb_prev_id = NFFS_ID_NONE;
    } else {
        disk_block.ndb_prev_id = entry->nhe_id;
    }
    disk_block.ndb_data_len = data_len;
    nffs_crc_disk_block_fill(&disk_block, data);

    to_area_offset = to_area->na_cur;
    rc = nffs_flash_write(to_area_idx, to_area_offset,
                         &disk_block, sizeof disk_block);
    if (rc != 0) {
        goto done;
    }

    rc = nffs_flash_write(to_area_idx, to_area_offset + sizeof disk_block,
                          data, data_len);
    if (rc != 0) {
        goto done;
    }

    last_entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset);

    rc = 0;

    ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, to_area_idx,
                                                to_area_offset) == 0);

done:
    free(data);
    return rc;
}
Пример #13
0
/**
 * Overwrites an existing data block.  The resulting block has the same ID as
 * the old one, but it supersedes it with a greater sequence number.
 *
 * @param entry                 The data block to overwrite.
 * @param left_copy_len         The number of bytes of existing data to retain
 *                                  before the new data begins.
 * @param new_data              The new data to write to the block.
 * @param new_data_len          The number of new bytes to write to the block.
 *                                  If this value plus left_copy_len is less
 *                                  than the existing block's data length,
 *                                  previous data at the end of the block is
 *                                  retained.
 *
 * @return                      0 on success; nonzero on failure.
 */
static int
nffs_write_over_block(struct nffs_hash_entry *entry, uint16_t left_copy_len,
                      const void *new_data, uint16_t new_data_len)
{
    struct nffs_disk_block disk_block;
    struct nffs_block block;
    uint32_t src_area_offset;
    uint32_t dst_area_offset;
    uint16_t right_copy_len;
    uint16_t block_off;
    uint8_t src_area_idx;
    uint8_t dst_area_idx;
    int rc;

    rc = nffs_block_from_hash_entry(&block, entry);
    if (rc != 0) {
        return rc;
    }

    assert(left_copy_len <= block.nb_data_len);

    /* Determine how much old data at the end of the block needs to be
     * retained.  If the new data doesn't extend to the end of the block, the
     * the rest of the block retains its old contents.
     */
    if (left_copy_len + new_data_len > block.nb_data_len) {
        right_copy_len = 0;
    } else {
        right_copy_len = block.nb_data_len - left_copy_len - new_data_len;
    }

    block.nb_seq++;
    block.nb_data_len = left_copy_len + new_data_len + right_copy_len;
    nffs_block_to_disk(&block, &disk_block);

    nffs_flash_loc_expand(entry->nhe_flash_loc,
                          &src_area_idx, &src_area_offset);

    rc = nffs_write_fill_crc16_overwrite(&disk_block,
                                         src_area_idx, src_area_offset,
                                         left_copy_len, right_copy_len,
                                         new_data, new_data_len);
    if (rc != 0) {
        return rc;
    }

    rc = nffs_misc_reserve_space(sizeof disk_block + disk_block.ndb_data_len,
                                 &dst_area_idx, &dst_area_offset);
    if (rc != 0) {
        return rc;
    }

    block_off = 0;

    /* Write the block header. */
    rc = nffs_flash_write(dst_area_idx, dst_area_offset + block_off,
                          &disk_block, sizeof disk_block);
    if (rc != 0) {
        return rc;
    }
    block_off += sizeof disk_block;

    /* Copy data from the start of the old block, in case the new data starts
     * at a non-zero offset.
     */
    if (left_copy_len > 0) {
        rc = nffs_flash_copy(src_area_idx, src_area_offset + block_off,
                             dst_area_idx, dst_area_offset + block_off,
                             left_copy_len);
        if (rc != 0) {
            return rc;
        }
        block_off += left_copy_len;
    }

    /* Write the new data into the data block.  This may extend the block's
     * length beyond its old value.
     */
    rc = nffs_flash_write(dst_area_idx, dst_area_offset + block_off,
                          new_data, new_data_len);
    if (rc != 0) {
        return rc;
    }
    block_off += new_data_len;

    /* Copy data from the end of the old block, in case the new data doesn't
     * extend to the end of the block.
     */
    if (right_copy_len > 0) {
        rc = nffs_flash_copy(src_area_idx, src_area_offset + block_off,
                             dst_area_idx, dst_area_offset + block_off,
                             right_copy_len);
        if (rc != 0) {
            return rc;
        }
        block_off += right_copy_len;
    }

    assert(block_off == sizeof disk_block + block.nb_data_len);

    entry->nhe_flash_loc = nffs_flash_loc(dst_area_idx, dst_area_offset);

    ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, dst_area_idx,
                                                dst_area_offset) == 0);

    return 0;
}
/**
 * Repairs the effects of a corrupt scratch area.  Scratch area corruption can
 * occur when the system resets while a garbage collection cycle is in
 * progress.
 *
 * @return                      0 on success; nonzero on failure.
 */
static int
nffs_restore_corrupt_scratch(void)
{
    struct nffs_inode_entry *inode_entry;
    struct nffs_hash_entry *entry;
    struct nffs_hash_entry *next;
    uint32_t area_offset;
    uint16_t good_idx;
    uint16_t bad_idx;
    uint8_t area_idx;
    int rc;
    int i;

    /* Search for a pair of areas with identical IDs.  If found, these areas
     * represent the source and destination areas of a garbage collection
     * cycle.  The shorter of the two areas was the destination area.  Since
     * the garbage collection cycle did not finish, the source area contains a
     * more complete set of objects than the destination area.
     *
     * good_idx = index of source area.
     * bad_idx  = index of destination area; this will be turned into the
     *            scratch area.
     */
    rc = nffs_area_find_corrupt_scratch(&good_idx, &bad_idx);
    if (rc != 0) {
        return rc;
    }

    /* Invalidate all objects resident in the bad area. */
    for (i = 0; i < NFFS_HASH_SIZE; i++) {
        entry = SLIST_FIRST(&nffs_hash[i]);
        while (entry != NULL) {
            next = SLIST_NEXT(entry, nhe_next);

            nffs_flash_loc_expand(entry->nhe_flash_loc,
                                 &area_idx, &area_offset);
            if (area_idx == bad_idx) {
                if (nffs_hash_id_is_block(entry->nhe_id)) {
                    rc = nffs_block_delete_from_ram(entry);
                    if (rc != 0) {
                        return rc;
                    }
                } else {
                    inode_entry = (struct nffs_inode_entry *)entry;
                    inode_entry->nie_refcnt = 0;
                }
            }

            entry = next;
        }
    }

    /* Now that the objects in the scratch area have been invalidated, reload
     * everything from the good area.
     */
    rc = nffs_restore_area_contents(good_idx);
    if (rc != 0) {
        return rc;
    }

    /* Convert the bad area into a scratch area. */
    rc = nffs_format_area(bad_idx, 1);
    if (rc != 0) {
        return rc;
    }
    nffs_scratch_area_idx = bad_idx;

    return 0;
}