blob: d3002096e5c3da689dc85b979b75429272a7e728 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <string.h>
#include "hal/hal_flash.h"
#include "nffs_priv.h"
#include "nffs/nffs.h"
/**
* 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);
STATS_INC(nffs_stats, nffs_readcnt_format);
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;
}
/**
* Formats a single scratch area.
*/
int
nffs_format_area(uint8_t area_idx, int is_scratch)
{
struct nffs_disk_area disk_area;
struct nffs_area *area;
uint32_t write_len;
int rc;
area = nffs_areas + area_idx;
rc = hal_flash_erase(area->na_flash_id, area->na_offset, area->na_length);
if (rc != 0) {
return FS_EHW;
}
area->na_cur = 0;
nffs_area_to_disk(area, &disk_area);
if (is_scratch) {
nffs_areas[area_idx].na_id = NFFS_AREA_ID_NONE;
write_len = sizeof disk_area - sizeof disk_area.nda_id;
} else {
write_len = sizeof disk_area;
}
rc = nffs_flash_write(area_idx, 0, &disk_area.nda_magic, write_len);
if (rc != 0) {
return rc;
}
return 0;
}
/**
* 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;
}
nffs_current_area_descs = (struct nffs_area_desc*) area_descs;
return 0;
err:
nffs_misc_reset();
return rc;
}