Пример #1
0
int
nffs_crc_flash(uint16_t initial_crc, uint8_t area_idx, uint32_t area_offset,
               uint32_t len, uint16_t *out_crc)
{
    uint32_t chunk_len;
    uint16_t crc;
    int rc;

    crc = initial_crc;

    /* Copy data in chunks small enough to fit in the flash buffer. */
    while (len > 0) {
        if (len > sizeof nffs_flash_buf) {
            chunk_len = sizeof nffs_flash_buf;
        } else {
            chunk_len = len;
        }

        rc = nffs_flash_read(area_idx, area_offset, nffs_flash_buf, chunk_len);
        if (rc != 0) {
            return rc;
        }

        crc = crc16_ccitt(crc, nffs_flash_buf, chunk_len);

        area_offset += chunk_len;
        len -= chunk_len;
    }

    *out_crc = crc;
    return 0;
}
/**
 * Turns a scratch area into a non-scratch area.  If the specified area is not
 * actually a scratch area, this function falls back to a slower full format
 * operation.
 */
int
nffs_format_from_scratch_area(uint8_t area_idx, uint8_t area_id)
{
    struct nffs_disk_area disk_area;
    int rc;

    assert(area_idx < nffs_num_areas);
    rc = nffs_flash_read(area_idx, 0, &disk_area, sizeof disk_area);
    if (rc != 0) {
        return rc;
    }

    nffs_areas[area_idx].na_id = area_id;
    if (!nffs_area_is_scratch(&disk_area)) {
        rc = nffs_format_area(area_idx, 0);
        if (rc != 0) {
            return rc;
        }
    } else {
        disk_area.nda_id = area_id;
        rc = nffs_flash_write(area_idx, NFFS_AREA_OFFSET_ID,
                             &disk_area.nda_id, sizeof disk_area.nda_id);
        if (rc != 0) {
            return rc;
        }
    }

    return 0;
}
/**
 * Copies a chunk of data from one region of flash to another.
 *
 * @param area_idx_from         The index of the area to copy from.
 * @param area_offset_from      The offset within the area to copy from.
 * @param area_idx_to           The index of the area to copy to.
 * @param area_offset_to        The offset within the area to copy to.
 * @param len                   The number of bytes to copy.
 *
 * @return                      0 on success; nonzero on failure.
 */
int
nffs_flash_copy(uint8_t area_idx_from, uint32_t area_offset_from,
                uint8_t area_idx_to, uint32_t area_offset_to,
                uint32_t len)
{
    uint32_t chunk_len;
    int rc;

    /* Copy data in chunks small enough to fit in the flash buffer. */
    while (len > 0) {
        if (len > sizeof nffs_flash_buf) {
            chunk_len = sizeof nffs_flash_buf;
        } else {
            chunk_len = len;
        }

        rc = nffs_flash_read(area_idx_from, area_offset_from, nffs_flash_buf,
                             chunk_len);
        if (rc != 0) {
            return rc;
        }

        rc = nffs_flash_write(area_idx_to, area_offset_to, nffs_flash_buf,
                              chunk_len);
        if (rc != 0) {
            return rc;
        }

        area_offset_from += chunk_len;
        area_offset_to += chunk_len;
        len -= chunk_len;
    }

    return 0;
}
Пример #4
0
/**
 * Reads a single disk object from flash.
 *
 * @param area_idx              The area to read the object from.
 * @param area_offset           The offset within the area to read from.
 * @param out_disk_object       On success, the restored object gets written
 *                                  here.
 *
 * @return                      0 on success; nonzero on failure.
 */
static int
nffs_restore_disk_object(int area_idx, uint32_t area_offset,
                         struct nffs_disk_object *out_disk_object)
{
    int rc;

    rc = nffs_flash_read(area_idx, area_offset,
                         &out_disk_object->ndo_un_obj,
                         sizeof(out_disk_object->ndo_un_obj));
    if (rc != 0) {
        return rc;
    }
    STATS_INC(nffs_stats, nffs_readcnt_object);

