Import of adapted loboris/ESP32_ePaper_example repository
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
|
||||
COMPONENT_SRCDIRS := .
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_PRIV_INCLUDEDIRS :=
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Lua RTOS, SPIFFS low access
|
||||
*
|
||||
* Copyright (C) 2015 - 2017
|
||||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
|
||||
*
|
||||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* and its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that the above copyright notice appear in all
|
||||
* copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "esp_spiffs.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#include "spiffs.h"
|
||||
|
||||
#include <esp_spi_flash.h>
|
||||
|
||||
s32_t IRAM_ATTR esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) {
|
||||
u32_t aaddr;
|
||||
u8_t *buff = NULL;
|
||||
u8_t *abuff = NULL;
|
||||
u32_t asize;
|
||||
|
||||
asize = size;
|
||||
|
||||
// Align address to 4 byte
|
||||
aaddr = (addr + (4 - 1)) & (u32_t)-4;
|
||||
if (aaddr != addr) {
|
||||
aaddr -= 4;
|
||||
asize += (addr - aaddr);
|
||||
}
|
||||
|
||||
// Align size to 4 byte
|
||||
asize = (asize + (4 - 1)) & (u32_t)-4;
|
||||
|
||||
if ((aaddr != addr) || (asize != size)) {
|
||||
// Align buffer
|
||||
buff = malloc(asize + 4);
|
||||
if (!buff) {
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & (u32_t)-4);
|
||||
|
||||
if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) {
|
||||
free(buff);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
memcpy(dst, abuff + (addr - aaddr), size);
|
||||
|
||||
free(buff);
|
||||
} else {
|
||||
if (spi_flash_read(addr, (void *)dst, size) != 0) {
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
s32_t IRAM_ATTR esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src) {
|
||||
u32_t aaddr;
|
||||
u8_t *buff = NULL;
|
||||
u8_t *abuff = NULL;
|
||||
u32_t asize;
|
||||
|
||||
asize = size;
|
||||
|
||||
// Align address to 4 byte
|
||||
aaddr = (addr + (4 - 1)) & -4;
|
||||
if (aaddr != addr) {
|
||||
aaddr -= 4;
|
||||
asize += (addr - aaddr);
|
||||
}
|
||||
|
||||
// Align size to 4 byte
|
||||
asize = (asize + (4 - 1)) & -4;
|
||||
|
||||
if ((aaddr != addr) || (asize != size)) {
|
||||
// Align buffer
|
||||
buff = malloc(asize + 4);
|
||||
if (!buff) {
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & -4);
|
||||
|
||||
if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) {
|
||||
free(buff);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
memcpy(abuff + (addr - aaddr), src, size);
|
||||
|
||||
if (spi_flash_write(aaddr, (uint32_t *)abuff, asize) != 0) {
|
||||
free(buff);
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
free(buff);
|
||||
} else {
|
||||
if (spi_flash_write(addr, (uint32_t *)src, size) != 0) {
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
s32_t IRAM_ATTR esp32_spi_flash_erase(u32_t addr, u32_t size) {
|
||||
if (spi_flash_erase_sector(addr >> 12) != 0) {
|
||||
return SPIFFS_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Lua RTOS, write syscall implementation
|
||||
*
|
||||
* Copyright (C) 2015 - 2017
|
||||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
|
||||
*
|
||||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* and its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that the above copyright notice appear in all
|
||||
* copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*/
|
||||
|
||||
#ifndef __ESP_SPIFFS_H__
|
||||
#define __ESP_SPIFFS_H__
|
||||
|
||||
#include "spiffs.h"
|
||||
|
||||
s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst);
|
||||
s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src);
|
||||
s32_t esp32_spi_flash_erase(u32_t addr, u32_t size);
|
||||
|
||||
#define low_spiffs_read (spiffs_read *)esp32_spi_flash_read
|
||||
#define low_spiffs_write (spiffs_write *)esp32_spi_flash_write
|
||||
#define low_spiffs_erase (spiffs_erase *)esp32_spi_flash_erase
|
||||
|
||||
#endif // __ESP_SPIFFS_H__
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Lua RTOS, list data structure
|
||||
*
|
||||
* Copyright (C) 2015 - 2017
|
||||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
|
||||
*
|
||||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* and its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that the above copyright notice appear in all
|
||||
* copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*/
|
||||
|
||||
#include "esp_attr.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "mutex.h"
|
||||
|
||||
void list_init(struct list *list, int first_index) {
|
||||
// Create the mutex
|
||||
mtx_init(&list->mutex, NULL, NULL, 0);
|
||||
|
||||
mtx_lock(&list->mutex);
|
||||
|
||||
list->indexes = 0;
|
||||
list->free = NULL;
|
||||
list->index = NULL;
|
||||
list->first_index = first_index;
|
||||
|
||||
mtx_unlock(&list->mutex);
|
||||
}
|
||||
|
||||
int list_add(struct list *list, void *item, int *item_index) {
|
||||
struct list_index *index = NULL;
|
||||
struct list_index *indexa = NULL;
|
||||
int grow = 0;
|
||||
|
||||
mtx_lock(&list->mutex);
|
||||
|
||||
// Get an index
|
||||
if (list->free) {
|
||||
// Get first free element
|
||||
index = list->free;
|
||||
list->free = index->next;
|
||||
} else {
|
||||
// Must grow index array
|
||||
grow = 1;
|
||||
}
|
||||
|
||||
if (grow) {
|
||||
// Increment index count
|
||||
list->indexes++;
|
||||
|
||||
// Create a new index array for allocate new index
|
||||
indexa = (struct list_index *)malloc(sizeof(struct list_index) * list->indexes);
|
||||
if (!indexa) {
|
||||
mtx_unlock(&list->mutex);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (list->index) {
|
||||
// Copy current index array to new created
|
||||
bcopy(list->index, indexa, sizeof(struct list_index) * (list->indexes - 1));
|
||||
|
||||
// Free current index array
|
||||
free(list->index);
|
||||
}
|
||||
|
||||
// Store new index array
|
||||
list->index = indexa;
|
||||
|
||||
// Current index
|
||||
index = list->index + list->indexes - 1;
|
||||
|
||||
// Initialize new index
|
||||
index->index = list->indexes - 1;
|
||||
|
||||
}
|
||||
|
||||
index->next = NULL;
|
||||
index->item = item;
|
||||
index->deleted = 0;
|
||||
|
||||
// Return index
|
||||
*item_index = index->index + list->first_index;
|
||||
|
||||
mtx_unlock(&list->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IRAM_ATTR list_get(struct list *list, int index, void **item) {
|
||||
struct list_index *cindex = NULL;
|
||||
int iindex;
|
||||
|
||||
mtx_lock(&list->mutex);
|
||||
|
||||
if (!list->indexes) {
|
||||
mtx_unlock(&list->mutex);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
// Check index
|
||||
if (index < list->first_index) {
|
||||
mtx_unlock(&list->mutex);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
// Get new internal index
|
||||
iindex = index - list->first_index;
|
||||
|
||||
// Test for a valid index
|
||||
if (iindex > list->indexes) {
|
||||
mtx_unlock(&list->mutex);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
cindex = list->index + iindex;
|
||||
|
||||
if (cindex->deleted) {
|
||||
mtx_unlock(&list->mutex);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
*item = cindex->item;
|
||||
|
||||
mtx_unlock(&list->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int list_remove(struct list *list, int index, int destroy) {
|
||||
struct list_index *cindex = NULL;
|
||||
int iindex;
|
||||
|
||||
mtx_lock(&list->mutex);
|
||||
|
||||
// Check index
|
||||
if (index < list->first_index) {
|
||||
mtx_unlock(&list->mutex);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
// Get new internal index
|
||||
iindex = index - list->first_index;
|
||||
|
||||
// Test for a valid index
|
||||
if ((iindex < 0) || (iindex > list->indexes)) {
|
||||
mtx_unlock(&list->mutex);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
cindex = &list->index[iindex];
|
||||
|
||||
if (destroy) {
|
||||
free(cindex->item);
|
||||
}
|
||||
|
||||
cindex->next = list->free;
|
||||
cindex->deleted = 1;
|
||||
list->free = cindex;
|
||||
|
||||
mtx_unlock(&list->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IRAM_ATTR list_first(struct list *list) {
|
||||
int index;
|
||||
int res = -1;
|
||||
|
||||
mtx_lock(&list->mutex);
|
||||
|
||||
for(index=0;index < list->indexes;index++) {
|
||||
if (!list->index[index].deleted) {
|
||||
res = index + list->first_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mtx_unlock(&list->mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int IRAM_ATTR list_next(struct list *list, int index) {
|
||||
int res = -1;
|
||||
int iindex;
|
||||
|
||||
mtx_lock(&list->mutex);
|
||||
|
||||
// Check index
|
||||
if (index < list->first_index) {
|
||||
mtx_unlock(&list->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get new internal index
|
||||
iindex = index - list->first_index + 1;
|
||||
|
||||
// Get next non deleted item on list
|
||||
for(;iindex < list->indexes;iindex++) {
|
||||
if (!list->index[iindex].deleted) {
|
||||
res = iindex + list->first_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mtx_unlock(&list->mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void list_destroy(struct list *list, int items) {
|
||||
int index;
|
||||
|
||||
mtx_lock(&list->mutex);
|
||||
|
||||
if (items) {
|
||||
for(index=0;index < list->indexes;index++) {
|
||||
if (!list->index[index].deleted) {
|
||||
free(list->index[index].item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(list->index);
|
||||
|
||||
mtx_unlock(&list->mutex);
|
||||
mtx_destroy(&list->mutex);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Lua RTOS, list data structure
|
||||
*
|
||||
* Copyright (C) 2015 - 2017
|
||||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
|
||||
*
|
||||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* and its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that the above copyright notice appear in all
|
||||
* copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*/
|
||||
|
||||
#ifndef _LIST_H
|
||||
#define _LIST_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mutex.h"
|
||||
|
||||
struct list {
|
||||
struct mtx mutex;
|
||||
struct list_index *index;
|
||||
struct list_index *free;
|
||||
uint8_t indexes;
|
||||
uint8_t first_index;
|
||||
};
|
||||
|
||||
struct list_index {
|
||||
void *item;
|
||||
uint8_t index;
|
||||
uint8_t deleted;
|
||||
struct list_index *next;
|
||||
};
|
||||
|
||||
void list_init(struct list *list, int first_index);
|
||||
int list_add(struct list *list, void *item, int *item_index);
|
||||
int list_get(struct list *list, int index, void **item);
|
||||
int list_remove(struct list *list, int index, int destroy);
|
||||
int list_first(struct list *list);
|
||||
int list_next(struct list *list, int index);
|
||||
void list_destroy(struct list *list, int items);
|
||||
|
||||
#endif /* LIST_H */
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Lua RTOS, mutex api implementation over FreeRTOS
|
||||
*
|
||||
* Copyright (C) 2015 - 2017
|
||||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
|
||||
*
|
||||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* and its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that the above copyright notice appear in all
|
||||
* copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*
|
||||
* Modified by: LoBo (loboris@gmail.com / https://github.com/loboris)
|
||||
*
|
||||
*/
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "mutex.h"
|
||||
|
||||
|
||||
#define portEND_SWITCHING_ISR(xSwitchRequired) \
|
||||
if (xSwitchRequired) { \
|
||||
_frxt_setup_switch(); \
|
||||
}
|
||||
|
||||
extern unsigned port_interruptNesting[portNUM_PROCESSORS];
|
||||
|
||||
void _mtx_init() {
|
||||
}
|
||||
|
||||
void mtx_init(struct mtx *mutex, const char *name, const char *type, int opts) {
|
||||
mutex->sem = xSemaphoreCreateBinary();
|
||||
|
||||
if (mutex->sem) {
|
||||
if (port_interruptNesting[xPortGetCoreID()] != 0) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken);
|
||||
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
|
||||
} else {
|
||||
xSemaphoreGive( mutex->sem );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR mtx_lock(struct mtx *mutex) {
|
||||
if (port_interruptNesting[xPortGetCoreID()] != 0) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xSemaphoreTakeFromISR( mutex->sem, &xHigherPriorityTaskWoken );
|
||||
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
|
||||
} else {
|
||||
xSemaphoreTake( mutex->sem, portMAX_DELAY );
|
||||
}
|
||||
}
|
||||
|
||||
int mtx_trylock(struct mtx *mutex) {
|
||||
if (xSemaphoreTake( mutex->sem, 0 ) == pdTRUE) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR mtx_unlock(struct mtx *mutex) {
|
||||
if (port_interruptNesting[xPortGetCoreID()] != 0) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken );
|
||||
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
|
||||
} else {
|
||||
xSemaphoreGive( mutex->sem );
|
||||
}
|
||||
}
|
||||
|
||||
void mtx_destroy(struct mtx *mutex) {
|
||||
if (port_interruptNesting[xPortGetCoreID()] != 0) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken );
|
||||
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
|
||||
} else {
|
||||
xSemaphoreGive( mutex->sem );
|
||||
}
|
||||
|
||||
vSemaphoreDelete( mutex->sem );
|
||||
|
||||
mutex->sem = 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Lua RTOS, mutex api implementation over FreeRTOS
|
||||
*
|
||||
* Copyright (C) 2015 - 2017
|
||||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
|
||||
*
|
||||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* and its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that the above copyright notice appear in all
|
||||
* copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*
|
||||
* Modified by: LoBo (loboris@gmail.com / https://github.com/loboris)
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MUTEX_H_H
|
||||
#define MUTEX_H_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#define MUTEX_INITIALIZER {.sem = 0}
|
||||
|
||||
struct mtx {
|
||||
SemaphoreHandle_t sem;
|
||||
};
|
||||
|
||||
|
||||
void mtx_init(struct mtx *mutex, const char *name, const char *type, int opts);
|
||||
void mtx_lock(struct mtx *mutex);
|
||||
int mtx_trylock(struct mtx *mutex);
|
||||
void mtx_unlock(struct mtx *mutex);
|
||||
void mtx_destroy(struct mtx *mutex);
|
||||
|
||||
#endif /* MUTEX_H_H */
|
||||
|
||||
@@ -0,0 +1,813 @@
|
||||
/*
|
||||
* spiffs.h
|
||||
*
|
||||
* Created on: May 26, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#ifndef SPIFFS_H_
|
||||
#define SPIFFS_H_
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "spiffs_config.h"
|
||||
|
||||
#define SPIFFS_OK 0
|
||||
#define SPIFFS_ERR_NOT_MOUNTED -10000
|
||||
#define SPIFFS_ERR_FULL -10001
|
||||
#define SPIFFS_ERR_NOT_FOUND -10002
|
||||
#define SPIFFS_ERR_END_OF_OBJECT -10003
|
||||
#define SPIFFS_ERR_DELETED -10004
|
||||
#define SPIFFS_ERR_NOT_FINALIZED -10005
|
||||
#define SPIFFS_ERR_NOT_INDEX -10006
|
||||
#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007
|
||||
#define SPIFFS_ERR_FILE_CLOSED -10008
|
||||
#define SPIFFS_ERR_FILE_DELETED -10009
|
||||
#define SPIFFS_ERR_BAD_DESCRIPTOR -10010
|
||||
#define SPIFFS_ERR_IS_INDEX -10011
|
||||
#define SPIFFS_ERR_IS_FREE -10012
|
||||
#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013
|
||||
#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014
|
||||
#define SPIFFS_ERR_INDEX_REF_FREE -10015
|
||||
#define SPIFFS_ERR_INDEX_REF_LU -10016
|
||||
#define SPIFFS_ERR_INDEX_REF_INVALID -10017
|
||||
#define SPIFFS_ERR_INDEX_FREE -10018
|
||||
#define SPIFFS_ERR_INDEX_LU -10019
|
||||
#define SPIFFS_ERR_INDEX_INVALID -10020
|
||||
#define SPIFFS_ERR_NOT_WRITABLE -10021
|
||||
#define SPIFFS_ERR_NOT_READABLE -10022
|
||||
#define SPIFFS_ERR_CONFLICTING_NAME -10023
|
||||
#define SPIFFS_ERR_NOT_CONFIGURED -10024
|
||||
|
||||
#define SPIFFS_ERR_NOT_A_FS -10025
|
||||
#define SPIFFS_ERR_MOUNTED -10026
|
||||
#define SPIFFS_ERR_ERASE_FAIL -10027
|
||||
#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028
|
||||
|
||||
#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029
|
||||
|
||||
#define SPIFFS_ERR_FILE_EXISTS -10030
|
||||
|
||||
#define SPIFFS_ERR_NOT_A_FILE -10031
|
||||
#define SPIFFS_ERR_RO_NOT_IMPL -10032
|
||||
#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033
|
||||
#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034
|
||||
#define SPIFFS_ERR_PROBE_NOT_A_FS -10035
|
||||
#define SPIFFS_ERR_NAME_TOO_LONG -10036
|
||||
|
||||
#define SPIFFS_ERR_IX_MAP_UNMAPPED -10037
|
||||
#define SPIFFS_ERR_IX_MAP_MAPPED -10038
|
||||
#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039
|
||||
|
||||
#define SPIFFS_ERR_INTERNAL -10050
|
||||
|
||||
#define SPIFFS_ERR_TEST -10100
|
||||
|
||||
|
||||
// spiffs file descriptor index type. must be signed
|
||||
typedef s16_t spiffs_file;
|
||||
// spiffs file descriptor flags
|
||||
typedef u16_t spiffs_flags;
|
||||
// spiffs file mode
|
||||
typedef u16_t spiffs_mode;
|
||||
// object type
|
||||
typedef u8_t spiffs_obj_type;
|
||||
|
||||
struct spiffs_t;
|
||||
|
||||
#if SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* spi read call function type */
|
||||
typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst);
|
||||
/* spi write call function type */
|
||||
typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src);
|
||||
/* spi erase call function type */
|
||||
typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size);
|
||||
|
||||
#else // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* spi read call function type */
|
||||
typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst);
|
||||
/* spi write call function type */
|
||||
typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src);
|
||||
/* spi erase call function type */
|
||||
typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size);
|
||||
#endif // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* file system check callback report operation */
|
||||
typedef enum {
|
||||
SPIFFS_CHECK_LOOKUP = 0,
|
||||
SPIFFS_CHECK_INDEX,
|
||||
SPIFFS_CHECK_PAGE
|
||||
} spiffs_check_type;
|
||||
|
||||
/* file system check callback report type */
|
||||
typedef enum {
|
||||
SPIFFS_CHECK_PROGRESS = 0,
|
||||
SPIFFS_CHECK_ERROR,
|
||||
SPIFFS_CHECK_FIX_INDEX,
|
||||
SPIFFS_CHECK_FIX_LOOKUP,
|
||||
SPIFFS_CHECK_DELETE_ORPHANED_INDEX,
|
||||
SPIFFS_CHECK_DELETE_PAGE,
|
||||
SPIFFS_CHECK_DELETE_BAD_FILE
|
||||
} spiffs_check_report;
|
||||
|
||||
/* file system check callback function */
|
||||
#if SPIFFS_HAL_CALLBACK_EXTRA
|
||||
typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report,
|
||||
u32_t arg1, u32_t arg2);
|
||||
#else // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report,
|
||||
u32_t arg1, u32_t arg2);
|
||||
#endif // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* file system listener callback operation */
|
||||
typedef enum {
|
||||
/* the file has been created */
|
||||
SPIFFS_CB_CREATED = 0,
|
||||
/* the file has been updated or moved to another page */
|
||||
SPIFFS_CB_UPDATED,
|
||||
/* the file has been deleted */
|
||||
SPIFFS_CB_DELETED
|
||||
} spiffs_fileop_type;
|
||||
|
||||
/* file system listener callback function */
|
||||
typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix);
|
||||
|
||||
#ifndef SPIFFS_DBG
|
||||
#define SPIFFS_DBG(...) \
|
||||
printf(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef SPIFFS_GC_DBG
|
||||
#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef SPIFFS_CACHE_DBG
|
||||
#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef SPIFFS_CHECK_DBG
|
||||
#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/* Any write to the filehandle is appended to end of the file */
|
||||
#define SPIFFS_APPEND (1<<0)
|
||||
#define SPIFFS_O_APPEND SPIFFS_APPEND
|
||||
/* If the opened file exists, it will be truncated to zero length before opened */
|
||||
#define SPIFFS_TRUNC (1<<1)
|
||||
#define SPIFFS_O_TRUNC SPIFFS_TRUNC
|
||||
/* If the opened file does not exist, it will be created before opened */
|
||||
#define SPIFFS_CREAT (1<<2)
|
||||
#define SPIFFS_O_CREAT SPIFFS_CREAT
|
||||
/* The opened file may only be read */
|
||||
#define SPIFFS_RDONLY (1<<3)
|
||||
#define SPIFFS_O_RDONLY SPIFFS_RDONLY
|
||||
/* The opened file may only be written */
|
||||
#define SPIFFS_WRONLY (1<<4)
|
||||
#define SPIFFS_O_WRONLY SPIFFS_WRONLY
|
||||
/* The opened file may be both read and written */
|
||||
#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY)
|
||||
#define SPIFFS_O_RDWR SPIFFS_RDWR
|
||||
/* Any writes to the filehandle will never be cached but flushed directly */
|
||||
#define SPIFFS_DIRECT (1<<5)
|
||||
#define SPIFFS_O_DIRECT SPIFFS_DIRECT
|
||||
/* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */
|
||||
#define SPIFFS_EXCL (1<<6)
|
||||
#define SPIFFS_O_EXCL SPIFFS_EXCL
|
||||
|
||||
#define SPIFFS_SEEK_SET (0)
|
||||
#define SPIFFS_SEEK_CUR (1)
|
||||
#define SPIFFS_SEEK_END (2)
|
||||
|
||||
#define SPIFFS_TYPE_FILE (1)
|
||||
#define SPIFFS_TYPE_DIR (2)
|
||||
#define SPIFFS_TYPE_HARD_LINK (3)
|
||||
#define SPIFFS_TYPE_SOFT_LINK (4)
|
||||
|
||||
#ifndef SPIFFS_LOCK
|
||||
#define SPIFFS_LOCK(fs)
|
||||
#endif
|
||||
|
||||
#ifndef SPIFFS_UNLOCK
|
||||
#define SPIFFS_UNLOCK(fs)
|
||||
#endif
|
||||
|
||||
// phys structs
|
||||
|
||||
// spiffs spi configuration struct
|
||||
typedef struct {
|
||||
// physical read function
|
||||
spiffs_read hal_read_f;
|
||||
// physical write function
|
||||
spiffs_write hal_write_f;
|
||||
// physical erase function
|
||||
spiffs_erase hal_erase_f;
|
||||
#if SPIFFS_SINGLETON == 0
|
||||
// physical size of the spi flash
|
||||
u32_t phys_size;
|
||||
// physical offset in spi flash used for spiffs,
|
||||
// must be on block boundary
|
||||
u32_t phys_addr;
|
||||
// physical size when erasing a block
|
||||
u32_t phys_erase_block;
|
||||
|
||||
// logical size of a block, must be on physical
|
||||
// block size boundary and must never be less than
|
||||
// a physical block
|
||||
u32_t log_block_size;
|
||||
// logical size of a page, must be at least
|
||||
// log_block_size / 8
|
||||
u32_t log_page_size;
|
||||
|
||||
#endif
|
||||
#if SPIFFS_FILEHDL_OFFSET
|
||||
// an integer offset added to each file handle
|
||||
u16_t fh_ix_offset;
|
||||
#endif
|
||||
} spiffs_config;
|
||||
|
||||
typedef struct spiffs_t {
|
||||
// file system configuration
|
||||
spiffs_config cfg;
|
||||
// number of logical blocks
|
||||
u32_t block_count;
|
||||
|
||||
// cursor for free blocks, block index
|
||||
spiffs_block_ix free_cursor_block_ix;
|
||||
// cursor for free blocks, entry index
|
||||
int free_cursor_obj_lu_entry;
|
||||
// cursor when searching, block index
|
||||
spiffs_block_ix cursor_block_ix;
|
||||
// cursor when searching, entry index
|
||||
int cursor_obj_lu_entry;
|
||||
|
||||
// primary work buffer, size of a logical page
|
||||
u8_t *lu_work;
|
||||
// secondary work buffer, size of a logical page
|
||||
u8_t *work;
|
||||
// file descriptor memory area
|
||||
u8_t *fd_space;
|
||||
// available file descriptors
|
||||
u32_t fd_count;
|
||||
|
||||
// last error
|
||||
s32_t err_code;
|
||||
|
||||
// current number of free blocks
|
||||
u32_t free_blocks;
|
||||
// current number of busy pages
|
||||
u32_t stats_p_allocated;
|
||||
// current number of deleted pages
|
||||
u32_t stats_p_deleted;
|
||||
// flag indicating that garbage collector is cleaning
|
||||
u8_t cleaning;
|
||||
// max erase count amongst all blocks
|
||||
spiffs_obj_id max_erase_count;
|
||||
|
||||
#if SPIFFS_GC_STATS
|
||||
u32_t stats_gc_runs;
|
||||
#endif
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
// cache memory
|
||||
void *cache;
|
||||
// cache size
|
||||
u32_t cache_size;
|
||||
#if SPIFFS_CACHE_STATS
|
||||
u32_t cache_hits;
|
||||
u32_t cache_misses;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// check callback function
|
||||
spiffs_check_callback check_cb_f;
|
||||
// file callback function
|
||||
spiffs_file_callback file_cb_f;
|
||||
// mounted flag
|
||||
u8_t mounted;
|
||||
// user data
|
||||
void *user_data;
|
||||
// config magic
|
||||
u32_t config_magic;
|
||||
} spiffs;
|
||||
|
||||
/* spiffs file status struct */
|
||||
typedef struct {
|
||||
spiffs_obj_id obj_id;
|
||||
u32_t size;
|
||||
spiffs_obj_type type;
|
||||
spiffs_page_ix pix;
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
#if SPIFFS_OBJ_META_LEN
|
||||
u8_t meta[SPIFFS_OBJ_META_LEN];
|
||||
#endif
|
||||
} spiffs_stat;
|
||||
|
||||
struct spiffs_dirent {
|
||||
spiffs_obj_id obj_id;
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
spiffs_obj_type type;
|
||||
u32_t size;
|
||||
spiffs_page_ix pix;
|
||||
#if SPIFFS_OBJ_META_LEN
|
||||
u8_t meta[SPIFFS_OBJ_META_LEN];
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
spiffs *fs;
|
||||
spiffs_block_ix block;
|
||||
int entry;
|
||||
} spiffs_DIR;
|
||||
|
||||
#if SPIFFS_IX_MAP
|
||||
|
||||
typedef struct {
|
||||
// buffer with looked up data pixes
|
||||
spiffs_page_ix *map_buf;
|
||||
// precise file byte offset
|
||||
u32_t offset;
|
||||
// start data span index of lookup buffer
|
||||
spiffs_span_ix start_spix;
|
||||
// end data span index of lookup buffer
|
||||
spiffs_span_ix end_spix;
|
||||
} spiffs_ix_map;
|
||||
|
||||
#endif
|
||||
|
||||
// functions
|
||||
|
||||
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
|
||||
/**
|
||||
* Special function. This takes a spiffs config struct and returns the number
|
||||
* of blocks this file system was formatted with. This function relies on
|
||||
* that following info is set correctly in given config struct:
|
||||
*
|
||||
* phys_addr, log_page_size, and log_block_size.
|
||||
*
|
||||
* Also, hal_read_f must be set in the config struct.
|
||||
*
|
||||
* One must be sure of the correct page size and that the physical address is
|
||||
* correct in the probed file system when calling this function. It is not
|
||||
* checked if the phys_addr actually points to the start of the file system,
|
||||
* so one might get a false positive if entering a phys_addr somewhere in the
|
||||
* middle of the file system at block boundary. In addition, it is not checked
|
||||
* if the page size is actually correct. If it is not, weird file system sizes
|
||||
* will be returned.
|
||||
*
|
||||
* If this function detects a file system it returns the assumed file system
|
||||
* size, which can be used to set the phys_size.
|
||||
*
|
||||
* Otherwise, it returns an error indicating why it is not regarded as a file
|
||||
* system.
|
||||
*
|
||||
* Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK
|
||||
* macros. It returns the error code directly, instead of as read by
|
||||
* SPIFFS_errno.
|
||||
*
|
||||
* @param config essential parts of the physical and logical
|
||||
* configuration of the file system.
|
||||
*/
|
||||
s32_t SPIFFS_probe_fs(spiffs_config *config);
|
||||
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
|
||||
|
||||
/**
|
||||
* Initializes the file system dynamic parameters and mounts the filesystem.
|
||||
* If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS
|
||||
* if the flash does not contain a recognizable file system.
|
||||
* In this case, SPIFFS_format must be called prior to remounting.
|
||||
* @param fs the file system struct
|
||||
* @param config the physical and logical configuration of the file system
|
||||
* @param work a memory work buffer comprising 2*config->log_page_size
|
||||
* bytes used throughout all file system operations
|
||||
* @param fd_space memory for file descriptors
|
||||
* @param fd_space_size memory size of file descriptors
|
||||
* @param cache memory for cache, may be null
|
||||
* @param cache_size memory size of cache
|
||||
* @param check_cb_f callback function for reporting during consistency checks
|
||||
*/
|
||||
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
|
||||
u8_t *fd_space, u32_t fd_space_size,
|
||||
void *cache, u32_t cache_size,
|
||||
spiffs_check_callback check_cb_f);
|
||||
|
||||
/**
|
||||
* Unmounts the file system. All file handles will be flushed of any
|
||||
* cached writes and closed.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
void SPIFFS_unmount(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Creates a new file.
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the new file
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens/creates a file.
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the new file
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY,
|
||||
* SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens a file by given dir entry.
|
||||
* Optimization purposes, when traversing a file system with SPIFFS_readdir
|
||||
* a normal SPIFFS_open would need to traverse the filesystem again to find
|
||||
* the file, whilst SPIFFS_open_by_dirent already knows where the file resides.
|
||||
* @param fs the file system struct
|
||||
* @param e the dir entry to the file
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
||||
* SPIFFS_CREAT will have no effect in this case.
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens a file by given page index.
|
||||
* Optimization purposes, opens a file by directly pointing to the page
|
||||
* index in the spi flash.
|
||||
* If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE
|
||||
* is returned.
|
||||
* @param fs the file system struct
|
||||
* @param page_ix the page index
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
||||
* SPIFFS_CREAT will have no effect in this case.
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Reads from given filehandle.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param buf where to put read data
|
||||
* @param len how much to read
|
||||
* @returns number of bytes read, or -1 if error
|
||||
*/
|
||||
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
|
||||
|
||||
/**
|
||||
* Writes to given filehandle.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param buf the data to write
|
||||
* @param len how much to write
|
||||
* @returns number of bytes written, or -1 if error
|
||||
*/
|
||||
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
|
||||
|
||||
/**
|
||||
* Moves the read/write file offset. Resulting offset is returned or negative if error.
|
||||
* lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param offs how much/where to move the offset
|
||||
* @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes
|
||||
* if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset
|
||||
* if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative
|
||||
*/
|
||||
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
|
||||
|
||||
/**
|
||||
* Removes a file by path
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the file to remove
|
||||
*/
|
||||
s32_t SPIFFS_remove(spiffs *fs, const char *path);
|
||||
|
||||
/**
|
||||
* Removes a file by filehandle
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to remove
|
||||
*/
|
||||
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Gets file status by path
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the file to stat
|
||||
* @param s the stat struct to populate
|
||||
*/
|
||||
s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s);
|
||||
|
||||
/**
|
||||
* Gets file status by filehandle
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to stat
|
||||
* @param s the stat struct to populate
|
||||
*/
|
||||
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s);
|
||||
|
||||
/**
|
||||
* Flushes all pending write operations from cache for given file
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to flush
|
||||
*/
|
||||
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Closes a filehandle. If there are pending write operations, these are finalized before closing.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to close
|
||||
*/
|
||||
s32_t SPIFFS_close(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Renames a file
|
||||
* @param fs the file system struct
|
||||
* @param old path of file to rename
|
||||
* @param newPath new path of file
|
||||
*/
|
||||
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath);
|
||||
|
||||
#if SPIFFS_OBJ_META_LEN
|
||||
/**
|
||||
* Updates file's metadata
|
||||
* @param fs the file system struct
|
||||
* @param path path to the file
|
||||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
|
||||
*/
|
||||
s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta);
|
||||
|
||||
/**
|
||||
* Updates file's metadata
|
||||
* @param fs the file system struct
|
||||
* @param fh file handle of the file
|
||||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
|
||||
*/
|
||||
s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns last error of last file operation.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_errno(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Clears last error.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
void SPIFFS_clearerr(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Opens a directory stream corresponding to the given name.
|
||||
* The stream is positioned at the first entry in the directory.
|
||||
* On hydrogen builds the name argument is ignored as hydrogen builds always correspond
|
||||
* to a flat file structure - no directories.
|
||||
* @param fs the file system struct
|
||||
* @param name the name of the directory
|
||||
* @param d pointer the directory stream to be populated
|
||||
*/
|
||||
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d);
|
||||
|
||||
/**
|
||||
* Closes a directory stream
|
||||
* @param d the directory stream to close
|
||||
*/
|
||||
s32_t SPIFFS_closedir(spiffs_DIR *d);
|
||||
|
||||
/**
|
||||
* Reads a directory into given spifs_dirent struct.
|
||||
* @param d pointer to the directory stream
|
||||
* @param e the dirent struct to be populated
|
||||
* @returns null if error or end of stream, else given dirent is returned
|
||||
*/
|
||||
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
|
||||
|
||||
/**
|
||||
* Runs a consistency check on given filesystem.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_check(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Returns number of total bytes available and number of used bytes.
|
||||
* This is an estimation, and depends on if there a many files with little
|
||||
* data or few files with much data.
|
||||
* NB: If used number of bytes exceeds total bytes, a SPIFFS_check should
|
||||
* run. This indicates a power loss in midst of things. In worst case
|
||||
* (repeated powerlosses in mending or gc) you might have to delete some files.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param total total number of bytes in filesystem
|
||||
* @param used used number of bytes in filesystem
|
||||
*/
|
||||
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used);
|
||||
|
||||
/**
|
||||
* Formats the entire file system. All data will be lost.
|
||||
* The filesystem must not be mounted when calling this.
|
||||
*
|
||||
* NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount
|
||||
* MUST be called prior to formatting in order to configure the filesystem.
|
||||
* If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling
|
||||
* SPIFFS_format.
|
||||
* If SPIFFS_mount fails, SPIFFS_format can be called directly without calling
|
||||
* SPIFFS_unmount first.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_format(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Returns nonzero if spiffs is mounted, or zero if unmounted.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
u8_t SPIFFS_mounted(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Tries to find a block where most or all pages are deleted, and erase that
|
||||
* block if found. Does not care for wear levelling. Will not move pages
|
||||
* around.
|
||||
* If parameter max_free_pages are set to 0, only blocks with only deleted
|
||||
* pages will be selected.
|
||||
*
|
||||
* NB: the garbage collector is automatically called when spiffs needs free
|
||||
* pages. The reason for this function is to give possibility to do background
|
||||
* tidying when user knows the system is idle.
|
||||
*
|
||||
* Use with care.
|
||||
*
|
||||
* Setting max_free_pages to anything larger than zero will eventually wear
|
||||
* flash more as a block containing free pages can be erased.
|
||||
*
|
||||
* Will set err_no to SPIFFS_OK if a block was found and erased,
|
||||
* SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found,
|
||||
* or other error.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param max_free_pages maximum number allowed free pages in block
|
||||
*/
|
||||
s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages);
|
||||
|
||||
/**
|
||||
* Will try to make room for given amount of bytes in the filesystem by moving
|
||||
* pages and erasing blocks.
|
||||
* If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If
|
||||
* there already is this amount (or more) of free space, SPIFFS_gc will
|
||||
* silently return. It is recommended to call SPIFFS_info before invoking
|
||||
* this method in order to determine what amount of bytes to give.
|
||||
*
|
||||
* NB: the garbage collector is automatically called when spiffs needs free
|
||||
* pages. The reason for this function is to give possibility to do background
|
||||
* tidying when user knows the system is idle.
|
||||
*
|
||||
* Use with care.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param size amount of bytes that should be freed
|
||||
*/
|
||||
s32_t SPIFFS_gc(spiffs *fs, u32_t size);
|
||||
|
||||
/**
|
||||
* Check if EOF reached.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to check
|
||||
*/
|
||||
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Get position in file.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to check
|
||||
*/
|
||||
s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Registers a callback function that keeps track on operations on file
|
||||
* headers. Do note, that this callback is called from within internal spiffs
|
||||
* mechanisms. Any operations on the actual file system being callbacked from
|
||||
* in this callback will mess things up for sure - do not do this.
|
||||
* This can be used to track where files are and move around during garbage
|
||||
* collection, which in turn can be used to build location tables in ram.
|
||||
* Used in conjuction with SPIFFS_open_by_page this may improve performance
|
||||
* when opening a lot of files.
|
||||
* Must be invoked after mount.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param cb_func the callback on file operations
|
||||
*/
|
||||
s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func);
|
||||
|
||||
#if SPIFFS_IX_MAP
|
||||
|
||||
/**
|
||||
* Maps the first level index lookup to a given memory map.
|
||||
* This will make reading big files faster, as the memory map will be used for
|
||||
* looking up data pages instead of searching for the indices on the physical
|
||||
* medium. When mapping, all affected indicies are found and the information is
|
||||
* copied to the array.
|
||||
* Whole file or only parts of it may be mapped. The index map will cover file
|
||||
* contents from argument offset until and including arguments (offset+len).
|
||||
* It is valid to map a longer range than the current file size. The map will
|
||||
* then be populated when the file grows.
|
||||
* On garbage collections and file data page movements, the map array will be
|
||||
* automatically updated. Do not tamper with the map array, as this contains
|
||||
* the references to the data pages. Modifying it from outside will corrupt any
|
||||
* future readings using this file descriptor.
|
||||
* The map will no longer be used when the file descriptor closed or the file
|
||||
* is unmapped.
|
||||
* This can be useful to get faster and more deterministic timing when reading
|
||||
* large files, or when seeking and reading a lot within a file.
|
||||
* @param fs the file system struct
|
||||
* @param fh the file handle of the file to map
|
||||
* @param map a spiffs_ix_map struct, describing the index map
|
||||
* @param offset absolute file offset where to start the index map
|
||||
* @param len length of the mapping in actual file bytes
|
||||
* @param map_buf the array buffer for the look up data - number of required
|
||||
* elements in the array can be derived from function
|
||||
* SPIFFS_bytes_to_ix_map_entries given the length
|
||||
*/
|
||||
s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map,
|
||||
u32_t offset, u32_t len, spiffs_page_ix *map_buf);
|
||||
|
||||
/**
|
||||
* Unmaps the index lookup from this filehandle. All future readings will
|
||||
* proceed as normal, requiring reading of the first level indices from
|
||||
* physical media.
|
||||
* The map and map buffer given in function SPIFFS_ix_map will no longer be
|
||||
* referenced by spiffs.
|
||||
* It is not strictly necessary to unmap a file before closing it, as closing
|
||||
* a file will automatically unmap it.
|
||||
* @param fs the file system struct
|
||||
* @param fh the file handle of the file to unmap
|
||||
*/
|
||||
s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Moves the offset for the index map given in function SPIFFS_ix_map. Parts or
|
||||
* all of the map buffer will repopulated.
|
||||
* @param fs the file system struct
|
||||
* @param fh the mapped file handle of the file to remap
|
||||
* @param offset new absolute file offset where to start the index map
|
||||
*/
|
||||
s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs);
|
||||
|
||||
/**
|
||||
* Utility function to get number of spiffs_page_ix entries a map buffer must
|
||||
* contain on order to map given amount of file data in bytes.
|
||||
* See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes.
|
||||
* @param fs the file system struct
|
||||
* @param bytes number of file data bytes to map
|
||||
* @return needed number of elements in a spiffs_page_ix array needed to
|
||||
* map given amount of bytes in a file
|
||||
*/
|
||||
s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes);
|
||||
|
||||
/**
|
||||
* Utility function to amount of file data bytes that can be mapped when
|
||||
* mapping a file with buffer having given number of spiffs_page_ix entries.
|
||||
* See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries.
|
||||
* @param fs the file system struct
|
||||
* @param map_page_ix_entries number of entries in a spiffs_page_ix array
|
||||
* @return amount of file data in bytes that can be mapped given a map
|
||||
* buffer having given amount of spiffs_page_ix entries
|
||||
*/
|
||||
s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries);
|
||||
|
||||
#endif // SPIFFS_IX_MAP
|
||||
|
||||
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
/**
|
||||
* Prints out a visualization of the filesystem.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_vis(spiffs *fs);
|
||||
#endif
|
||||
|
||||
#if SPIFFS_BUFFER_HELP
|
||||
/**
|
||||
* Returns number of bytes needed for the filedescriptor buffer given
|
||||
* amount of file descriptors.
|
||||
*/
|
||||
u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
/**
|
||||
* Returns number of bytes needed for the cache buffer given
|
||||
* amount of cache pages.
|
||||
*/
|
||||
u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SPIFFS_H_ */
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* spiffs_cache.c
|
||||
*
|
||||
* Created on: Jun 23, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
|
||||
// returns cached page for give page index, or null if no such cached page
|
||||
static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) {
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0;
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->cpage_use_map & (1<<i)) &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
||||
cp->pix == pix ) {
|
||||
SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix);
|
||||
cp->last_access = cache->last_access;
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
//SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// frees cached page
|
||||
static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix);
|
||||
if (cache->cpage_use_map & (1<<ix)) {
|
||||
if (write_back &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, ix);
|
||||
res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
|
||||
}
|
||||
|
||||
cp->flags = 0;
|
||||
cache->cpage_use_map &= ~(1 << ix);
|
||||
|
||||
if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id);
|
||||
} else {
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// removes the oldest accessed cached page
|
||||
static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) {
|
||||
// at least one free cpage
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
// all busy, scan thru all to find the cpage which has oldest access
|
||||
int i;
|
||||
int cand_ix = -1;
|
||||
u32_t oldest_val = 0;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->last_access - cp->last_access) > oldest_val &&
|
||||
(cp->flags & flag_mask) == flags) {
|
||||
oldest_val = cache->last_access - cp->last_access;
|
||||
cand_ix = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (cand_ix >= 0) {
|
||||
res = spiffs_cache_page_free(fs, cand_ix, 1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// allocates a new cached page and returns it, or null if all cache pages are busy
|
||||
static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
if (cache->cpage_use_map == 0xffffffff) {
|
||||
// out of cache memory
|
||||
return 0;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
if ((cache->cpage_use_map & (1<<i)) == 0) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
cache->cpage_use_map |= (1<<i);
|
||||
cp->last_access = cache->last_access;
|
||||
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i);
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
// out of cache entries
|
||||
return 0;
|
||||
}
|
||||
|
||||
// drops the cache page for give page index
|
||||
void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) {
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
|
||||
if (cp) {
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
// reads from spi flash or the cache
|
||||
s32_t spiffs_phys_rd(
|
||||
spiffs *fs,
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *dst) {
|
||||
(void)fh;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
|
||||
cache->last_access++;
|
||||
if (cp) {
|
||||
// we've already got one, you see
|
||||
#if SPIFFS_CACHE_STATS
|
||||
fs->cache_hits++;
|
||||
#endif
|
||||
cp->last_access = cache->last_access;
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
|
||||
} else {
|
||||
if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
|
||||
// for second layer lookup functions, we do not cache in order to prevent shredding
|
||||
return SPIFFS_HAL_READ(fs, addr, len, dst);
|
||||
}
|
||||
#if SPIFFS_CACHE_STATS
|
||||
fs->cache_misses++;
|
||||
#endif
|
||||
// this operation will always free one cache page (unless all already free),
|
||||
// the result code stems from the write operation of the possibly freed cache page
|
||||
res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
|
||||
|
||||
cp = spiffs_cache_page_allocate(fs);
|
||||
if (cp) {
|
||||
cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
|
||||
cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
|
||||
|
||||
s32_t res2 = SPIFFS_HAL_READ(fs,
|
||||
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs),
|
||||
spiffs_get_cache_page(fs, cache, cp->ix));
|
||||
if (res2 != SPIFFS_OK) {
|
||||
// honor read failure before possible write failure (bad idea?)
|
||||
res = res2;
|
||||
}
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
|
||||
} else {
|
||||
// this will never happen, last resort for sake of symmetry
|
||||
s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst);
|
||||
if (res2 != SPIFFS_OK) {
|
||||
// honor read failure before possible write failure (bad idea?)
|
||||
res = res2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// writes to spi flash and/or the cache
|
||||
s32_t spiffs_phys_wr(
|
||||
spiffs *fs,
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *src) {
|
||||
(void)fh;
|
||||
spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
|
||||
|
||||
if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) {
|
||||
// have a cache page
|
||||
// copy in data to cache page
|
||||
|
||||
if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE &&
|
||||
(op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) {
|
||||
// page is being deleted, wipe from cache - unless it is a lookup page
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
return SPIFFS_HAL_WRITE(fs, addr, len, src);
|
||||
}
|
||||
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len);
|
||||
|
||||
cache->last_access++;
|
||||
cp->last_access = cache->last_access;
|
||||
|
||||
if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) {
|
||||
// page is being updated, no write-cache, just pass thru
|
||||
return SPIFFS_HAL_WRITE(fs, addr, len, src);
|
||||
} else {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
} else {
|
||||
// no cache page, no write cache - just write thru
|
||||
return SPIFFS_HAL_WRITE(fs, addr, len, src);
|
||||
}
|
||||
}
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
// returns the cache page that this fd refers, or null if no cache page
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) {
|
||||
// all cpages free, no cpage cannot be assigned to obj_id
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->cpage_use_map & (1<<i)) &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) &&
|
||||
cp->obj_id == fd->obj_id) {
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocates a new cache page and refers this to given fd - flushes an old cache
|
||||
// page if all cache is busy
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
// before this function is called, it is ensured that there is no already existing
|
||||
// cache page with same object id
|
||||
spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_allocate(fs);
|
||||
if (cp == 0) {
|
||||
// could not get cache page
|
||||
return 0;
|
||||
}
|
||||
|
||||
cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR;
|
||||
cp->obj_id = fd->obj_id;
|
||||
fd->cache_page = cp;
|
||||
return cp;
|
||||
}
|
||||
|
||||
// unrefers all fds that this cache page refers to and releases the cache page
|
||||
void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) {
|
||||
if (cp == 0) return;
|
||||
u32_t i;
|
||||
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
|
||||
for (i = 0; i < fs->fd_count; i++) {
|
||||
spiffs_fd *cur_fd = &fds[i];
|
||||
if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) {
|
||||
cur_fd->cache_page = 0;
|
||||
}
|
||||
}
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
|
||||
cp->obj_id = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// initializes the cache
|
||||
void spiffs_cache_init(spiffs *fs) {
|
||||
if (fs->cache == 0) return;
|
||||
u32_t sz = fs->cache_size;
|
||||
u32_t cache_mask = 0;
|
||||
int i;
|
||||
int cache_entries =
|
||||
(sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs));
|
||||
if (cache_entries <= 0) return;
|
||||
|
||||
for (i = 0; i < cache_entries; i++) {
|
||||
cache_mask <<= 1;
|
||||
cache_mask |= 1;
|
||||
}
|
||||
|
||||
spiffs_cache cache;
|
||||
memset(&cache, 0, sizeof(spiffs_cache));
|
||||
cache.cpage_count = cache_entries;
|
||||
cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache));
|
||||
|
||||
cache.cpage_use_map = 0xffffffff;
|
||||
cache.cpage_use_mask = cache_mask;
|
||||
memcpy(fs->cache, &cache, sizeof(spiffs_cache));
|
||||
|
||||
spiffs_cache *c = spiffs_get_cache(fs);
|
||||
|
||||
memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs));
|
||||
|
||||
c->cpage_use_map &= ~(c->cpage_use_mask);
|
||||
for (i = 0; i < cache.cpage_count; i++) {
|
||||
spiffs_get_cache_page_hdr(fs, c, i)->ix = i;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SPIFFS_CACHE
|
||||
@@ -0,0 +1,995 @@
|
||||
/*
|
||||
* spiffs_check.c
|
||||
*
|
||||
* Contains functionality for checking file system consistency
|
||||
* and mending problems.
|
||||
* Three levels of consistency checks are implemented:
|
||||
*
|
||||
* Look up consistency
|
||||
* Checks if indices in lookup pages are coherent with page headers
|
||||
* Object index consistency
|
||||
* Checks if there are any orphaned object indices (missing object index headers).
|
||||
* If an object index is found but not its header, the object index is deleted.
|
||||
* This is critical for the following page consistency check.
|
||||
* Page consistency
|
||||
* Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed
|
||||
*
|
||||
*
|
||||
* Created on: Jul 7, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
#if !SPIFFS_READ_ONLY
|
||||
|
||||
#if SPIFFS_HAL_CALLBACK_EXTRA
|
||||
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \
|
||||
do { \
|
||||
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \
|
||||
do { \
|
||||
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
//---------------------------------------
|
||||
// Look up consistency
|
||||
|
||||
// searches in the object indices and returns the referenced page index given
|
||||
// the object id and the data span index
|
||||
// destroys fs->lu_work
|
||||
static s32_t spiffs_object_get_data_page_index_reference(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix data_spix,
|
||||
spiffs_page_ix *pix,
|
||||
spiffs_page_ix *objix_pix) {
|
||||
s32_t res;
|
||||
|
||||
// calculate object index span index for given data page span index
|
||||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
|
||||
|
||||
// find obj index for obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// load obj index entry
|
||||
u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix);
|
||||
if (objix_spix == 0) {
|
||||
// get referenced page from object index header
|
||||
addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix);
|
||||
} else {
|
||||
// get referenced page from object index
|
||||
addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix);
|
||||
}
|
||||
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// copies page contents to a new page
|
||||
static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) {
|
||||
s32_t res;
|
||||
res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_phys_cpy(fs, 0,
|
||||
SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_DATA_PAGE_SIZE(fs));
|
||||
SPIFFS_CHECK_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// rewrites the object index for given object id and replaces the
|
||||
// data page index to a new page index
|
||||
static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) {
|
||||
s32_t res;
|
||||
spiffs_block_ix bix;
|
||||
int entry;
|
||||
spiffs_page_ix free_pix;
|
||||
obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
|
||||
|
||||
// find free entry
|
||||
res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
|
||||
|
||||
// calculate object index span index for given data page span index
|
||||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
|
||||
if (objix_spix == 0) {
|
||||
// calc index in index header
|
||||
entry = data_spix;
|
||||
} else {
|
||||
// calc entry in index
|
||||
entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix);
|
||||
|
||||
}
|
||||
// load index
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
|
||||
|
||||
// be ultra safe, double check header against provided data
|
||||
if (objix_p_hdr->obj_id != obj_id) {
|
||||
spiffs_page_delete(fs, free_pix);
|
||||
return SPIFFS_ERR_CHECK_OBJ_ID_MISM;
|
||||
}
|
||||
if (objix_p_hdr->span_ix != objix_spix) {
|
||||
spiffs_page_delete(fs, free_pix);
|
||||
return SPIFFS_ERR_CHECK_SPIX_MISM;
|
||||
}
|
||||
if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX |
|
||||
SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) !=
|
||||
(SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) {
|
||||
spiffs_page_delete(fs, free_pix);
|
||||
return SPIFFS_ERR_CHECK_FLAGS_BAD;
|
||||
}
|
||||
|
||||
// rewrite in mem
|
||||
if (objix_spix == 0) {
|
||||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix;
|
||||
} else {
|
||||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix;
|
||||
}
|
||||
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix),
|
||||
sizeof(spiffs_obj_id),
|
||||
(u8_t *)&obj_id);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_page_delete(fs, objix_pix);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// deletes an object just by marking object index header as deleted
|
||||
static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) {
|
||||
spiffs_page_ix objix_hdr_pix;
|
||||
s32_t res;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE;
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags),
|
||||
sizeof(u8_t),
|
||||
(u8_t *)&flags);
|
||||
return res;
|
||||
}
|
||||
|
||||
// validates the given look up entry
|
||||
static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr,
|
||||
spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) {
|
||||
(void)cur_block;
|
||||
(void)cur_entry;
|
||||
u8_t delete_page = 0;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_ix objix_pix;
|
||||
spiffs_page_ix ref_pix;
|
||||
// check validity, take actions
|
||||
if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) ||
|
||||
((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) {
|
||||
// look up entry deleted / free but used in page header
|
||||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" deleted/free in lu but not on page\n", cur_pix);
|
||||
*reload_lu = 1;
|
||||
delete_page = 1;
|
||||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
|
||||
// header says data page
|
||||
// data page can be removed if not referenced by some object index
|
||||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (ref_pix == cur_pix) {
|
||||
// data page referenced by object index but deleted in lu
|
||||
// copy page to new place and re-write the object index to new place
|
||||
spiffs_page_ix new_pix;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: "_SPIPRIpg" rewritten to "_SPIPRIpg", affected objix_pix "_SPIPRIpg"\n", cur_pix, new_pix, objix_pix);
|
||||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||
res = spiffs_page_delete(fs, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
|
||||
} else {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// header says index page
|
||||
// index page can be removed if other index with same obj_id and spanix is found
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no such index page found, check for a data page amongst page headers
|
||||
// lu cannot be trusted
|
||||
res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0);
|
||||
if (res == SPIFFS_OK) { // ignore other errors
|
||||
// got a data page also, assume lu corruption only, rewrite to new page
|
||||
spiffs_page_ix new_pix;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
}
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) {
|
||||
// look up entry used
|
||||
if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" differ in obj_id lu:"_SPIPRIid" ph:"_SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id);
|
||||
delete_page = 1;
|
||||
if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 ||
|
||||
(p_hdr->flags & SPIFFS_PH_FLAG_FINAL) ||
|
||||
(p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) {
|
||||
// page deleted or not finalized, just remove it
|
||||
} else {
|
||||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
|
||||
// if data page, check for reference to this page
|
||||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// if found, rewrite page with object id, update index, and delete current
|
||||
if (ref_pix == cur_pix) {
|
||||
spiffs_page_ix new_pix;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||
res = spiffs_page_delete(fs, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
|
||||
*reload_lu = 1;
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// else if index, check for other pages with both obj_id's and spanix
|
||||
spiffs_page_ix objix_pix_lu, objix_pix_ph;
|
||||
// see if other object index page exists for lookup obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_lu = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// see if other object index exists for page header obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_ph = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// if both obj_id's found, just delete current
|
||||
if (objix_pix_ph == 0 || objix_pix_lu == 0) {
|
||||
// otherwise try finding first corresponding data pages
|
||||
spiffs_page_ix data_pix_lu, data_pix_ph;
|
||||
// see if other data page exists for look up obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_lu = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// see if other data page exists for page header obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_ph = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
spiffs_page_header new_ph;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL);
|
||||
new_ph.span_ix = p_hdr->span_ix;
|
||||
spiffs_page_ix new_pix;
|
||||
if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) ||
|
||||
(objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) {
|
||||
// got a data page for page header obj id
|
||||
// rewrite as obj_id_ph
|
||||
new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
} else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) ||
|
||||
(objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) {
|
||||
// got a data page for look up obj id
|
||||
// rewrite as obj_id_lu
|
||||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
} else {
|
||||
// cannot safely do anything
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) ||
|
||||
((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) {
|
||||
SPIFFS_CHECK_DBG("LU: "_SPIPRIpg" lu/page index marking differ\n", cur_pix);
|
||||
spiffs_page_ix data_pix, objix_pix_d;
|
||||
// see if other data page exists for given obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
data_pix = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// see if other object index exists for given obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_d = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
delete_page = 1;
|
||||
// if other data page exists and object index exists, just delete page
|
||||
if (data_pix && objix_pix_d) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n");
|
||||
} else
|
||||
// if only data page exists, make this page index
|
||||
if (data_pix && objix_pix_d == 0) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n");
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix);
|
||||
spiffs_page_header new_ph;
|
||||
spiffs_page_ix new_pix;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
|
||||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
new_ph.span_ix = p_hdr->span_ix;
|
||||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else
|
||||
// if only index exists, make data page
|
||||
if (data_pix == 0 && objix_pix_d) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n");
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix);
|
||||
spiffs_page_header new_ph;
|
||||
spiffs_page_ix new_pix;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
|
||||
new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
new_ph.span_ix = p_hdr->span_ix;
|
||||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// if nothing exists, we cannot safely make a decision - delete
|
||||
}
|
||||
}
|
||||
else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) {
|
||||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy in lu but deleted on page\n", cur_pix);
|
||||
delete_page = 1;
|
||||
} else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) {
|
||||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy but not final\n", cur_pix);
|
||||
// page can be removed if not referenced by object index
|
||||
*reload_lu = 1;
|
||||
res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
delete_page = 1;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (ref_pix != cur_pix) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n");
|
||||
delete_page = 1;
|
||||
} else {
|
||||
// page referenced by object index but not final
|
||||
// just finalize
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n");
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL;
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags),
|
||||
sizeof(u8_t), (u8_t*)&flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_page) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry,
|
||||
const void *user_const_p, void *user_var_p) {
|
||||
(void)user_const_p;
|
||||
(void)user_var_p;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
|
||||
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS,
|
||||
(cur_block * 256)/fs->block_count, 0);
|
||||
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
int reload_lu = 0;
|
||||
|
||||
res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (res == SPIFFS_OK) {
|
||||
return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// Scans all object look up. For each entry, corresponding page header is checked for validity.
|
||||
// If an object index header page is found, this is also checked
|
||||
s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) {
|
||||
(void)check_all_objects;
|
||||
s32_t res = SPIFFS_OK;
|
||||
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0);
|
||||
|
||||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0);
|
||||
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
if (res != SPIFFS_OK) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0);
|
||||
}
|
||||
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
// Page consistency
|
||||
|
||||
// Scans all pages (except lu pages), reserves 4 bits in working memory for each page
|
||||
// bit 0: 0 == FREE|DELETED, 1 == USED
|
||||
// bit 1: 0 == UNREFERENCED, 1 == REFERENCED
|
||||
// bit 2: 0 == NOT_INDEX, 1 == INDEX
|
||||
// bit 3: unused
|
||||
// A consistent file system will have only pages being
|
||||
// * x000 free, unreferenced, not index
|
||||
// * x011 used, referenced only once, not index
|
||||
// * x101 used, unreferenced, index
|
||||
// The working memory might not fit all pages so several scans might be needed
|
||||
static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
|
||||
const u32_t bits = 4;
|
||||
const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits;
|
||||
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_ix pix_offset = 0;
|
||||
|
||||
// for each range of pages fitting into work memory
|
||||
while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) {
|
||||
// set this flag to abort all checks and rescan the page range
|
||||
u8_t restart = 0;
|
||||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
|
||||
spiffs_block_ix cur_block = 0;
|
||||
// build consistency bitmap for id range traversing all blocks
|
||||
while (!restart && cur_block < fs->block_count) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS,
|
||||
(pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) +
|
||||
((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count),
|
||||
0);
|
||||
// traverse each page except for lookup pages
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block;
|
||||
while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) {
|
||||
//if ((cur_pix & 0xff) == 0)
|
||||
// SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n",
|
||||
// cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count);
|
||||
|
||||
// read header
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan);
|
||||
const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits);
|
||||
const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits;
|
||||
|
||||
if (within_range &&
|
||||
(p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) {
|
||||
// used
|
||||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0));
|
||||
}
|
||||
if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) &&
|
||||
(p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) &&
|
||||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) {
|
||||
// found non-deleted index
|
||||
if (within_range) {
|
||||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2));
|
||||
}
|
||||
|
||||
// load non-deleted index
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// traverse index for referenced pages
|
||||
spiffs_page_ix *object_page_index;
|
||||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
|
||||
|
||||
int entries;
|
||||
int i;
|
||||
spiffs_span_ix data_spix_offset;
|
||||
if (p_hdr.span_ix == 0) {
|
||||
// object header page index
|
||||
entries = SPIFFS_OBJ_HDR_IX_LEN(fs);
|
||||
data_spix_offset = 0;
|
||||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header));
|
||||
} else {
|
||||
// object page index
|
||||
entries = SPIFFS_OBJ_IX_LEN(fs);
|
||||
data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1);
|
||||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix));
|
||||
}
|
||||
|
||||
// for all entries in index
|
||||
for (i = 0; !restart && i < entries; i++) {
|
||||
spiffs_page_ix rpix = object_page_index[i];
|
||||
u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan;
|
||||
|
||||
if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs))
|
||||
|| (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) {
|
||||
|
||||
// bad reference
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg"x bad pix / LU referenced from page "_SPIPRIpg"\n",
|
||||
rpix, cur_pix);
|
||||
// check for data page elsewhere
|
||||
spiffs_page_ix data_pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
|
||||
data_spix_offset + i, 0, &data_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
data_pix = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (data_pix == 0) {
|
||||
// if not, allocate free page
|
||||
spiffs_page_header new_ph;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
|
||||
new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
new_ph.span_ix = data_spix_offset + i;
|
||||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ "_SPIPRIpg"\n", data_pix);
|
||||
}
|
||||
// remap index
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix "_SPIPRIpg"\n", cur_pix);
|
||||
res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
|
||||
data_spix_offset + i, data_pix, cur_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0);
|
||||
// delete file
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
} else {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
|
||||
} else if (rpix_within_range) {
|
||||
|
||||
// valid reference
|
||||
// read referenced page header
|
||||
spiffs_page_header rp_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// cross reference page header check
|
||||
if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) ||
|
||||
rp_hdr.span_ix != data_spix_offset + i ||
|
||||
(rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) !=
|
||||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) {
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" has inconsistent page header ix id/span:"_SPIPRIid"/"_SPIPRIsp", ref id/span:"_SPIPRIid"/"_SPIPRIsp" flags:"_SPIPRIfl"\n",
|
||||
rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i,
|
||||
rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags);
|
||||
// try finding correct page
|
||||
spiffs_page_ix data_pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
|
||||
data_spix_offset + i, rpix, &data_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
data_pix = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (data_pix == 0) {
|
||||
// not found, this index is badly borked
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
break;
|
||||
} else {
|
||||
// found it, so rewrite index
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix "_SPIPRIpg", rewrite ix pix "_SPIPRIpg" id "_SPIPRIid"\n",
|
||||
data_pix, cur_pix, p_hdr.obj_id);
|
||||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
} else {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// mark rpix as referenced
|
||||
const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits);
|
||||
const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits;
|
||||
if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) {
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" multiple referenced from page "_SPIPRIpg"\n",
|
||||
rpix, cur_pix);
|
||||
// Here, we should have fixed all broken references - getting this means there
|
||||
// must be multiple files with same object id. Only solution is to delete
|
||||
// the object which is referring to this page
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: removing object "_SPIPRIid" and page "_SPIPRIpg"\n",
|
||||
p_hdr.obj_id, cur_pix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// extra precaution, delete this page also
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
}
|
||||
fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1));
|
||||
}
|
||||
}
|
||||
} // for all index entries
|
||||
} // found index
|
||||
|
||||
// next page
|
||||
cur_pix++;
|
||||
}
|
||||
// next block
|
||||
cur_block++;
|
||||
}
|
||||
// check consistency bitmap
|
||||
if (!restart) {
|
||||
spiffs_page_ix objix_pix;
|
||||
spiffs_page_ix rpix;
|
||||
|
||||
u32_t byte_ix;
|
||||
u8_t bit_ix;
|
||||
for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) {
|
||||
for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) {
|
||||
u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7;
|
||||
spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix;
|
||||
|
||||
// 000 ok - free, unreferenced, not index
|
||||
|
||||
if (bitmask == 0x1) {
|
||||
|
||||
// 001
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, UNREFERENCED, not index\n", cur_pix);
|
||||
|
||||
u8_t rewrite_ix_to_this = 0;
|
||||
u8_t delete_page = 0;
|
||||
// check corresponding object index entry
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix,
|
||||
&rpix, &objix_pix);
|
||||
if (res == SPIFFS_OK) {
|
||||
if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) {
|
||||
// pointing to a bad page altogether, rewrite index to this
|
||||
rewrite_ix_to_this = 1;
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref is bad: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix);
|
||||
} else {
|
||||
// pointing to something else, check what
|
||||
spiffs_page_header rp_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) &&
|
||||
((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) ==
|
||||
(SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) {
|
||||
// pointing to something else valid, just delete this page then
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: "_SPIPRIpg", delete this "_SPIPRIpg"\n", rpix, cur_pix);
|
||||
delete_page = 1;
|
||||
} else {
|
||||
// pointing to something weird, update index to point to this page instead
|
||||
if (rpix != cur_pix) {
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref is weird: "_SPIPRIpg" %s%s%s%s, rewrite this "_SPIPRIpg"\n", rpix,
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ",
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ",
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "",
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "",
|
||||
cur_pix);
|
||||
rewrite_ix_to_this = 1;
|
||||
} else {
|
||||
// should not happen, destined for fubar
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete "_SPIPRIpg"\n", cur_pix);
|
||||
delete_page = 1;
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
if (rewrite_ix_to_this) {
|
||||
// if pointing to invalid page, redirect index to this page
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id "_SPIPRIid" data spix "_SPIPRIsp" to point to this pix: "_SPIPRIpg"\n",
|
||||
p_hdr.obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
} else {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
continue;
|
||||
} else if (delete_page) {
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
if (bitmask == 0x2) {
|
||||
|
||||
// 010
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, not index\n", cur_pix);
|
||||
|
||||
// no op, this should be taken care of when checking valid references
|
||||
}
|
||||
|
||||
// 011 ok - busy, referenced, not index
|
||||
|
||||
if (bitmask == 0x4) {
|
||||
|
||||
// 100
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix);
|
||||
|
||||
// this should never happen, major fubar
|
||||
}
|
||||
|
||||
// 101 ok - busy, unreferenced, index
|
||||
|
||||
if (bitmask == 0x6) {
|
||||
|
||||
// 110
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, INDEX\n", cur_pix);
|
||||
|
||||
// no op, this should be taken care of when checking valid references
|
||||
}
|
||||
if (bitmask == 0x7) {
|
||||
|
||||
// 111
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, REFERENCED, INDEX\n", cur_pix);
|
||||
|
||||
// no op, this should be taken care of when checking valid references
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPIFFS_CHECK_DBG("PA: processed "_SPIPRIpg", restart "_SPIPRIi"\n", pix_offset, restart);
|
||||
// next page range
|
||||
if (!restart) {
|
||||
pix_offset += pages_per_scan;
|
||||
}
|
||||
} // while page range not reached end
|
||||
return res;
|
||||
}
|
||||
|
||||
// Checks consistency amongst all pages and fixes irregularities
|
||||
s32_t spiffs_page_consistency_check(spiffs *fs) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0);
|
||||
s32_t res = spiffs_page_consistency_check_i(fs);
|
||||
if (res != SPIFFS_OK) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0);
|
||||
}
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
// Object index consistency
|
||||
|
||||
// searches for given object id in temporary object id index,
|
||||
// returns the index or -1
|
||||
static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) {
|
||||
u32_t i;
|
||||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
|
||||
obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) {
|
||||
if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block,
|
||||
int cur_entry, const void *user_const_p, void *user_var_p) {
|
||||
(void)user_const_p;
|
||||
s32_t res_c = SPIFFS_VIS_COUNTINUE;
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t *log_ix = (u32_t*)user_var_p;
|
||||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
|
||||
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS,
|
||||
(cur_block * 256)/fs->block_count, 0);
|
||||
|
||||
if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
|
||||
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (p_hdr.span_ix == 0 &&
|
||||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
|
||||
(SPIFFS_PH_FLAG_DELET)) {
|
||||
SPIFFS_CHECK_DBG("IX: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" header not fully deleted - deleting\n",
|
||||
cur_pix, obj_id, p_hdr.span_ix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
return res_c;
|
||||
}
|
||||
|
||||
if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
|
||||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
|
||||
return res_c;
|
||||
}
|
||||
|
||||
if (p_hdr.span_ix == 0) {
|
||||
// objix header page, register objid as reachable
|
||||
int r = spiffs_object_index_search(fs, obj_id);
|
||||
if (r == -1) {
|
||||
// not registered, do it
|
||||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
(*log_ix)++;
|
||||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
|
||||
*log_ix = 0;
|
||||
}
|
||||
}
|
||||
} else { // span index
|
||||
// objix page, see if header can be found
|
||||
int r = spiffs_object_index_search(fs, obj_id);
|
||||
u8_t delete = 0;
|
||||
if (r == -1) {
|
||||
// not in temporary index, try finding it
|
||||
spiffs_page_ix objix_hdr_pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix);
|
||||
res_c = SPIFFS_VIS_COUNTINUE_RELOAD;
|
||||
if (res == SPIFFS_OK) {
|
||||
// found, register as reachable
|
||||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
} else if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// not found, register as unreachable
|
||||
delete = 1;
|
||||
obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
(*log_ix)++;
|
||||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
|
||||
*log_ix = 0;
|
||||
}
|
||||
} else {
|
||||
// in temporary index, check reachable flag
|
||||
if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
// registered as unreachable
|
||||
delete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (delete) {
|
||||
SPIFFS_CHECK_DBG("IX: FIXUP: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" is orphan index - deleting\n",
|
||||
cur_pix, obj_id, p_hdr.span_ix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
} // span index
|
||||
} // valid object index id
|
||||
|
||||
return res_c;
|
||||
}
|
||||
|
||||
// Removes orphaned and partially deleted index pages.
|
||||
// Scans for index pages. When an index page is found, corresponding index header is searched for.
|
||||
// If no such page exists, the index page cannot be reached as no index header exists and must be
|
||||
// deleted.
|
||||
s32_t spiffs_object_index_consistency_check(spiffs *fs) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
// impl note:
|
||||
// fs->work is used for a temporary object index memory, listing found object ids and
|
||||
// indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit.
|
||||
// In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate
|
||||
// a reachable/unreachable object id.
|
||||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
u32_t obj_id_log_ix = 0;
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0);
|
||||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix,
|
||||
0, 0);
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
if (res != SPIFFS_OK) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0);
|
||||
}
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // !SPIFFS_READ_ONLY
|
||||
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* spiffs_config.h
|
||||
*
|
||||
* Created on: Jul 3, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#ifndef SPIFFS_CONFIG_H_
|
||||
#define SPIFFS_CONFIG_H_
|
||||
|
||||
// ----------- 8< ------------
|
||||
// Following includes are for the linux test build of spiffs
|
||||
// These may/should/must be removed/altered/replaced in your target
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
// ----------- >8 ------------
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
typedef signed int s32_t;
|
||||
typedef unsigned int u32_t;
|
||||
typedef signed short s16_t;
|
||||
typedef unsigned short u16_t;
|
||||
typedef signed char s8_t;
|
||||
typedef unsigned char u8_t;
|
||||
|
||||
QueueHandle_t spiffs_mutex;
|
||||
|
||||
// compile time switches
|
||||
|
||||
// Set generic spiffs debug output call.
|
||||
#ifndef SPIFFS_DBG
|
||||
#define SPIFFS_DBG(...) //printf(__VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for garbage collecting.
|
||||
#ifndef SPIFFS_GC_DBG
|
||||
#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for caching.
|
||||
#ifndef SPIFFS_CACHE_DBG
|
||||
#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for system consistency checks.
|
||||
#ifndef SPIFFS_CHECK_DBG
|
||||
#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
|
||||
// Defines spiffs debug print formatters
|
||||
// some general signed number
|
||||
#ifndef _SPIPRIi
|
||||
#define _SPIPRIi "%d"
|
||||
#endif
|
||||
// address
|
||||
#ifndef _SPIPRIad
|
||||
#define _SPIPRIad "%08x"
|
||||
#endif
|
||||
// block
|
||||
#ifndef _SPIPRIbl
|
||||
#define _SPIPRIbl "%04x"
|
||||
#endif
|
||||
// page
|
||||
#ifndef _SPIPRIpg
|
||||
#define _SPIPRIpg "%04x"
|
||||
#endif
|
||||
// span index
|
||||
#ifndef _SPIPRIsp
|
||||
#define _SPIPRIsp "%04x"
|
||||
#endif
|
||||
// file descriptor
|
||||
#ifndef _SPIPRIfd
|
||||
#define _SPIPRIfd "%d"
|
||||
#endif
|
||||
// file object id
|
||||
#ifndef _SPIPRIid
|
||||
#define _SPIPRIid "%04x"
|
||||
#endif
|
||||
// file flags
|
||||
#ifndef _SPIPRIfl
|
||||
#define _SPIPRIfl "%02x"
|
||||
#endif
|
||||
|
||||
// Enable/disable API functions to determine exact number of bytes
|
||||
// for filedescriptor and cache buffers. Once decided for a configuration,
|
||||
// this can be disabled to reduce flash.
|
||||
#ifndef SPIFFS_BUFFER_HELP
|
||||
#define SPIFFS_BUFFER_HELP 0
|
||||
#endif
|
||||
|
||||
// Enables/disable memory read caching of nucleus file system operations.
|
||||
// If enabled, memory area must be provided for cache in SPIFFS_mount.
|
||||
#ifndef SPIFFS_CACHE
|
||||
#define SPIFFS_CACHE 1
|
||||
#endif
|
||||
#if SPIFFS_CACHE
|
||||
// Enables memory write caching for file descriptors in hydrogen
|
||||
#ifndef SPIFFS_CACHE_WR
|
||||
#define SPIFFS_CACHE_WR 1
|
||||
#endif
|
||||
|
||||
// Enable/disable statistics on caching. Debug/test purpose only.
|
||||
#ifndef SPIFFS_CACHE_STATS
|
||||
#define SPIFFS_CACHE_STATS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Always check header of each accessed page to ensure consistent state.
|
||||
// If enabled it will increase number of reads, will increase flash.
|
||||
#ifndef SPIFFS_PAGE_CHECK
|
||||
#define SPIFFS_PAGE_CHECK 0
|
||||
#endif
|
||||
|
||||
// Define maximum number of gc runs to perform to reach desired free pages.
|
||||
#ifndef SPIFFS_GC_MAX_RUNS
|
||||
#define SPIFFS_GC_MAX_RUNS 5
|
||||
#endif
|
||||
|
||||
// Enable/disable statistics on gc. Debug/test purpose only.
|
||||
#ifndef SPIFFS_GC_STATS
|
||||
#define SPIFFS_GC_STATS 0
|
||||
#endif
|
||||
|
||||
// Garbage collecting examines all pages in a block which and sums up
|
||||
// to a block score. Deleted pages normally gives positive score and
|
||||
// used pages normally gives a negative score (as these must be moved).
|
||||
// To have a fair wear-leveling, the erase age is also included in score,
|
||||
// whose factor normally is the most positive.
|
||||
// The larger the score, the more likely it is that the block will
|
||||
// picked for garbage collection.
|
||||
|
||||
// Garbage collecting heuristics - weight used for deleted pages.
|
||||
#ifndef SPIFFS_GC_HEUR_W_DELET
|
||||
#define SPIFFS_GC_HEUR_W_DELET (5)
|
||||
#endif
|
||||
// Garbage collecting heuristics - weight used for used pages.
|
||||
#ifndef SPIFFS_GC_HEUR_W_USED
|
||||
#define SPIFFS_GC_HEUR_W_USED (-1)
|
||||
#endif
|
||||
// Garbage collecting heuristics - weight used for time between
|
||||
// last erased and erase of this block.
|
||||
#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE
|
||||
#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
|
||||
#endif
|
||||
|
||||
// Object name maximum length. Note that this length include the
|
||||
// zero-termination character, meaning maximum string of characters
|
||||
// can at most be SPIFFS_OBJ_NAME_LEN - 1.
|
||||
#ifndef SPIFFS_OBJ_NAME_LEN
|
||||
#define SPIFFS_OBJ_NAME_LEN (64)
|
||||
#endif
|
||||
|
||||
// Maximum length of the metadata associated with an object.
|
||||
// Setting to non-zero value enables metadata-related API but also
|
||||
// changes the on-disk format, so the change is not backward-compatible.
|
||||
//
|
||||
// Do note: the meta length must never exceed
|
||||
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64)
|
||||
//
|
||||
// This is derived from following:
|
||||
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
|
||||
// spiffs_object_ix_header fields + at least some LUT entries)
|
||||
#ifndef SPIFFS_OBJ_META_LEN
|
||||
#define SPIFFS_OBJ_META_LEN (64)
|
||||
#endif
|
||||
|
||||
// Size of buffer allocated on stack used when copying data.
|
||||
// Lower value generates more read/writes. No meaning having it bigger
|
||||
// than logical page size.
|
||||
#ifndef SPIFFS_COPY_BUFFER_STACK
|
||||
#define SPIFFS_COPY_BUFFER_STACK (256)
|
||||
#endif
|
||||
|
||||
// Enable this to have an identifiable spiffs filesystem. This will look for
|
||||
// a magic in all sectors to determine if this is a valid spiffs system or
|
||||
// not on mount point. If not, SPIFFS_format must be called prior to mounting
|
||||
// again.
|
||||
#ifndef SPIFFS_USE_MAGIC
|
||||
#define SPIFFS_USE_MAGIC (1)
|
||||
#endif
|
||||
|
||||
#if SPIFFS_USE_MAGIC
|
||||
// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is
|
||||
// enabled, the magic will also be dependent on the length of the filesystem.
|
||||
// For example, a filesystem configured and formatted for 4 megabytes will not
|
||||
// be accepted for mounting with a configuration defining the filesystem as 2
|
||||
// megabytes.
|
||||
#ifndef SPIFFS_USE_MAGIC_LENGTH
|
||||
#define SPIFFS_USE_MAGIC_LENGTH (1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
|
||||
// These should be defined on a multithreaded system
|
||||
|
||||
// define this to enter a mutex if you're running on a multithreaded system
|
||||
#ifndef SPIFFS_LOCK
|
||||
#define SPIFFS_LOCK(fs) xSemaphoreTake(spiffs_mutex, portMAX_DELAY)
|
||||
#endif
|
||||
// define this to exit a mutex if you're running on a multithreaded system
|
||||
#ifndef SPIFFS_UNLOCK
|
||||
#define SPIFFS_UNLOCK(fs) xSemaphoreGive(spiffs_mutex)
|
||||
#endif
|
||||
|
||||
// Enable if only one spiffs instance with constant configuration will exist
|
||||
// on the target. This will reduce calculations, flash and memory accesses.
|
||||
// Parts of configuration must be defined below instead of at time of mount.
|
||||
#ifndef SPIFFS_SINGLETON
|
||||
#define SPIFFS_SINGLETON 0
|
||||
#endif
|
||||
|
||||
#if SPIFFS_SINGLETON
|
||||
// Instead of giving parameters in config struct, singleton build must
|
||||
// give parameters in defines below.
|
||||
#ifndef SPIFFS_CFG_PHYS_SZ
|
||||
#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*2)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_PHYS_ERASE_SZ
|
||||
#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_PHYS_ADDR
|
||||
#define SPIFFS_CFG_PHYS_ADDR(ignore) (0)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_LOG_PAGE_SZ
|
||||
#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_LOG_BLOCK_SZ
|
||||
#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Enable this if your target needs aligned data for index tables
|
||||
#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
|
||||
#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1
|
||||
#endif
|
||||
|
||||
// Enable this if you want the HAL callbacks to be called with the spiffs struct
|
||||
#ifndef SPIFFS_HAL_CALLBACK_EXTRA
|
||||
#define SPIFFS_HAL_CALLBACK_EXTRA 0
|
||||
#endif
|
||||
|
||||
// Enable this if you want to add an integer offset to all file handles
|
||||
// (spiffs_file). This is useful if running multiple instances of spiffs on
|
||||
// same target, in order to recognise to what spiffs instance a file handle
|
||||
// belongs.
|
||||
// NB: This adds config field fh_ix_offset in the configuration struct when
|
||||
// mounting, which must be defined.
|
||||
#ifndef SPIFFS_FILEHDL_OFFSET
|
||||
#define SPIFFS_FILEHDL_OFFSET 0
|
||||
#endif
|
||||
|
||||
// Enable this to compile a read only version of spiffs.
|
||||
// This will reduce binary size of spiffs. All code comprising modification
|
||||
// of the file system will not be compiled. Some config will be ignored.
|
||||
// HAL functions for erasing and writing to spi-flash may be null. Cache
|
||||
// can be disabled for even further binary size reduction (and ram savings).
|
||||
// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL.
|
||||
// If the file system cannot be mounted due to aborted erase operation and
|
||||
// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be
|
||||
// returned.
|
||||
// Might be useful for e.g. bootloaders and such.
|
||||
#ifndef SPIFFS_READ_ONLY
|
||||
#define SPIFFS_READ_ONLY 0
|
||||
#endif
|
||||
|
||||
// Enable this to add a temporal file cache using the fd buffer.
|
||||
// The effects of the cache is that SPIFFS_open will find the file faster in
|
||||
// certain cases. It will make it a lot easier for spiffs to find files
|
||||
// opened frequently, reducing number of readings from the spi flash for
|
||||
// finding those files.
|
||||
// This will grow each fd by 6 bytes. If your files are opened in patterns
|
||||
// with a degree of temporal locality, the system is optimized.
|
||||
// Examples can be letting spiffs serve web content, where one file is the css.
|
||||
// The css is accessed for each html file that is opened, meaning it is
|
||||
// accessed almost every second time a file is opened. Another example could be
|
||||
// a log file that is often opened, written, and closed.
|
||||
// The size of the cache is number of given file descriptors, as it piggybacks
|
||||
// on the fd update mechanism. The cache lives in the closed file descriptors.
|
||||
// When closed, the fd know the whereabouts of the file. Instead of forgetting
|
||||
// this, the temporal cache will keep handling updates to that file even if the
|
||||
// fd is closed. If the file is opened again, the location of the file is found
|
||||
// directly. If all available descriptors become opened, all cache memory is
|
||||
// lost.
|
||||
#ifndef SPIFFS_TEMPORAL_FD_CACHE
|
||||
#define SPIFFS_TEMPORAL_FD_CACHE 1
|
||||
#endif
|
||||
|
||||
// Temporal file cache hit score. Each time a file is opened, all cached files
|
||||
// will lose one point. If the opened file is found in cache, that entry will
|
||||
// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this
|
||||
// value for the specific access patterns of the application. However, it must
|
||||
// be between 1 (no gain for hitting a cached entry often) and 255.
|
||||
#ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE
|
||||
#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 8
|
||||
#endif
|
||||
|
||||
// Enable to be able to map object indices to memory.
|
||||
// This allows for faster and more deterministic reading if cases of reading
|
||||
// large files and when changing file offset by seeking around a lot.
|
||||
// When mapping a file's index, the file system will be scanned for index pages
|
||||
// and the info will be put in memory provided by user. When reading, the
|
||||
// memory map can be looked up instead of searching for index pages on the
|
||||
// medium. This way, user can trade memory against performance.
|
||||
// Whole, parts of, or future parts not being written yet can be mapped. The
|
||||
// memory array will be owned by spiffs and updated accordingly during garbage
|
||||
// collecting or when modifying the indices. The latter is invoked by when the
|
||||
// file is modified in some way. The index buffer is tied to the file
|
||||
// descriptor.
|
||||
#ifndef SPIFFS_IX_MAP
|
||||
#define SPIFFS_IX_MAP 1
|
||||
#endif
|
||||
|
||||
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
|
||||
// in the api. This function will visualize all filesystem using given printf
|
||||
// function.
|
||||
#ifndef SPIFFS_TEST_VISUALISATION
|
||||
#define SPIFFS_TEST_VISUALISATION 0
|
||||
#endif
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
#ifndef spiffs_printf
|
||||
#define spiffs_printf(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
// spiffs_printf argument for a free page
|
||||
#ifndef SPIFFS_TEST_VIS_FREE_STR
|
||||
#define SPIFFS_TEST_VIS_FREE_STR "_"
|
||||
#endif
|
||||
// spiffs_printf argument for a deleted page
|
||||
#ifndef SPIFFS_TEST_VIS_DELE_STR
|
||||
#define SPIFFS_TEST_VIS_DELE_STR "/"
|
||||
#endif
|
||||
// spiffs_printf argument for an index page for given object id
|
||||
#ifndef SPIFFS_TEST_VIS_INDX_STR
|
||||
#define SPIFFS_TEST_VIS_INDX_STR(id) "i"
|
||||
#endif
|
||||
// spiffs_printf argument for a data page for given object id
|
||||
#ifndef SPIFFS_TEST_VIS_DATA_STR
|
||||
#define SPIFFS_TEST_VIS_DATA_STR(id) "d"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Types depending on configuration such as the amount of flash bytes
|
||||
// given to spiffs file system in total (spiffs_file_system_size),
|
||||
// the logical block size (log_block_size), and the logical page size
|
||||
// (log_page_size)
|
||||
|
||||
// Block index type. Make sure the size of this type can hold
|
||||
// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
|
||||
typedef u16_t spiffs_block_ix;
|
||||
// Page index type. Make sure the size of this type can hold
|
||||
// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size
|
||||
typedef u16_t spiffs_page_ix;
|
||||
// Object id type - most significant bit is reserved for index flag. Make sure the
|
||||
// size of this type can hold the highest object id on a full system,
|
||||
// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2
|
||||
typedef u16_t spiffs_obj_id;
|
||||
// Object span index type. Make sure the size of this type can
|
||||
// hold the largest possible span index on the system -
|
||||
// i.e. (spiffs_file_system_size / log_page_size) - 1
|
||||
typedef u16_t spiffs_span_ix;
|
||||
|
||||
#endif /* SPIFFS_CONFIG_H_ */
|
||||
@@ -0,0 +1,606 @@
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
#if !SPIFFS_READ_ONLY
|
||||
|
||||
// Erases a logical block and updates the erase counter.
|
||||
// If cache is enabled, all pages that might be cached in this block
|
||||
// is dropped.
|
||||
static s32_t spiffs_gc_erase_block(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
s32_t res;
|
||||
|
||||
SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix);
|
||||
res = spiffs_erase_block(fs, bix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
{
|
||||
u32_t i;
|
||||
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
|
||||
spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
// Searches for blocks where all entries are deleted - if one is found,
|
||||
// the block is erased. Compared to the non-quick gc, the quick one ensures
|
||||
// that no updates are needed on existing objects on pages that are erased.
|
||||
s32_t spiffs_gc_quick(
|
||||
spiffs *fs, u16_t max_free_pages) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = 0;
|
||||
int cur_entry = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
|
||||
SPIFFS_GC_DBG("gc_quick: running\n");
|
||||
#if SPIFFS_GC_STATS
|
||||
fs->stats_gc_runs++;
|
||||
#endif
|
||||
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// find fully deleted blocks
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
u16_t free_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page &&
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
deleted_pages_in_block++;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// kill scan, go for next block
|
||||
free_pages_in_block++;
|
||||
if (free_pages_in_block > max_free_pages) {
|
||||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// kill scan, go for next block
|
||||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
if (res == 1) res = SPIFFS_OK;
|
||||
|
||||
if (res == SPIFFS_OK &&
|
||||
deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) &&
|
||||
free_pages_in_block <= max_free_pages) {
|
||||
// found a fully deleted block
|
||||
fs->stats_p_deleted -= deleted_pages_in_block;
|
||||
res = spiffs_gc_erase_block(fs, cur_block);
|
||||
return res;
|
||||
}
|
||||
|
||||
cur_entry = 0;
|
||||
cur_block++;
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
if (res == SPIFFS_OK) {
|
||||
res = SPIFFS_ERR_NO_DELETED_BLOCKS;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Checks if garbage collecting is necessary. If so a candidate block is found,
|
||||
// cleansed and erased
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
u32_t len) {
|
||||
s32_t res;
|
||||
s32_t free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
int tries = 0;
|
||||
|
||||
if (fs->free_blocks > 3 &&
|
||||
(s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
|
||||
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
|
||||
// SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
|
||||
// return SPIFFS_ERR_FULL;
|
||||
// }
|
||||
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
|
||||
SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
|
||||
return SPIFFS_ERR_FULL;
|
||||
}
|
||||
|
||||
do {
|
||||
SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n",
|
||||
tries,
|
||||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
|
||||
len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
|
||||
spiffs_block_ix *cands;
|
||||
int count;
|
||||
spiffs_block_ix cand;
|
||||
s32_t prev_free_pages = free_pages;
|
||||
// if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
|
||||
res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (count == 0) {
|
||||
SPIFFS_GC_DBG("gc_check: no candidates, return\n");
|
||||
return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL;
|
||||
}
|
||||
#if SPIFFS_GC_STATS
|
||||
fs->stats_gc_runs++;
|
||||
#endif
|
||||
cand = cands[0];
|
||||
fs->cleaning = 1;
|
||||
//SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand);
|
||||
res = spiffs_gc_clean(fs, cand);
|
||||
fs->cleaning = 0;
|
||||
if (res < 0) {
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
|
||||
} else {
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_gc_erase_page_stats(fs, cand);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_gc_erase_block(fs, cand);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
|
||||
if (prev_free_pages <= 0 && prev_free_pages == free_pages) {
|
||||
// abort early to reduce wear, at least tried once
|
||||
SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
} while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
|
||||
(s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
|
||||
free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
res = SPIFFS_ERR_FULL;
|
||||
}
|
||||
|
||||
SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n",
|
||||
fs->stats_p_allocated + fs->stats_p_deleted,
|
||||
fs->free_blocks, free_pages, tries, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Updates page statistics for a block that is about to be erased
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
int obj_lookup_page = 0;
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
u32_t dele = 0;
|
||||
u32_t allo = 0;
|
||||
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
dele++;
|
||||
} else {
|
||||
allo++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele);
|
||||
fs->stats_p_allocated -= allo;
|
||||
fs->stats_p_deleted -= dele;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Finds block candidates to erase
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix **block_candidates,
|
||||
int *candidate_count,
|
||||
char fs_crammed) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
|
||||
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
|
||||
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t)));
|
||||
*candidate_count = 0;
|
||||
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
|
||||
// divide up work area into block indices and scores
|
||||
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
|
||||
s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
|
||||
|
||||
// align cand_scores on s32_t boundary
|
||||
cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1));
|
||||
|
||||
*block_candidates = cand_blocks;
|
||||
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
u16_t used_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page &&
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// when a free entry is encountered, scan logic ensures that all following entries are free also
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
deleted_pages_in_block++;
|
||||
} else {
|
||||
used_pages_in_block++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
if (res == 1) res = SPIFFS_OK;
|
||||
|
||||
// calculate score and insert into candidate table
|
||||
// stoneage sort, but probably not so many blocks
|
||||
if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) {
|
||||
// read erase count
|
||||
spiffs_obj_id erase_count;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
|
||||
SPIFFS_ERASE_COUNT_PADDR(fs, cur_block),
|
||||
sizeof(spiffs_obj_id), (u8_t *)&erase_count);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
spiffs_obj_id erase_age;
|
||||
if (fs->max_erase_count > erase_count) {
|
||||
erase_age = fs->max_erase_count - erase_count;
|
||||
} else {
|
||||
erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count);
|
||||
}
|
||||
|
||||
s32_t score =
|
||||
deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
|
||||
used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
|
||||
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
|
||||
int cand_ix = 0;
|
||||
SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
|
||||
while (cand_ix < max_candidates) {
|
||||
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
} else if (cand_scores[cand_ix] < score) {
|
||||
int reorder_cand_ix = max_candidates - 2;
|
||||
while (reorder_cand_ix >= cand_ix) {
|
||||
cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
|
||||
cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
|
||||
reorder_cand_ix--;
|
||||
}
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
}
|
||||
cand_ix++;
|
||||
}
|
||||
(*candidate_count)++;
|
||||
}
|
||||
|
||||
cur_entry = 0;
|
||||
cur_block++;
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
FIND_OBJ_DATA,
|
||||
MOVE_OBJ_DATA,
|
||||
MOVE_OBJ_IX,
|
||||
FINISHED
|
||||
} spiffs_gc_clean_state;
|
||||
|
||||
typedef struct {
|
||||
spiffs_gc_clean_state state;
|
||||
spiffs_obj_id cur_obj_id;
|
||||
spiffs_span_ix cur_objix_spix;
|
||||
spiffs_page_ix cur_objix_pix;
|
||||
spiffs_page_ix cur_data_pix;
|
||||
int stored_scan_entry_index;
|
||||
u8_t obj_id_found;
|
||||
} spiffs_gc;
|
||||
|
||||
// Empties given block by moving all data into free pages of another block
|
||||
// Strategy:
|
||||
// loop:
|
||||
// scan object lookup for object data pages
|
||||
// for first found id, check spix and load corresponding object index page to memory
|
||||
// push object scan lookup entry index
|
||||
// rescan object lookup, find data pages with same id and referenced by same object index
|
||||
// move data page, update object index in memory
|
||||
// when reached end of lookup, store updated object index
|
||||
// pop object scan lookup entry index
|
||||
// repeat loop until end of object lookup
|
||||
// scan object lookup again for remaining object index pages, move to new page in other block
|
||||
//
|
||||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
// this is the global localizer being pushed and popped
|
||||
int cur_entry = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
spiffs_gc gc; // our stack frame/state
|
||||
spiffs_page_ix cur_pix = 0;
|
||||
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
|
||||
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
|
||||
|
||||
SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix);
|
||||
|
||||
memset(&gc, 0, sizeof(spiffs_gc));
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
|
||||
if (fs->free_cursor_block_ix == bix) {
|
||||
// move free cursor to next block, cannot use free pages from the block we want to clean
|
||||
fs->free_cursor_block_ix = (bix+1)%fs->block_count;
|
||||
fs->free_cursor_obj_lu_entry = 0;
|
||||
SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix);
|
||||
}
|
||||
|
||||
while (res == SPIFFS_OK && gc.state != FINISHED) {
|
||||
SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry);
|
||||
gc.obj_id_found = 0; // reset (to no found data page)
|
||||
|
||||
// scan through lookup pages
|
||||
int obj_lookup_page = cur_entry / entries_per_page;
|
||||
u8_t scan = 1;
|
||||
// check each object lookup page
|
||||
while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each object lookup entry
|
||||
while (scan && res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
|
||||
|
||||
// act upon object id depending on gc state
|
||||
switch (gc.state) {
|
||||
case FIND_OBJ_DATA:
|
||||
// find a data page
|
||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
|
||||
// found a data page, stop scanning and handle in switch case below
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id);
|
||||
gc.obj_id_found = 1;
|
||||
gc.cur_obj_id = obj_id;
|
||||
gc.cur_data_pix = cur_pix;
|
||||
scan = 0;
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA:
|
||||
// evacuate found data pages for corresponding object index we have in memory,
|
||||
// update memory representation
|
||||
if (obj_id == gc.cur_obj_id) {
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
|
||||
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
|
||||
} else {
|
||||
spiffs_page_ix new_data_pix;
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it -
|
||||
// might seem unnecessary as we will erase this block, but
|
||||
// we might get aborted
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
new_data_pix = SPIFFS_OBJ_ID_FREE;
|
||||
}
|
||||
// update memory representation of object index page with new data page
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// update object index header page
|
||||
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
} else {
|
||||
// update object index page
|
||||
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_IX:
|
||||
// find and evacuate object index pages
|
||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
// found an index object id
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix new_pix;
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr,
|
||||
SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it -
|
||||
// might seem unnecessary as we will erase this block, but
|
||||
// we might get aborted
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
if (res == SPIFFS_OK) {
|
||||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
|
||||
SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
|
||||
}
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
scan = 0;
|
||||
break;
|
||||
} // switch gc state
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop
|
||||
} // per object lookup page
|
||||
if (res != SPIFFS_OK) break;
|
||||
|
||||
// state finalization and switch
|
||||
switch (gc.state) {
|
||||
case FIND_OBJ_DATA:
|
||||
if (gc.obj_id_found) {
|
||||
// handle found data page -
|
||||
// find out corresponding obj ix page and load it to memory
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix objix_pix;
|
||||
gc.stored_scan_entry_index = cur_entry; // push cursor
|
||||
cur_entry = 0; // restart scan from start
|
||||
gc.state = MOVE_OBJ_DATA;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix);
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// on borked systems we might get an ERR_NOT_FOUND here -
|
||||
// this is handled by simply deleting the page as it is not referenced
|
||||
// from anywhere
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix);
|
||||
res = spiffs_page_delete(fs, gc.cur_data_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// then we restore states and continue scanning for data pages
|
||||
cur_entry = gc.stored_scan_entry_index; // pop cursor
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
break; // done
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix);
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// cannot allow a gc if the presumed index in fact is no index, a
|
||||
// check must run or lot of data may be lost
|
||||
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
|
||||
gc.cur_objix_pix = objix_pix;
|
||||
} else {
|
||||
// no more data pages found, passed thru all block, start evacuating object indices
|
||||
gc.state = MOVE_OBJ_IX;
|
||||
cur_entry = 0; // restart entry scan index
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA: {
|
||||
// store modified objix (hdr) page residing in memory now that all
|
||||
// data pages belonging to this object index and residing in the block
|
||||
// we want to evacuate
|
||||
spiffs_page_ix new_objix_pix;
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
cur_entry = gc.stored_scan_entry_index; // pop cursor
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// store object index header page
|
||||
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// store object index page
|
||||
res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
|
||||
SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_IX:
|
||||
// scanned thru all block, no more object indices found - our work here is done
|
||||
gc.state = FINISHED;
|
||||
break;
|
||||
default:
|
||||
cur_entry = 0;
|
||||
break;
|
||||
} // switch gc.state
|
||||
SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state);
|
||||
} // while state != FINISHED
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // !SPIFFS_READ_ONLY
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,797 @@
|
||||
/*
|
||||
* spiffs_nucleus.h
|
||||
*
|
||||
* Created on: Jun 15, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
/* SPIFFS layout
|
||||
*
|
||||
* spiffs is designed for following spi flash characteristics:
|
||||
* - only big areas of data (blocks) can be erased
|
||||
* - erasing resets all bits in a block to ones
|
||||
* - writing pulls ones to zeroes
|
||||
* - zeroes cannot be pulled to ones, without erase
|
||||
* - wear leveling
|
||||
*
|
||||
* spiffs is also meant to be run on embedded, memory constraint devices.
|
||||
*
|
||||
* Entire area is divided in blocks. Entire area is also divided in pages.
|
||||
* Each block contains same number of pages. A page cannot be erased, but a
|
||||
* block can be erased.
|
||||
*
|
||||
* Entire area must be block_size * x
|
||||
* page_size must be block_size / (2^y) where y > 2
|
||||
*
|
||||
* ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes
|
||||
*
|
||||
* BLOCK 0 PAGE 0 object lookup 1
|
||||
* PAGE 1 object lookup 2
|
||||
* ...
|
||||
* PAGE n-1 object lookup n
|
||||
* PAGE n object data 1
|
||||
* PAGE n+1 object data 2
|
||||
* ...
|
||||
* PAGE n+m-1 object data m
|
||||
*
|
||||
* BLOCK 1 PAGE n+m object lookup 1
|
||||
* PAGE n+m+1 object lookup 2
|
||||
* ...
|
||||
* PAGE 2n+m-1 object lookup n
|
||||
* PAGE 2n+m object data 1
|
||||
* PAGE 2n+m object data 2
|
||||
* ...
|
||||
* PAGE 2n+2m-1 object data m
|
||||
* ...
|
||||
*
|
||||
* n is number of object lookup pages, which is number of pages needed to index all pages
|
||||
* in a block by object id
|
||||
* : block_size / page_size * sizeof(obj_id) / page_size
|
||||
* m is number data pages, which is number of pages in block minus number of lookup pages
|
||||
* : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size
|
||||
* thus, n+m is total number of pages in a block
|
||||
* : block_size / page_size
|
||||
*
|
||||
* ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256
|
||||
*
|
||||
* Object lookup pages contain object id entries. Each entry represent the corresponding
|
||||
* data page.
|
||||
* Assuming a 16 bit object id, an object id being 0xffff represents a free page.
|
||||
* An object id being 0x0000 represents a deleted page.
|
||||
*
|
||||
* ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff ..
|
||||
* page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff ..
|
||||
* page 2 : data : data for object id 0008
|
||||
* page 3 : data : data for object id 0001
|
||||
* page 4 : data : data for object id 0aaa
|
||||
* ...
|
||||
*
|
||||
*
|
||||
* Object data pages can be either object index pages or object content.
|
||||
* All object data pages contains a data page header, containing object id and span index.
|
||||
* The span index denotes the object page ordering amongst data pages with same object id.
|
||||
* This applies to both object index pages (when index spans more than one page of entries),
|
||||
* and object data pages.
|
||||
* An object index page contains page entries pointing to object content page. The entry index
|
||||
* in a object index page correlates to the span index in the actual object data page.
|
||||
* The first object index page (span index 0) is called object index header page, and also
|
||||
* contains object flags (directory/file), size, object name etc.
|
||||
*
|
||||
* ex:
|
||||
* BLOCK 1
|
||||
* PAGE 256: objectl lookup page 1
|
||||
* [*123] [ 123] [ 123] [ 123]
|
||||
* [ 123] [*123] [ 123] [ 123]
|
||||
* [free] [free] [free] [free] ...
|
||||
* PAGE 257: objectl lookup page 2
|
||||
* [free] [free] [free] [free] ...
|
||||
* PAGE 258: object index page (header)
|
||||
* obj.id:0123 span.ix:0000 flags:INDEX
|
||||
* size:1600 name:ex.txt type:file
|
||||
* [259] [260] [261] [262]
|
||||
* PAGE 259: object data page
|
||||
* obj.id:0123 span.ix:0000 flags:DATA
|
||||
* PAGE 260: object data page
|
||||
* obj.id:0123 span.ix:0001 flags:DATA
|
||||
* PAGE 261: object data page
|
||||
* obj.id:0123 span.ix:0002 flags:DATA
|
||||
* PAGE 262: object data page
|
||||
* obj.id:0123 span.ix:0003 flags:DATA
|
||||
* PAGE 263: object index page
|
||||
* obj.id:0123 span.ix:0001 flags:INDEX
|
||||
* [264] [265] [fre] [fre]
|
||||
* [fre] [fre] [fre] [fre]
|
||||
* PAGE 264: object data page
|
||||
* obj.id:0123 span.ix:0004 flags:DATA
|
||||
* PAGE 265: object data page
|
||||
* obj.id:0123 span.ix:0005 flags:DATA
|
||||
*
|
||||
*/
|
||||
#ifndef SPIFFS_NUCLEUS_H_
|
||||
#define SPIFFS_NUCLEUS_H_
|
||||
|
||||
#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1)
|
||||
#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1)
|
||||
#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2)
|
||||
#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3)
|
||||
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4)
|
||||
|
||||
// visitor result, continue searching
|
||||
#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20)
|
||||
// visitor result, continue searching after reloading lu buffer
|
||||
#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21)
|
||||
// visitor result, stop searching
|
||||
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22)
|
||||
|
||||
// updating an object index contents
|
||||
#define SPIFFS_EV_IX_UPD (0)
|
||||
// creating a new object index
|
||||
#define SPIFFS_EV_IX_NEW (1)
|
||||
// deleting an object index
|
||||
#define SPIFFS_EV_IX_DEL (2)
|
||||
// moving an object index without updating contents
|
||||
#define SPIFFS_EV_IX_MOV (3)
|
||||
// updating an object index header data only, not the table itself
|
||||
#define SPIFFS_EV_IX_UPD_HDR (4)
|
||||
|
||||
#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1)))
|
||||
|
||||
#define SPIFFS_UNDEFINED_LEN (u32_t)(-1)
|
||||
|
||||
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0)
|
||||
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
|
||||
|
||||
#if SPIFFS_USE_MAGIC
|
||||
#if !SPIFFS_USE_MAGIC_LENGTH
|
||||
#define SPIFFS_MAGIC(fs, bix) \
|
||||
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
|
||||
#else // SPIFFS_USE_MAGIC_LENGTH
|
||||
#define SPIFFS_MAGIC(fs, bix) \
|
||||
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix))))
|
||||
#endif // SPIFFS_USE_MAGIC_LENGTH
|
||||
#endif // SPIFFS_USE_MAGIC
|
||||
|
||||
#define SPIFFS_CONFIG_MAGIC (0x20090315)
|
||||
|
||||
#if SPIFFS_SINGLETON == 0
|
||||
#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \
|
||||
((fs)->cfg.log_page_size)
|
||||
#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \
|
||||
((fs)->cfg.log_block_size)
|
||||
#define SPIFFS_CFG_PHYS_SZ(fs) \
|
||||
((fs)->cfg.phys_size)
|
||||
#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \
|
||||
((fs)->cfg.phys_erase_block)
|
||||
#define SPIFFS_CFG_PHYS_ADDR(fs) \
|
||||
((fs)->cfg.phys_addr)
|
||||
#endif
|
||||
|
||||
// total number of pages
|
||||
#define SPIFFS_MAX_PAGES(fs) \
|
||||
( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// total number of pages per block, including object lookup pages
|
||||
#define SPIFFS_PAGES_PER_BLOCK(fs) \
|
||||
( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// number of object lookup pages per block
|
||||
#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \
|
||||
(MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) )
|
||||
// checks if page index belongs to object lookup
|
||||
#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \
|
||||
(((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
// number of object lookup entries in all object lookup pages
|
||||
#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
// converts a block to physical address
|
||||
#define SPIFFS_BLOCK_TO_PADDR(fs, block) \
|
||||
( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) )
|
||||
// converts a object lookup entry to page index
|
||||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \
|
||||
((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry))
|
||||
// converts a object lookup entry to physical address of corresponding page
|
||||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \
|
||||
(SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// converts a page to physical address
|
||||
#define SPIFFS_PAGE_TO_PADDR(fs, page) \
|
||||
( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// converts a physical address to page
|
||||
#define SPIFFS_PADDR_TO_PAGE(fs, addr) \
|
||||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// gives index in page for a physical address
|
||||
#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \
|
||||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// returns containing block for given page
|
||||
#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \
|
||||
( (page) / SPIFFS_PAGES_PER_BLOCK(fs) )
|
||||
// returns starting page for block
|
||||
#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \
|
||||
( (block) * SPIFFS_PAGES_PER_BLOCK(fs) )
|
||||
// converts page to entry in object lookup page
|
||||
#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \
|
||||
( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) )
|
||||
// returns data size in a data page
|
||||
#define SPIFFS_DATA_PAGE_SIZE(fs) \
|
||||
( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) )
|
||||
// returns physical address for block's erase count,
|
||||
// always in the physical last entry of the last object lookup page
|
||||
#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \
|
||||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) )
|
||||
// returns physical address for block's magic,
|
||||
// always in the physical second last entry of the last object lookup page
|
||||
#define SPIFFS_MAGIC_PADDR(fs, bix) \
|
||||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 )
|
||||
// checks if there is any room for magic in the object luts
|
||||
#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \
|
||||
( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \
|
||||
<= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) )
|
||||
|
||||
// define helpers object
|
||||
|
||||
// entries in an object header page index
|
||||
#define SPIFFS_OBJ_HDR_IX_LEN(fs) \
|
||||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix))
|
||||
// entries in an object page index
|
||||
#define SPIFFS_OBJ_IX_LEN(fs) \
|
||||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix))
|
||||
// object index entry for given data span index
|
||||
#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \
|
||||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs)))
|
||||
// object index span index number for given data span index or entry
|
||||
#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \
|
||||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs)))
|
||||
// get data span index for object index span index
|
||||
#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \
|
||||
( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) )
|
||||
|
||||
#define SPIFFS_OP_T_OBJ_LU (0<<0)
|
||||
#define SPIFFS_OP_T_OBJ_LU2 (1<<0)
|
||||
#define SPIFFS_OP_T_OBJ_IX (2<<0)
|
||||
#define SPIFFS_OP_T_OBJ_DA (3<<0)
|
||||
#define SPIFFS_OP_C_DELE (0<<2)
|
||||
#define SPIFFS_OP_C_UPDT (1<<2)
|
||||
#define SPIFFS_OP_C_MOVS (2<<2)
|
||||
#define SPIFFS_OP_C_MOVD (3<<2)
|
||||
#define SPIFFS_OP_C_FLSH (4<<2)
|
||||
#define SPIFFS_OP_C_READ (5<<2)
|
||||
#define SPIFFS_OP_C_WRTHRU (6<<2)
|
||||
|
||||
#define SPIFFS_OP_TYPE_MASK (3<<0)
|
||||
#define SPIFFS_OP_COM_MASK (7<<2)
|
||||
|
||||
|
||||
// if 0, this page is written to, else clean
|
||||
#define SPIFFS_PH_FLAG_USED (1<<0)
|
||||
// if 0, writing is finalized, else under modification
|
||||
#define SPIFFS_PH_FLAG_FINAL (1<<1)
|
||||
// if 0, this is an index page, else a data page
|
||||
#define SPIFFS_PH_FLAG_INDEX (1<<2)
|
||||
// if 0, page is deleted, else valid
|
||||
#define SPIFFS_PH_FLAG_DELET (1<<7)
|
||||
// if 0, this index header is being deleted
|
||||
#define SPIFFS_PH_FLAG_IXDELE (1<<6)
|
||||
|
||||
|
||||
#define SPIFFS_CHECK_MOUNT(fs) \
|
||||
((fs)->mounted != 0)
|
||||
|
||||
#define SPIFFS_CHECK_CFG(fs) \
|
||||
((fs)->config_magic == SPIFFS_CONFIG_MAGIC)
|
||||
|
||||
#define SPIFFS_CHECK_RES(res) \
|
||||
do { \
|
||||
if ((res) < SPIFFS_OK) return (res); \
|
||||
} while (0);
|
||||
|
||||
#define SPIFFS_API_CHECK_MOUNT(fs) \
|
||||
if (!SPIFFS_CHECK_MOUNT((fs))) { \
|
||||
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
|
||||
return SPIFFS_ERR_NOT_MOUNTED; \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_CFG(fs) \
|
||||
if (!SPIFFS_CHECK_CFG((fs))) { \
|
||||
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
|
||||
return SPIFFS_ERR_NOT_CONFIGURED; \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_RES(fs, res) \
|
||||
if ((res) < SPIFFS_OK) { \
|
||||
(fs)->err_code = (res); \
|
||||
return (res); \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
|
||||
if ((res) < SPIFFS_OK) { \
|
||||
(fs)->err_code = (res); \
|
||||
SPIFFS_UNLOCK(fs); \
|
||||
return (res); \
|
||||
}
|
||||
|
||||
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \
|
||||
if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \
|
||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH;
|
||||
//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED;
|
||||
|
||||
#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \
|
||||
if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \
|
||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
|
||||
|
||||
|
||||
// check id, only visit matching objec ids
|
||||
#define SPIFFS_VIS_CHECK_ID (1<<0)
|
||||
// report argument object id to visitor - else object lookup id is reported
|
||||
#define SPIFFS_VIS_CHECK_PH (1<<1)
|
||||
// stop searching at end of all look up pages
|
||||
#define SPIFFS_VIS_NO_WRAP (1<<2)
|
||||
|
||||
#if SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
|
||||
(_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src))
|
||||
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
|
||||
(_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst))
|
||||
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
|
||||
(_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len))
|
||||
|
||||
#else // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
|
||||
(_fs)->cfg.hal_write_f((_paddr), (_len), (_src))
|
||||
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
|
||||
(_fs)->cfg.hal_read_f((_paddr), (_len), (_dst))
|
||||
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
|
||||
(_fs)->cfg.hal_erase_f((_paddr), (_len))
|
||||
|
||||
#endif // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
|
||||
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0)
|
||||
#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1)
|
||||
#define SPIFFS_CACHE_FLAG_OBJLU (1<<2)
|
||||
#define SPIFFS_CACHE_FLAG_OBJIX (1<<3)
|
||||
#define SPIFFS_CACHE_FLAG_DATA (1<<4)
|
||||
#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7)
|
||||
|
||||
#define SPIFFS_CACHE_PAGE_SIZE(fs) \
|
||||
(sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs))
|
||||
|
||||
#define spiffs_get_cache(fs) \
|
||||
((spiffs_cache *)((fs)->cache))
|
||||
|
||||
#define spiffs_get_cache_page_hdr(fs, c, ix) \
|
||||
((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])))
|
||||
|
||||
#define spiffs_get_cache_page(fs, c, ix) \
|
||||
((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page))
|
||||
|
||||
// cache page struct
|
||||
typedef struct {
|
||||
// cache flags
|
||||
u8_t flags;
|
||||
// cache page index
|
||||
u8_t ix;
|
||||
// last access of this cache page
|
||||
u32_t last_access;
|
||||
union {
|
||||
// type read cache
|
||||
struct {
|
||||
// read cache page index
|
||||
spiffs_page_ix pix;
|
||||
};
|
||||
#if SPIFFS_CACHE_WR
|
||||
// type write cache
|
||||
struct {
|
||||
// write cache
|
||||
spiffs_obj_id obj_id;
|
||||
// offset in cache page
|
||||
u32_t offset;
|
||||
// size of cache page
|
||||
u16_t size;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
} spiffs_cache_page;
|
||||
|
||||
// cache struct
|
||||
typedef struct {
|
||||
u8_t cpage_count;
|
||||
u32_t last_access;
|
||||
u32_t cpage_use_map;
|
||||
u32_t cpage_use_mask;
|
||||
u8_t *cpages;
|
||||
} spiffs_cache;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// spiffs nucleus file descriptor
|
||||
typedef struct {
|
||||
// the filesystem of this descriptor
|
||||
spiffs *fs;
|
||||
// number of file descriptor - if 0, the file descriptor is closed
|
||||
spiffs_file file_nbr;
|
||||
// object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted
|
||||
spiffs_obj_id obj_id;
|
||||
// size of the file
|
||||
u32_t size;
|
||||
// cached object index header page index
|
||||
spiffs_page_ix objix_hdr_pix;
|
||||
// cached offset object index page index
|
||||
spiffs_page_ix cursor_objix_pix;
|
||||
// cached offset object index span index
|
||||
spiffs_span_ix cursor_objix_spix;
|
||||
// current absolute offset
|
||||
u32_t offset;
|
||||
// current file descriptor offset
|
||||
u32_t fdoffset;
|
||||
// fd flags
|
||||
spiffs_flags flags;
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_cache_page *cache_page;
|
||||
#endif
|
||||
#if SPIFFS_TEMPORAL_FD_CACHE
|
||||
// djb2 hash of filename
|
||||
u32_t name_hash;
|
||||
// hit score (score == 0 indicates never used fd)
|
||||
u16_t score;
|
||||
#endif
|
||||
#if SPIFFS_IX_MAP
|
||||
// spiffs index map, if 0 it means unmapped
|
||||
spiffs_ix_map *ix_map;
|
||||
#endif
|
||||
} spiffs_fd;
|
||||
|
||||
|
||||
// object structs
|
||||
|
||||
// page header, part of each page except object lookup pages
|
||||
// NB: this is always aligned when the data page is an object index,
|
||||
// as in this case struct spiffs_page_object_ix is used
|
||||
typedef struct __attribute(( packed )) {
|
||||
// object id
|
||||
spiffs_obj_id obj_id;
|
||||
// object span index
|
||||
spiffs_span_ix span_ix;
|
||||
// flags
|
||||
u8_t flags;
|
||||
} spiffs_page_header;
|
||||
|
||||
// object index header page header
|
||||
typedef struct __attribute(( packed ))
|
||||
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
|
||||
__attribute(( aligned(sizeof(spiffs_page_ix)) ))
|
||||
#endif
|
||||
{
|
||||
// common page header
|
||||
spiffs_page_header p_hdr;
|
||||
// alignment
|
||||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
|
||||
// size of object
|
||||
u32_t size;
|
||||
// type of object
|
||||
spiffs_obj_type type;
|
||||
// name of object
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
#if SPIFFS_OBJ_META_LEN
|
||||
// metadata. not interpreted by SPIFFS in any way.
|
||||
u8_t meta[SPIFFS_OBJ_META_LEN];
|
||||
#endif
|
||||
} spiffs_page_object_ix_header;
|
||||
|
||||
// object index page header
|
||||
typedef struct __attribute(( packed )) {
|
||||
spiffs_page_header p_hdr;
|
||||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
|
||||
} spiffs_page_object_ix;
|
||||
|
||||
// callback func for object lookup visitor
|
||||
typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
|
||||
const void *user_const_p, void *user_var_p);
|
||||
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
|
||||
spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst))
|
||||
#define _spiffs_wr(fs, op, fh, addr, len, src) \
|
||||
spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src))
|
||||
#else
|
||||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
|
||||
spiffs_phys_rd((fs), (addr), (len), (dst))
|
||||
#define _spiffs_wr(fs, op, fh, addr, len, src) \
|
||||
spiffs_phys_wr((fs), (addr), (len), (src))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_phys_rd(
|
||||
spiffs *fs,
|
||||
#if SPIFFS_CACHE
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
#endif
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *dst);
|
||||
|
||||
s32_t spiffs_phys_wr(
|
||||
spiffs *fs,
|
||||
#if SPIFFS_CACHE
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
#endif
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *src);
|
||||
|
||||
s32_t spiffs_phys_cpy(
|
||||
spiffs *fs,
|
||||
spiffs_file fh,
|
||||
u32_t dst,
|
||||
u32_t src,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_phys_count_free_blocks(
|
||||
spiffs *fs);
|
||||
|
||||
s32_t spiffs_obj_lu_find_entry_visitor(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix starting_block,
|
||||
int starting_lu_entry,
|
||||
u8_t flags,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_visitor_f v,
|
||||
const void *user_const_p,
|
||||
void *user_var_p,
|
||||
spiffs_block_ix *block_ix,
|
||||
int *lu_entry);
|
||||
|
||||
s32_t spiffs_erase_block(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix);
|
||||
|
||||
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
|
||||
s32_t spiffs_probe(
|
||||
spiffs_config *cfg);
|
||||
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_obj_lu_scan(
|
||||
spiffs *fs);
|
||||
|
||||
s32_t spiffs_obj_lu_find_free_obj_id(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id *obj_id,
|
||||
const u8_t *conflicting_name);
|
||||
|
||||
s32_t spiffs_obj_lu_find_free(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix starting_block,
|
||||
int starting_lu_entry,
|
||||
spiffs_block_ix *block_ix,
|
||||
int *lu_entry);
|
||||
|
||||
s32_t spiffs_obj_lu_find_id(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix starting_block,
|
||||
int starting_lu_entry,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_block_ix *block_ix,
|
||||
int *lu_entry);
|
||||
|
||||
s32_t spiffs_obj_lu_find_id_and_span(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix spix,
|
||||
spiffs_page_ix exclusion_pix,
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix spix,
|
||||
spiffs_page_ix exclusion_pix,
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_page_allocate_data(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_page_header *ph,
|
||||
u8_t *data,
|
||||
u32_t len,
|
||||
u32_t page_offs,
|
||||
u8_t finalize,
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
s32_t spiffs_page_move(
|
||||
spiffs *fs,
|
||||
spiffs_file fh,
|
||||
u8_t *page_data,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_page_header *page_hdr,
|
||||
spiffs_page_ix src_pix,
|
||||
spiffs_page_ix *dst_pix);
|
||||
|
||||
s32_t spiffs_page_delete(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_object_create(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
const u8_t name[],
|
||||
const u8_t meta[],
|
||||
spiffs_obj_type type,
|
||||
spiffs_page_ix *objix_hdr_pix);
|
||||
|
||||
s32_t spiffs_object_update_index_hdr(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_page_ix objix_hdr_pix,
|
||||
u8_t *new_objix_hdr_data,
|
||||
const u8_t name[],
|
||||
const u8_t meta[],
|
||||
u32_t size,
|
||||
spiffs_page_ix *new_pix);
|
||||
|
||||
#if SPIFFS_IX_MAP
|
||||
|
||||
s32_t spiffs_populate_ix_map(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd,
|
||||
u32_t vec_entry_start,
|
||||
u32_t vec_entry_end);
|
||||
|
||||
#endif
|
||||
|
||||
void spiffs_cb_object_event(
|
||||
spiffs *fs,
|
||||
spiffs_page_object_ix *objix,
|
||||
int ev,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix spix,
|
||||
spiffs_page_ix new_pix,
|
||||
u32_t new_size);
|
||||
|
||||
s32_t spiffs_object_open_by_id(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_fd *f,
|
||||
spiffs_flags flags,
|
||||
spiffs_mode mode);
|
||||
|
||||
s32_t spiffs_object_open_by_page(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix,
|
||||
spiffs_fd *f,
|
||||
spiffs_flags flags,
|
||||
spiffs_mode mode);
|
||||
|
||||
s32_t spiffs_object_append(
|
||||
spiffs_fd *fd,
|
||||
u32_t offset,
|
||||
u8_t *data,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_object_modify(
|
||||
spiffs_fd *fd,
|
||||
u32_t offset,
|
||||
u8_t *data,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_object_read(
|
||||
spiffs_fd *fd,
|
||||
u32_t offset,
|
||||
u32_t len,
|
||||
u8_t *dst);
|
||||
|
||||
s32_t spiffs_object_truncate(
|
||||
spiffs_fd *fd,
|
||||
u32_t new_len,
|
||||
u8_t remove_object);
|
||||
|
||||
s32_t spiffs_object_find_object_index_header_by_name(
|
||||
spiffs *fs,
|
||||
const u8_t name[SPIFFS_OBJ_NAME_LEN],
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix);
|
||||
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix **block_candidate,
|
||||
int *candidate_count,
|
||||
char fs_crammed);
|
||||
|
||||
s32_t spiffs_gc_clean(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix);
|
||||
|
||||
s32_t spiffs_gc_quick(
|
||||
spiffs *fs, u16_t max_free_pages);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_fd_find_new(
|
||||
spiffs *fs,
|
||||
spiffs_fd **fd,
|
||||
const char *name);
|
||||
|
||||
s32_t spiffs_fd_return(
|
||||
spiffs *fs,
|
||||
spiffs_file f);
|
||||
|
||||
s32_t spiffs_fd_get(
|
||||
spiffs *fs,
|
||||
spiffs_file f,
|
||||
spiffs_fd **fd);
|
||||
|
||||
#if SPIFFS_TEMPORAL_FD_CACHE
|
||||
void spiffs_fd_temporal_cache_rehash(
|
||||
spiffs *fs,
|
||||
const char *old_path,
|
||||
const char *new_path);
|
||||
#endif
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
void spiffs_cache_init(
|
||||
spiffs *fs);
|
||||
|
||||
void spiffs_cache_drop_page(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix);
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd);
|
||||
|
||||
void spiffs_cache_fd_release(
|
||||
spiffs *fs,
|
||||
spiffs_cache_page *cp);
|
||||
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
s32_t spiffs_lookup_consistency_check(
|
||||
spiffs *fs,
|
||||
u8_t check_all_objects);
|
||||
|
||||
s32_t spiffs_page_consistency_check(
|
||||
spiffs *fs);
|
||||
|
||||
s32_t spiffs_object_index_consistency_check(
|
||||
spiffs *fs);
|
||||
|
||||
#endif /* SPIFFS_NUCLEUS_H_ */
|
||||
@@ -0,0 +1,878 @@
|
||||
/*
|
||||
* spiffs VFS operations
|
||||
*
|
||||
* Author: LoBo (loboris@gmail.com / https://github.com/loboris)
|
||||
*
|
||||
* Part of this code is copied from or inspired by LUA-RTOS_ESP32 project:
|
||||
*
|
||||
* https://github.com/whitecatboard/Lua-RTOS-ESP32
|
||||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
|
||||
* Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_attr.h"
|
||||
#include <errno.h>
|
||||
|
||||
#include <spiffs_vfs.h>
|
||||
#include <spiffs.h>
|
||||
#include <esp_spiffs.h>
|
||||
#include <spiffs_nucleus.h>
|
||||
#include "list.h"
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/dirent.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
|
||||
#ifdef PATH_MAX
|
||||
#undef PATH_MAX
|
||||
#endif
|
||||
#define PATH_MAX MAXNAMLEN+8
|
||||
|
||||
#define SPIFFS_ERASE_SIZE 4096
|
||||
|
||||
int spiffs_is_registered = 0;
|
||||
int spiffs_is_mounted = 0;
|
||||
|
||||
QueueHandle_t spiffs_mutex = NULL;
|
||||
|
||||
static int IRAM_ATTR vfs_spiffs_open(const char *path, int flags, int mode);
|
||||
static ssize_t IRAM_ATTR vfs_spiffs_write(int fd, const void *data, size_t size);
|
||||
static ssize_t IRAM_ATTR vfs_spiffs_read(int fd, void * dst, size_t size);
|
||||
static int IRAM_ATTR vfs_spiffs_fstat(int fd, struct stat * st);
|
||||
static int IRAM_ATTR vfs_spiffs_close(int fd);
|
||||
static off_t IRAM_ATTR vfs_spiffs_lseek(int fd, off_t size, int mode);
|
||||
|
||||
typedef struct {
|
||||
DIR dir;
|
||||
spiffs_DIR spiffs_dir;
|
||||
char path[MAXNAMLEN + 1];
|
||||
struct dirent ent;
|
||||
uint8_t read_mount;
|
||||
} vfs_spiffs_dir_t;
|
||||
|
||||
typedef struct {
|
||||
spiffs_file spiffs_file;
|
||||
char path[MAXNAMLEN + 1];
|
||||
uint8_t is_dir;
|
||||
} vfs_spiffs_file_t;
|
||||
|
||||
typedef struct {
|
||||
time_t mtime;
|
||||
time_t ctime;
|
||||
time_t atime;
|
||||
uint8_t spare[SPIFFS_OBJ_META_LEN - (sizeof(time_t)*3)];
|
||||
} spiffs_metadata_t;
|
||||
|
||||
static spiffs fs;
|
||||
static struct list files;
|
||||
|
||||
static u8_t *my_spiffs_work_buf;
|
||||
static u8_t *my_spiffs_fds;
|
||||
static u8_t *my_spiffs_cache;
|
||||
|
||||
|
||||
/*
|
||||
* ########################################
|
||||
* file names/paths passed to the functions
|
||||
* do not contain '/spiffs' prefix
|
||||
* ########################################
|
||||
*/
|
||||
|
||||
//----------------------------------------------------
|
||||
void spiffs_fs_stat(uint32_t *total, uint32_t *used) {
|
||||
if (SPIFFS_info(&fs, total, used) != SPIFFS_OK) {
|
||||
*total = 0;
|
||||
*used = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if path corresponds to a directory. Return 0 if is not a directory,
|
||||
* 1 if it's a directory.
|
||||
*
|
||||
*/
|
||||
//-----------------------------------
|
||||
static int is_dir(const char *path) {
|
||||
spiffs_DIR d;
|
||||
char npath[PATH_MAX + 1];
|
||||
int res = 0;
|
||||
|
||||
struct spiffs_dirent e;
|
||||
|
||||
// Add /. to path
|
||||
strlcpy(npath, path, PATH_MAX);
|
||||
if (strcmp(path,"/") != 0) {
|
||||
strlcat(npath,"/.", PATH_MAX);
|
||||
} else {
|
||||
strlcat(npath,".", PATH_MAX);
|
||||
}
|
||||
|
||||
SPIFFS_opendir(&fs, "/", &d);
|
||||
while (SPIFFS_readdir(&d, &e)) {
|
||||
if (strncmp(npath, (const char *)e.name, strlen(npath)) == 0) {
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SPIFFS_closedir(&d);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function translate error codes from SPIFFS to errno error codes
|
||||
*
|
||||
*/
|
||||
//-------------------------------
|
||||
static int spiffs_result(int res) {
|
||||
switch (res) {
|
||||
case SPIFFS_OK:
|
||||
case SPIFFS_ERR_END_OF_OBJECT:
|
||||
return 0;
|
||||
|
||||
case SPIFFS_ERR_NOT_FOUND:
|
||||
case SPIFFS_ERR_CONFLICTING_NAME:
|
||||
return ENOENT;
|
||||
|
||||
case SPIFFS_ERR_NOT_WRITABLE:
|
||||
case SPIFFS_ERR_NOT_READABLE:
|
||||
return EACCES;
|
||||
|
||||
case SPIFFS_ERR_FILE_EXISTS:
|
||||
return EEXIST;
|
||||
|
||||
default:
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
static int IRAM_ATTR vfs_spiffs_getstat(spiffs_file fd, spiffs_stat *st, spiffs_metadata_t *metadata) {
|
||||
int res = SPIFFS_fstat(&fs, fd, st);
|
||||
if (res == SPIFFS_OK) {
|
||||
// Get file's time information from metadata
|
||||
memcpy(metadata, st->meta, sizeof(spiffs_metadata_t));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// ## path does not contain '/spiffs' prefix !
|
||||
//---------------------------------------------------------------------------
|
||||
static int IRAM_ATTR vfs_spiffs_open(const char *path, int flags, int mode) {
|
||||
int fd, result = 0, exists = 0;
|
||||
spiffs_stat stat;
|
||||
spiffs_metadata_t meta;
|
||||
|
||||
// Allocate new file
|
||||
vfs_spiffs_file_t *file = calloc(1, sizeof(vfs_spiffs_file_t));
|
||||
if (!file) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add file to file list. List index is file descriptor.
|
||||
int res = list_add(&files, file, &fd);
|
||||
if (res) {
|
||||
free(file);
|
||||
errno = res;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if file exists
|
||||
if (SPIFFS_stat(&fs, path, &stat) == SPIFFS_OK) exists = 1;
|
||||
|
||||
// Make a copy of path
|
||||
strlcpy(file->path, path, MAXNAMLEN);
|
||||
|
||||
// Open file
|
||||
spiffs_flags spiffs_mode = 0;
|
||||
|
||||
// Translate flags to SPIFFS flags
|
||||
if (flags == O_RDONLY)
|
||||
spiffs_mode |= SPIFFS_RDONLY;
|
||||
|
||||
if (flags & O_WRONLY)
|
||||
spiffs_mode |= SPIFFS_WRONLY;
|
||||
|
||||
if (flags & O_RDWR)
|
||||
spiffs_mode = SPIFFS_RDWR;
|
||||
|
||||
if (flags & O_EXCL)
|
||||
spiffs_mode |= SPIFFS_EXCL;
|
||||
|
||||
if (flags & O_CREAT)
|
||||
spiffs_mode |= SPIFFS_CREAT;
|
||||
|
||||
if (flags & O_TRUNC)
|
||||
spiffs_mode |= SPIFFS_TRUNC;
|
||||
|
||||
if (is_dir(path)) {
|
||||
char npath[PATH_MAX + 1];
|
||||
|
||||
// Add /. to path
|
||||
strlcpy(npath, path, PATH_MAX);
|
||||
if (strcmp(path,"/") != 0) {
|
||||
strlcat(npath,"/.", PATH_MAX);
|
||||
} else {
|
||||
strlcat(npath,".", PATH_MAX);
|
||||
}
|
||||
|
||||
// Open SPIFFS file
|
||||
file->spiffs_file = SPIFFS_open(&fs, npath, spiffs_mode, 0);
|
||||
if (file->spiffs_file < 0) {
|
||||
result = spiffs_result(fs.err_code);
|
||||
}
|
||||
|
||||
file->is_dir = 1;
|
||||
} else {
|
||||
// Open SPIFFS file
|
||||
file->spiffs_file = SPIFFS_open(&fs, path, spiffs_mode, 0);
|
||||
if (file->spiffs_file < 0) {
|
||||
result = spiffs_result(fs.err_code);
|
||||
}
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
list_remove(&files, fd, 1);
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = vfs_spiffs_getstat(file->spiffs_file, &stat, &meta);
|
||||
if (res == SPIFFS_OK) {
|
||||
// update file's time information
|
||||
meta.atime = time(NULL); // Get the system time to access time
|
||||
if (!exists) meta.ctime = meta.atime;
|
||||
if (spiffs_mode != SPIFFS_RDONLY) meta.mtime = meta.atime;
|
||||
SPIFFS_fupdate_meta(&fs, file->spiffs_file, &meta);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
static ssize_t IRAM_ATTR vfs_spiffs_write(int fd, const void *data, size_t size) {
|
||||
vfs_spiffs_file_t *file;
|
||||
int res;
|
||||
|
||||
res = list_get(&files, fd, (void **)&file);
|
||||
if (res) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file->is_dir) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Write SPIFFS file
|
||||
res = SPIFFS_write(&fs, file->spiffs_file, (void *)data, size);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
} else {
|
||||
res = spiffs_result(fs.err_code);
|
||||
if (res != 0) {
|
||||
errno = res;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static ssize_t IRAM_ATTR vfs_spiffs_read(int fd, void * dst, size_t size) {
|
||||
vfs_spiffs_file_t *file;
|
||||
int res;
|
||||
|
||||
res = list_get(&files, fd, (void **)&file);
|
||||
if (res) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file->is_dir) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read SPIFFS file
|
||||
res = SPIFFS_read(&fs, file->spiffs_file, dst, size);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
} else {
|
||||
res = spiffs_result(fs.err_code);
|
||||
if (res != 0) {
|
||||
errno = res;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// EOF
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
static int IRAM_ATTR vfs_spiffs_fstat(int fd, struct stat * st) {
|
||||
vfs_spiffs_file_t *file;
|
||||
spiffs_stat stat;
|
||||
int res;
|
||||
spiffs_metadata_t meta;
|
||||
|
||||
res = list_get(&files, fd, (void **)&file);
|
||||
if (res) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set block size for this file system
|
||||
st->st_blksize = CONFIG_SPIFFS_LOG_PAGE_SIZE;
|
||||
|
||||
// Get file/directory statistics
|
||||
res = vfs_spiffs_getstat(file->spiffs_file, &stat, &meta);
|
||||
if (res == SPIFFS_OK) {
|
||||
// Set file's time information from metadata
|
||||
st->st_mtime = meta.mtime;
|
||||
st->st_ctime = meta.ctime;
|
||||
st->st_atime = meta.atime;
|
||||
|
||||
st->st_size = stat.size;
|
||||
|
||||
} else {
|
||||
st->st_mtime = 0;
|
||||
st->st_ctime = 0;
|
||||
st->st_atime = 0;
|
||||
st->st_size = 0;
|
||||
errno = spiffs_result(fs.err_code);
|
||||
//printf("SPIFFS_STAT: error %d\r\n", res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Test if it's a directory entry
|
||||
if (file->is_dir) st->st_mode = S_IFDIR;
|
||||
else st->st_mode = S_IFREG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------
|
||||
static int IRAM_ATTR vfs_spiffs_close(int fd) {
|
||||
vfs_spiffs_file_t *file;
|
||||
int res;
|
||||
|
||||
res = list_get(&files, fd, (void **)&file);
|
||||
if (res) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = SPIFFS_close(&fs, file->spiffs_file);
|
||||
if (res) {
|
||||
res = spiffs_result(fs.err_code);
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
errno = res;
|
||||
return -1;
|
||||
}
|
||||
|
||||
list_remove(&files, fd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
static off_t IRAM_ATTR vfs_spiffs_lseek(int fd, off_t size, int mode) {
|
||||
vfs_spiffs_file_t *file;
|
||||
int res;
|
||||
|
||||
res = list_get(&files, fd, (void **)&file);
|
||||
if (res) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file->is_dir) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int whence = SPIFFS_SEEK_CUR;
|
||||
|
||||
switch (mode) {
|
||||
case SEEK_SET: whence = SPIFFS_SEEK_SET;break;
|
||||
case SEEK_CUR: whence = SPIFFS_SEEK_CUR;break;
|
||||
case SEEK_END: whence = SPIFFS_SEEK_END;break;
|
||||
}
|
||||
|
||||
res = SPIFFS_lseek(&fs, file->spiffs_file, size, whence);
|
||||
if (res < 0) {
|
||||
res = spiffs_result(fs.err_code);
|
||||
errno = res;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static int IRAM_ATTR vfs_spiffs_stat(const char * path, struct stat * st) {
|
||||
int fd;
|
||||
int res;
|
||||
fd = vfs_spiffs_open(path, 0, 0);
|
||||
res = vfs_spiffs_fstat(fd, st);
|
||||
vfs_spiffs_close(fd);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------
|
||||
static int IRAM_ATTR vfs_spiffs_unlink(const char *path) {
|
||||
char npath[PATH_MAX + 1];
|
||||
|
||||
strlcpy(npath, path, PATH_MAX);
|
||||
|
||||
if (is_dir(path)) {
|
||||
// Check if directory is empty
|
||||
int nument = 0;
|
||||
sprintf(npath, "/spiffs");
|
||||
strlcat(npath, path, PATH_MAX);
|
||||
|
||||
DIR *dir = opendir(npath);
|
||||
if (dir) {
|
||||
struct dirent *ent;
|
||||
// Read directory entries
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
nument++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
errno = ENOTEMPTY;
|
||||
return -1;
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
if (nument > 0) {
|
||||
// Directory not empty, cannot remove
|
||||
errno = ENOTEMPTY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
strlcpy(npath, path, PATH_MAX);
|
||||
// Add /. to path
|
||||
if (strcmp(path,"/") != 0) {
|
||||
strlcat(npath,"/.", PATH_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
// Open SPIFFS file
|
||||
spiffs_file FP = SPIFFS_open(&fs, npath, SPIFFS_RDWR, 0);
|
||||
if (FP < 0) {
|
||||
errno = spiffs_result(fs.err_code);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Remove SPIFSS file
|
||||
if (SPIFFS_fremove(&fs, FP) < 0) {
|
||||
errno = spiffs_result(fs.err_code);
|
||||
SPIFFS_close(&fs, FP);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SPIFFS_close(&fs, FP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static int IRAM_ATTR vfs_spiffs_rename(const char *src, const char *dst) {
|
||||
if (SPIFFS_rename(&fs, src, dst) < 0) {
|
||||
errno = spiffs_result(fs.err_code);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------
|
||||
static DIR* vfs_spiffs_opendir(const char* name) {
|
||||
struct stat st;
|
||||
|
||||
if (strcmp(name, "/") != 0) {
|
||||
// Not on root
|
||||
if (vfs_spiffs_stat(name, &st)) {
|
||||
// Not found
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
// Not a directory
|
||||
errno = ENOTDIR;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
vfs_spiffs_dir_t *dir = calloc(1, sizeof(vfs_spiffs_dir_t));
|
||||
|
||||
if (!dir) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!SPIFFS_opendir(&fs, name, &dir->spiffs_dir)) {
|
||||
free(dir);
|
||||
errno = spiffs_result(fs.err_code);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strlcpy(dir->path, name, MAXNAMLEN);
|
||||
|
||||
return (DIR *)dir;
|
||||
}
|
||||
|
||||
//---------------------------------------------------
|
||||
static struct dirent* vfs_spiffs_readdir(DIR* pdir) {
|
||||
int res = 0, len = 0, entries = 0;
|
||||
vfs_spiffs_dir_t* dir = (vfs_spiffs_dir_t*) pdir;
|
||||
|
||||
struct spiffs_dirent e;
|
||||
struct spiffs_dirent *pe = &e;
|
||||
|
||||
struct dirent *ent = &dir->ent;
|
||||
|
||||
char *fn;
|
||||
|
||||
// Clear current dirent
|
||||
memset(ent,0,sizeof(struct dirent));
|
||||
|
||||
// If this is the first call to readdir for pdir, and
|
||||
// directory is the root path, return the mounted point if any
|
||||
if (!dir->read_mount) {
|
||||
if (strcmp(dir->path,"/") == 0) {
|
||||
strlcpy(ent->d_name, "/spiffs", PATH_MAX);
|
||||
ent->d_type = DT_DIR;
|
||||
dir->read_mount = 1;
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
||||
dir->read_mount = 1;
|
||||
}
|
||||
|
||||
// Search for next entry
|
||||
for(;;) {
|
||||
// Read directory
|
||||
pe = SPIFFS_readdir(&dir->spiffs_dir, pe);
|
||||
if (!pe) {
|
||||
res = spiffs_result(fs.err_code);
|
||||
errno = res;
|
||||
break;
|
||||
}
|
||||
|
||||
// Break condition
|
||||
if (pe->name[0] == 0) break;
|
||||
|
||||
// Get name and length
|
||||
fn = (char *)pe->name;
|
||||
len = strlen(fn);
|
||||
|
||||
// Get entry type and size
|
||||
ent->d_type = DT_REG;
|
||||
|
||||
if (len >= 2) {
|
||||
if (fn[len - 1] == '.') {
|
||||
if (fn[len - 2] == '/') {
|
||||
ent->d_type = DT_DIR;
|
||||
|
||||
fn[len - 2] = '\0';
|
||||
|
||||
len = strlen(fn);
|
||||
|
||||
// Skip root dir
|
||||
if (len == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip entries not belonged to path
|
||||
if (strncmp(fn, dir->path, strlen(dir->path)) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strlen(dir->path) > 1) {
|
||||
if (*(fn + strlen(dir->path)) != '/') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip root directory
|
||||
fn = fn + strlen(dir->path);
|
||||
len = strlen(fn);
|
||||
if (len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip initial /
|
||||
if (len > 1) {
|
||||
if (*fn == '/') {
|
||||
fn = fn + 1;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip subdirectories
|
||||
if (strchr(fn,'/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//ent->d_fsize = pe->size;
|
||||
|
||||
strlcpy(ent->d_name, fn, MAXNAMLEN);
|
||||
|
||||
entries++;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (entries > 0) {
|
||||
return ent;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
static int IRAM_ATTR vfs_piffs_closedir(DIR* pdir) {
|
||||
vfs_spiffs_dir_t* dir = (vfs_spiffs_dir_t*) pdir;
|
||||
int res;
|
||||
|
||||
if (!pdir) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((res = SPIFFS_closedir(&dir->spiffs_dir)) < 0) {
|
||||
errno = spiffs_result(fs.err_code);;
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
static int IRAM_ATTR vfs_spiffs_mkdir(const char *path, mode_t mode) {
|
||||
char npath[PATH_MAX + 1];
|
||||
int res;
|
||||
|
||||
// Add /. to path
|
||||
strlcpy(npath, path, PATH_MAX);
|
||||
if ((strcmp(path,"/") != 0) && (strcmp(path,"/.") != 0)) {
|
||||
strlcat(npath,"/.", PATH_MAX);
|
||||
}
|
||||
|
||||
spiffs_file fd = SPIFFS_open(&fs, npath, SPIFFS_CREAT, 0);
|
||||
if (fd < 0) {
|
||||
res = spiffs_result(fs.err_code);
|
||||
errno = res;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SPIFFS_close(&fs, fd) < 0) {
|
||||
res = spiffs_result(fs.err_code);
|
||||
errno = res;
|
||||
return -1;
|
||||
}
|
||||
|
||||
spiffs_metadata_t meta;
|
||||
meta.atime = time(NULL); // Get the system time to access time
|
||||
meta.ctime = meta.atime;
|
||||
meta.mtime = meta.atime;
|
||||
SPIFFS_update_meta(&fs, npath, &meta);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const char tag[] = "[SPIFFS]";
|
||||
|
||||
//==================
|
||||
int spiffs_mount() {
|
||||
|
||||
if (!spiffs_is_registered) return 0;
|
||||
if (spiffs_is_mounted) return 1;
|
||||
|
||||
spiffs_config cfg;
|
||||
int res = 0;
|
||||
int retries = 0;
|
||||
int err = 0;
|
||||
|
||||
ESP_LOGI(tag, "Mounting SPIFFS files system");
|
||||
|
||||
cfg.phys_addr = CONFIG_SPIFFS_BASE_ADDR;
|
||||
cfg.phys_size = CONFIG_SPIFFS_SIZE;
|
||||
cfg.phys_erase_block = SPIFFS_ERASE_SIZE;
|
||||
cfg.log_page_size = CONFIG_SPIFFS_LOG_PAGE_SIZE;
|
||||
cfg.log_block_size = CONFIG_SPIFFS_LOG_BLOCK_SIZE;
|
||||
|
||||
cfg.hal_read_f = (spiffs_read)low_spiffs_read;
|
||||
cfg.hal_write_f = (spiffs_write)low_spiffs_write;
|
||||
cfg.hal_erase_f = (spiffs_erase)low_spiffs_erase;
|
||||
|
||||
my_spiffs_work_buf = malloc(cfg.log_page_size * 8);
|
||||
if (!my_spiffs_work_buf) {
|
||||
err = 1;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
int fds_len = sizeof(spiffs_fd) * SPIFFS_TEMPORAL_CACHE_HIT_SCORE;
|
||||
my_spiffs_fds = malloc(fds_len);
|
||||
if (!my_spiffs_fds) {
|
||||
free(my_spiffs_work_buf);
|
||||
err = 2;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
int cache_len = cfg.log_page_size * SPIFFS_TEMPORAL_CACHE_HIT_SCORE;
|
||||
my_spiffs_cache = malloc(cache_len);
|
||||
if (!my_spiffs_cache) {
|
||||
free(my_spiffs_work_buf);
|
||||
free(my_spiffs_fds);
|
||||
err = 3;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
ESP_LOGI(tag, "Start address: 0x%x; Size %d KB", cfg.phys_addr, cfg.phys_size / 1024);
|
||||
ESP_LOGI(tag, " Work buffer: %d B", cfg.log_page_size * 8);
|
||||
ESP_LOGI(tag, " FDS buffer: %d B", sizeof(spiffs_fd) * SPIFFS_TEMPORAL_CACHE_HIT_SCORE);
|
||||
ESP_LOGI(tag, " Cache size: %d B", cfg.log_page_size * SPIFFS_TEMPORAL_CACHE_HIT_SCORE);
|
||||
while (retries < 2) {
|
||||
res = SPIFFS_mount(
|
||||
&fs, &cfg, my_spiffs_work_buf, my_spiffs_fds,
|
||||
fds_len, my_spiffs_cache, cache_len, NULL
|
||||
);
|
||||
|
||||
if (res < 0) {
|
||||
if (fs.err_code == SPIFFS_ERR_NOT_A_FS) {
|
||||
ESP_LOGW(tag, "No file system detected, formating...");
|
||||
SPIFFS_unmount(&fs);
|
||||
res = SPIFFS_format(&fs);
|
||||
if (res < 0) {
|
||||
free(my_spiffs_work_buf);
|
||||
free(my_spiffs_fds);
|
||||
free(my_spiffs_cache);
|
||||
ESP_LOGE(tag, "Format error");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
free(my_spiffs_work_buf);
|
||||
free(my_spiffs_fds);
|
||||
free(my_spiffs_cache);
|
||||
ESP_LOGE(tag, "Error mounting fs (%d)", res);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else break;
|
||||
retries++;
|
||||
}
|
||||
|
||||
if (retries > 1) {
|
||||
free(my_spiffs_work_buf);
|
||||
free(my_spiffs_fds);
|
||||
free(my_spiffs_cache);
|
||||
ESP_LOGE(tag, "Can't mount");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
list_init(&files, 0);
|
||||
|
||||
ESP_LOGI(tag, "Mounted");
|
||||
|
||||
spiffs_is_mounted = 1;
|
||||
return 1;
|
||||
|
||||
err_exit:
|
||||
ESP_LOGE(tag, "Error allocating fs structures (%d)", err);
|
||||
exit:
|
||||
esp_vfs_unregister("/spiffs");
|
||||
spiffs_is_registered = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================
|
||||
void vfs_spiffs_register() {
|
||||
|
||||
if (spiffs_is_registered) return;
|
||||
|
||||
if (spiffs_mutex == NULL) {
|
||||
spiffs_mutex = xSemaphoreCreateMutex();
|
||||
if (spiffs_mutex == NULL) {
|
||||
ESP_LOGE(tag, "Error creating SPIFFS mutex");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
esp_vfs_t vfs = {
|
||||
//.fd_offset = 0, // not available in latest esp-idf
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
.write = &vfs_spiffs_write,
|
||||
.open = &vfs_spiffs_open,
|
||||
.fstat = &vfs_spiffs_fstat,
|
||||
.close = &vfs_spiffs_close,
|
||||
.read = &vfs_spiffs_read,
|
||||
.lseek = &vfs_spiffs_lseek,
|
||||
.stat = &vfs_spiffs_stat,
|
||||
.link = NULL,
|
||||
.unlink = &vfs_spiffs_unlink,
|
||||
.rename = &vfs_spiffs_rename,
|
||||
.mkdir = &vfs_spiffs_mkdir,
|
||||
.opendir = &vfs_spiffs_opendir,
|
||||
.readdir = &vfs_spiffs_readdir,
|
||||
.closedir = &vfs_piffs_closedir,
|
||||
};
|
||||
|
||||
ESP_LOGI(tag, "Registering SPIFFS file system");
|
||||
esp_err_t res = esp_vfs_register(SPIFFS_BASE_PATH, &vfs, NULL);
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE(tag, "Error, SPIFFS file system not registered");
|
||||
return;
|
||||
}
|
||||
spiffs_is_registered = 1;
|
||||
|
||||
spiffs_mount();
|
||||
}
|
||||
|
||||
//=============================
|
||||
int spiffs_unmount(int unreg) {
|
||||
|
||||
if (!spiffs_is_mounted) return 0;
|
||||
|
||||
SPIFFS_unmount(&fs);
|
||||
spiffs_is_mounted = 0;
|
||||
|
||||
if (unreg) {
|
||||
esp_vfs_unregister("/spiffs");
|
||||
spiffs_is_registered = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* spiffs VFS public function
|
||||
*
|
||||
* Author: LoBo (loboris@gmail.com / https://github.com/loboris)
|
||||
*
|
||||
* Part of this code is copied from or inspired by LUA-RTOS_ESP32 project:
|
||||
*
|
||||
* https://github.com/whitecatboard/Lua-RTOS-ESP32
|
||||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
|
||||
* Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
*/
|
||||
|
||||
#define SPIFFS_BASE_PATH "/spiffs"
|
||||
|
||||
|
||||
int spiffs_is_registered;
|
||||
int spiffs_is_mounted;
|
||||
|
||||
void vfs_spiffs_register();
|
||||
int spiffs_mount();
|
||||
int spiffs_unmount(int unreg);
|
||||
void spiffs_fs_stat(uint32_t *total, uint32_t *used);
|
||||
Reference in New Issue
Block a user