/**
 * Erases all the specified areas and initializes them with a clean nffs
 * file system.
 *
 * @param area_descs        The set of areas to format.
 *
 * @return                  0 on success;
 *                          nonzero on failure.
 */
int
nffs_format_full(const struct nffs_area_desc *area_descs)
{
    int rc;
    int i;

    /* Start from a clean state. */
    nffs_misc_reset();

    /* Select largest area to be the initial scratch area. */
    nffs_scratch_area_idx = 0;
    for (i = 1; area_descs[i].nad_length != 0; i++) {
        if (i >= NFFS_MAX_AREAS) {
            rc = FS_EINVAL;
            goto err;
        }

        if (area_descs[i].nad_length >
            area_descs[nffs_scratch_area_idx].nad_length) {

            nffs_scratch_area_idx = i;
        }
    }

    rc = nffs_misc_set_num_areas(i);
    if (rc != 0) {
        goto err;
    }

    for (i = 0; i < nffs_num_areas; i++) {
        nffs_areas[i].na_offset = area_descs[i].nad_offset;
        nffs_areas[i].na_length = area_descs[i].nad_length;
        nffs_areas[i].na_flash_id = area_descs[i].nad_flash_id;
        nffs_areas[i].na_cur = 0;
        nffs_areas[i].na_gc_seq = 0;

        if (i == nffs_scratch_area_idx) {
            nffs_areas[i].na_id = NFFS_AREA_ID_NONE;
        } else {
            nffs_areas[i].na_id = i;
        }

        rc = nffs_format_area(i, i == nffs_scratch_area_idx);
        if (rc != 0) {
            goto err;
        }
    }

    rc = nffs_misc_validate_scratch();
    if (rc != 0) {
        goto err;
    }

    /* Create root directory. */
    rc = nffs_file_new(NULL, "", 0, 1, &nffs_root_dir);
    if (rc != 0) {
        goto err;
    }

    /* Create "lost+found" directory. */
    rc = nffs_misc_create_lost_found_dir();
    if (rc != 0) {
        goto err;
    }

    rc = nffs_misc_validate_root_dir();
    if (rc != 0) {
        goto err;
    }

    rc = nffs_misc_set_max_block_data_len(0);
    if (rc != 0) {
        goto err;
    }

    return 0;

err:
    nffs_misc_reset();
    return rc;
}
/**
 * Searches for a valid nffs file system among the specified areas.  This
 * function succeeds if a file system is detected among any subset of the
 * supplied areas.  If the area set does not contain a valid file system,
 * a new one can be created via a call to nffs_format().
 *
 * @param area_descs        The area set to search.  This array must be
 *                              terminated with a 0-length area.
 *
 * @return                  0 on success;
 *                          FS_ECORRUPT if no valid file system was detected;
 *                          other nonzero on error.
 */
int
nffs_restore_full(const struct nffs_area_desc *area_descs)
{
    struct nffs_disk_area disk_area;
    int cur_area_idx;
    int use_area;
    int rc;
    int i;

    /* Start from a clean state. */
    rc = nffs_misc_reset();
    if (rc) {
        return rc;
    }
    nffs_restore_largest_block_data_len = 0;

    /* Read each area from flash. */
    for (i = 0; area_descs[i].nad_length != 0; i++) {
        if (i > NFFS_MAX_AREAS) {
            rc = FS_EINVAL;
            goto err;
        }

        rc = nffs_restore_detect_one_area(area_descs[i].nad_flash_id,
                                          area_descs[i].nad_offset,
                                          &disk_area);
        switch (rc) {
        case 0:
            use_area = 1;
            break;

        case FS_ECORRUPT:
            use_area = 0;
            break;

        default:
            goto err;
        }

        if (use_area) {
            if (disk_area.nda_id == NFFS_AREA_ID_NONE &&
                nffs_scratch_area_idx != NFFS_AREA_ID_NONE) {

                /* Don't allow more than one scratch area. */
                use_area = 0;
            }
        }

        if (use_area) {
            /* Populate RAM with a representation of this area. */
            cur_area_idx = nffs_num_areas;

            rc = nffs_misc_set_num_areas(nffs_num_areas + 1);
            if (rc != 0) {
                goto err;
            }

            nffs_areas[cur_area_idx].na_offset = area_descs[i].nad_offset;
            nffs_areas[cur_area_idx].na_length = area_descs[i].nad_length;
            nffs_areas[cur_area_idx].na_flash_id = area_descs[i].nad_flash_id;
            nffs_areas[cur_area_idx].na_gc_seq = disk_area.nda_gc_seq;
            nffs_areas[cur_area_idx].na_id = disk_area.nda_id;

            if (disk_area.nda_id == NFFS_AREA_ID_NONE) {
                nffs_areas[cur_area_idx].na_cur = NFFS_AREA_OFFSET_ID;
                nffs_scratch_area_idx = cur_area_idx;
            } else {
                nffs_areas[cur_area_idx].na_cur =
                    sizeof (struct nffs_disk_area);
                nffs_restore_area_contents(cur_area_idx);
            }
        }
    }

    /* All areas have been restored from flash. */

    if (nffs_scratch_area_idx == NFFS_AREA_ID_NONE) {
        /* No scratch area.  The system may have been rebooted in the middle of
         * a garbage collection cycle.  Look for a candidate scratch area.
         */
        rc = nffs_restore_corrupt_scratch();
        if (rc != 0) {
            if (rc == FS_ENOENT) {
                rc = FS_ECORRUPT;
            }
            goto err;
        }
    }

    /* Ensure this file system contains a valid scratch area. */
    rc = nffs_misc_validate_scratch();
    if (rc != 0) {
        goto err;
    }

    /* Make sure the file system contains a valid root directory. */
    rc = nffs_misc_validate_root_dir();
    if (rc != 0) {
        goto err;
    }

    /* Ensure there is a "/lost+found" directory. */
    rc = nffs_misc_create_lost_found_dir();
    if (rc != 0) {
        goto err;
    }

    /* Delete from RAM any objects that were invalidated when subsequent areas
     * were restored.
     */
    nffs_restore_sweep();

    /* Set the maximum data block size according to the size of the smallest
     * area.
     */
    rc = nffs_misc_set_max_block_data_len(nffs_restore_largest_block_data_len);
    if (rc != 0) {
        goto err;
    }

    return 0;

err:
    nffs_misc_reset();
    return rc;
}