    if (nffs_hash_id_is_inode(out_disk_object->ndo_disk_inode.ndi_id)) {
        out_disk_object->ndo_type = NFFS_OBJECT_TYPE_INODE;

    } else if (nffs_hash_id_is_block(out_disk_object->ndo_disk_block.ndb_id)) {
        out_disk_object->ndo_type = NFFS_OBJECT_TYPE_BLOCK;

    } else if (out_disk_object->ndo_disk_block.ndb_id == NFFS_ID_NONE) {
        return FS_EEMPTY;

    } else {
        return FS_ECORRUPT;
    }

    out_disk_object->ndo_area_idx = area_idx;
    out_disk_object->ndo_offset = area_offset;

    return 0;
}
Пример #5
0
/**
 * Reads a data block header from flash.
 *
 * @param area_idx              The index of the area to read from.
 * @param area_offset           The offset within the area to read from.
 * @param out_disk_block        On success, the block header is writteh here.
 *
 * @return                      0 on success; nonzero on failure.
 */
int
nffs_block_read_disk(uint8_t area_idx, uint32_t area_offset,
                     struct nffs_disk_block *out_disk_block)
{
    int rc;

    rc = nffs_flash_read(area_idx, area_offset, out_disk_block,
                        sizeof *out_disk_block);
    if (rc != 0) {
        return rc;
    }
    if (out_disk_block->ndb_magic != NFFS_BLOCK_MAGIC) {
        return FS_EUNEXP;
    }

    return 0;
}
Пример #6
0
/**
 * Reads a data block header from flash.
 *
 * @param area_idx              The index of the area to read from.
 * @param area_offset           The offset within the area to read from.
 * @param out_disk_block        On success, the block header is writteh here.
 *
 * @return                      0 on success;
 *                              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_read_disk(uint8_t area_idx, uint32_t area_offset,
                     struct nffs_disk_block *out_disk_block)
{
    int rc;

    rc = nffs_flash_read(area_idx, area_offset, out_disk_block,
                         sizeof *out_disk_block);
    if (rc != 0) {
        return rc;
    }
    if (!nffs_hash_id_is_block(out_disk_block->ndb_id)) {
        return FS_EUNEXP;
    }

    return 0;
}
/**
 * Reads a single disk object from flash.
 *
 * @param area_idx              The area to read the object from.
 * @param area_offset           The offset within the area to read from.
 * @param out_disk_object       On success, the restored object gets written
 *                                  here.
 *
 * @return                      0 on success; nonzero on failure.
 */
static int
nffs_restore_disk_object(int area_idx, uint32_t area_offset,
                         struct nffs_disk_object *out_disk_object)
{
    uint32_t magic;
    int rc;

    rc = nffs_flash_read(area_idx, area_offset, &magic, sizeof magic);
    if (rc != 0) {
        return rc;
    }

    switch (magic) {
    case NFFS_INODE_MAGIC:
        out_disk_object->ndo_type = NFFS_OBJECT_TYPE_INODE;
        rc = nffs_inode_read_disk(area_idx, area_offset,
                                 &out_disk_object->ndo_disk_inode);
        break;

    case NFFS_BLOCK_MAGIC:
        out_disk_object->ndo_type = NFFS_OBJECT_TYPE_BLOCK;
        rc = nffs_block_read_disk(area_idx, area_offset,
                                 &out_disk_object->ndo_disk_block);
        break;

    case 0xffffffff:
        rc = FS_EEMPTY;
        break;

    default:
        rc = FS_ECORRUPT;
        break;
    }

    if (rc != 0) {
        return rc;
    }

    out_disk_object->ndo_area_idx = area_idx;
    out_disk_object->ndo_offset = area_offset;

    return 0;
}
Пример #8
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;
}
Пример #9
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;
}
Пример #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 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;
}