zephyr/subsys/fs/fcb/fcb.c
Andrzej Puzdrowski 1c777dd52e fs/fcb: improve error handling in fcb_offset_last_n
Fix for not served error code in fcb_offset_last_n()
implementation.
Issue was reported by static code analyze scan.
Looks like the function might have failed silently
if aggressive access to the fcb instance
from another thread have been occurring while the function
have been processing the fcb storage.

fixes #20512

Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
2019-11-14 19:33:36 -05:00

343 lines
5.9 KiB
C

/*
* Copyright (c) 2017 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <limits.h>
#include <stdlib.h>
#include <fs/fcb.h>
#include "fcb_priv.h"
#include "string.h"
#include <errno.h>
u8_t
fcb_get_align(const struct fcb *fcb)
{
u8_t align;
if (fcb->fap == NULL) {
return 0;
}
align = flash_area_align(fcb->fap);
return align;
}
int fcb_flash_read(const struct fcb *fcb, const struct flash_sector *sector,
off_t off, void *dst, size_t len)
{
int rc;
if (off + len > sector->fs_size) {
return -EINVAL;
}
if (fcb->fap == NULL) {
return -EIO;
}
rc = flash_area_read(fcb->fap, sector->fs_off + off, dst, len);
if (rc != 0) {
return -EIO;
}
return 0;
}
int fcb_flash_write(const struct fcb *fcb, const struct flash_sector *sector,
off_t off, const void *src, size_t len)
{
int rc;
if (off + len > sector->fs_size) {
return -EINVAL;
}
if (fcb->fap == NULL) {
return -EIO;
}
rc = flash_area_write(fcb->fap, sector->fs_off + off, src, len);
if (rc != 0) {
return -EIO;
}
return 0;
}
int
fcb_erase_sector(const struct fcb *fcb, const struct flash_sector *sector)
{
int rc;
if (fcb->fap == NULL) {
return -EIO;
}
rc = flash_area_erase(fcb->fap, sector->fs_off, sector->fs_size);
if (rc != 0) {
return -EIO;
}
return 0;
}
int
fcb_init(int f_area_id, struct fcb *fcb)
{
struct flash_sector *sector;
int rc;
int i;
u8_t align;
int oldest = -1, newest = -1;
struct flash_sector *oldest_sector = NULL, *newest_sector = NULL;
struct fcb_disk_area fda;
if (!fcb->f_sectors || fcb->f_sector_cnt - fcb->f_scratch_cnt < 1) {
return -EINVAL;
}
rc = flash_area_open(f_area_id, &fcb->fap);
if (rc != 0) {
return -EINVAL;
}
align = fcb_get_align(fcb);
if (align == 0U) {
return -EINVAL;
}
/* Fill last used, first used */
for (i = 0; i < fcb->f_sector_cnt; i++) {
sector = &fcb->f_sectors[i];
rc = fcb_sector_hdr_read(fcb, sector, &fda);
if (rc < 0) {
return rc;
}
if (rc == 0) {
continue;
}
if (oldest < 0) {
oldest = newest = fda.fd_id;
oldest_sector = newest_sector = sector;
continue;
}
if (FCB_ID_GT(fda.fd_id, newest)) {
newest = fda.fd_id;
newest_sector = sector;
} else if (FCB_ID_GT(oldest, fda.fd_id)) {
oldest = fda.fd_id;
oldest_sector = sector;
}
}
if (oldest < 0) {
/*
* No initialized areas.
*/
oldest_sector = newest_sector = &fcb->f_sectors[0];
rc = fcb_sector_hdr_init(fcb, oldest_sector, 0);
if (rc) {
return rc;
}
newest = oldest = 0;
}
fcb->f_align = align;
fcb->f_oldest = oldest_sector;
fcb->f_active.fe_sector = newest_sector;
fcb->f_active.fe_elem_off = sizeof(struct fcb_disk_area);
fcb->f_active_id = newest;
while (1) {
rc = fcb_getnext_in_sector(fcb, &fcb->f_active);
if (rc == -ENOTSUP) {
rc = 0;
break;
}
if (rc != 0) {
break;
}
}
k_mutex_init(&fcb->f_mtx);
return rc;
}
int
fcb_free_sector_cnt(struct fcb *fcb)
{
int i;
struct flash_sector *fa;
fa = fcb->f_active.fe_sector;
for (i = 0; i < fcb->f_sector_cnt; i++) {
fa = fcb_getnext_sector(fcb, fa);
if (fa == fcb->f_oldest) {
break;
}
}
return i;
}
int
fcb_is_empty(struct fcb *fcb)
{
return (fcb->f_active.fe_sector == fcb->f_oldest &&
fcb->f_active.fe_elem_off == sizeof(struct fcb_disk_area));
}
/**
* Length of an element is encoded in 1 or 2 bytes.
* 1 byte for lengths < 128 bytes, and 2 bytes for < 16384.
*/
int
fcb_put_len(u8_t *buf, u16_t len)
{
if (len < 0x80) {
buf[0] = len;
return 1;
} else if (len < FCB_MAX_LEN) {
buf[0] = (len & 0x7f) | 0x80;
buf[1] = len >> 7;
return 2;
} else {
return -EINVAL;
}
}
int
fcb_get_len(u8_t *buf, u16_t *len)
{
int rc;
if (buf[0] & 0x80) {
if (buf[0] == 0xff && buf[1] == 0xff) {
return -ENOTSUP;
}
*len = (buf[0] & 0x7f) | (buf[1] << 7);
rc = 2;
} else {
*len = buf[0];
rc = 1;
}
return rc;
}
/**
* Initialize erased sector for use.
*/
int
fcb_sector_hdr_init(struct fcb *fcb, struct flash_sector *sector, u16_t id)
{
struct fcb_disk_area fda;
int rc;
fda.fd_magic = fcb->f_magic;
fda.fd_ver = fcb->f_version;
fda._pad = 0xff;
fda.fd_id = id;
rc = fcb_flash_write(fcb, sector, 0, &fda, sizeof(fda));
if (rc != 0) {
return -EIO;
}
return 0;
}
/**
* Checks whether FCB sector contains data or not.
* Returns <0 in error.
* Returns 0 if sector is unused;
* Returns 1 if sector has data.
*/
int fcb_sector_hdr_read(struct fcb *fcb, struct flash_sector *sector,
struct fcb_disk_area *fdap)
{
struct fcb_disk_area fda;
int rc;
if (!fdap) {
fdap = &fda;
}
rc = fcb_flash_read(fcb, sector, 0, fdap, sizeof(*fdap));
if (rc) {
return -EIO;
}
if (fdap->fd_magic == 0xffffffff) {
return 0;
}
if (fdap->fd_magic != fcb->f_magic) {
return -ENOMSG;
}
return 1;
}
/**
* Finds the fcb entry that gives back upto n entries at the end.
* @param0 ptr to fcb
* @param1 n number of fcb entries the user wants to get
* @param2 ptr to the fcb_entry to be returned
* @return 0 on there are any fcbs aviable; -ENOENT otherwise
*/
int
fcb_offset_last_n(struct fcb *fcb, u8_t entries,
struct fcb_entry *last_n_entry)
{
struct fcb_entry loc;
int i;
int rc;
/* assure a minimum amount of entries */
if (!entries) {
entries = 1U;
}
i = 0;
(void)memset(&loc, 0, sizeof(loc));
while (!fcb_getnext(fcb, &loc)) {
if (i == 0) {
/* Start from the beginning of fcb entries */
*last_n_entry = loc;
}
/* Update last_n_entry after n entries and keep updating */
else if (i > (entries - 1)) {
rc = fcb_getnext(fcb, last_n_entry);
if (rc) {
/* A fcb history must have been erased,
* wanted entry doesn't exist anymore.
*/
return -ENOENT;
}
}
i++;
}
return (i == 0) ? -ENOENT : 0;
}
/**
* Clear fcb
* @param fcb
* @return 0 on success; non-zero on failure
*/
int
fcb_clear(struct fcb *fcb)
{
int rc;
rc = 0;
while (!fcb_is_empty(fcb)) {
rc = fcb_rotate(fcb);
if (rc) {
break;
}
}
return rc;
}